From 1dca2841da6f024f613d6dc16de456d5035f8fce Mon Sep 17 00:00:00 2001 From: crupest Date: Sun, 31 Oct 2021 22:53:11 +0800 Subject: ... --- demos/input_method/main.cpp | 7 +- include/cru/osx/graphics/quartz/TextLayout.hpp | 10 +- include/cru/platform/GraphicsBase.hpp | 13 ++ include/cru/platform/Matrix.hpp | 9 ++ include/cru/platform/graphics/TextLayout.hpp | 3 +- include/cru/ui/render/TextRenderObject.hpp | 2 +- src/osx/graphics/quartz/Painter.cpp | 11 +- src/osx/graphics/quartz/TextLayout.cpp | 171 +++++++++++++++---------- src/osx/gui/Window.mm | 15 +-- src/ui/Helper.hpp | 1 - src/ui/render/TextRenderObject.cpp | 6 +- 11 files changed, 152 insertions(+), 96 deletions(-) diff --git a/demos/input_method/main.cpp b/demos/input_method/main.cpp index 67743239..913f0ae2 100644 --- a/demos/input_method/main.cpp +++ b/demos/input_method/main.cpp @@ -94,9 +94,10 @@ int main() { const auto cursor_lefttop = text_layout->TextSinglePoint(cursor_pos, false); - painter->FillRectangle(Rect{cursor_lefttop.x, cursor_lefttop.y + anchor_y, - 3, font->GetFontSize()}, - brush.get()); + painter->FillRectangle( + Rect{cursor_lefttop.left, cursor_lefttop.top + anchor_y, 3, + cursor_lefttop.height}, + brush.get()); } }); diff --git a/include/cru/osx/graphics/quartz/TextLayout.hpp b/include/cru/osx/graphics/quartz/TextLayout.hpp index 95e3d1be..35bcc32d 100644 --- a/include/cru/osx/graphics/quartz/TextLayout.hpp +++ b/include/cru/osx/graphics/quartz/TextLayout.hpp @@ -30,19 +30,25 @@ class OsxCTTextLayout : public OsxQuartzResource, public virtual ITextLayout { Rect GetTextBounds(bool includingTrailingSpace = false) override; std::vector TextRangeRect(const TextRange& text_range) override; - Point TextSinglePoint(Index position, bool trailing) override; + Rect TextSinglePoint(Index position, bool trailing) override; TextHitTestResult HitTest(const Point& point) override; CTFrameRef GetCTFrameRef() const { return ct_frame_; } CTFrameRef CreateFrameWithColor(const Color& color); + Matrix GetTransform() { return transform_; } + String GetDebugString() override; private: void ReleaseResource(); void RecreateFrame(); + Rect DoGetTextBounds(bool includingTrailingSpace = false); + std::vector DoTextRangeRect(const TextRange& text_range); + Rect DoTextSinglePoint(Index position, bool trailing); + private: float max_width_; float max_height_; @@ -58,5 +64,7 @@ class OsxCTTextLayout : public OsxQuartzResource, public virtual ITextLayout { int line_count_; std::vector line_origins_; std::vector lines_; + + Matrix transform_; }; } // namespace cru::platform::graphics::osx::quartz diff --git a/include/cru/platform/GraphicsBase.hpp b/include/cru/platform/GraphicsBase.hpp index aee718a1..73b41bb5 100644 --- a/include/cru/platform/GraphicsBase.hpp +++ b/include/cru/platform/GraphicsBase.hpp @@ -206,6 +206,19 @@ struct Rect final { point.y < GetBottom(); } + constexpr Rect Normalize() const { + Rect result = *this; + if (result.width < 0) { + result.left += result.width; + result.width = -result.width; + } + if (result.height < 0) { + result.top += result.height; + result.height = -result.height; + } + return result; + } + float left = 0.0f; float top = 0.0f; float width = 0.0f; diff --git a/include/cru/platform/Matrix.hpp b/include/cru/platform/Matrix.hpp index 72459b79..47917d47 100644 --- a/include/cru/platform/Matrix.hpp +++ b/include/cru/platform/Matrix.hpp @@ -43,6 +43,15 @@ struct Matrix { point.x * m12 + point.y * m22 + m32}; } + Rect TransformRect(const Rect& rect, bool normalize = true) const { + Point lefttop = TransformPoint(rect.GetLeftTop()), + rightbottom = TransformPoint(rect.GetRightBottom()); + auto result = + Rect::FromVertices(lefttop.x, lefttop.y, rightbottom.x, rightbottom.y); + if (normalize) result = result.Normalize(); + return result; + } + static Matrix Identity() { return Matrix{1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f}; } diff --git a/include/cru/platform/graphics/TextLayout.hpp b/include/cru/platform/graphics/TextLayout.hpp index ec3c2d96..5f98696d 100644 --- a/include/cru/platform/graphics/TextLayout.hpp +++ b/include/cru/platform/graphics/TextLayout.hpp @@ -17,7 +17,8 @@ struct ITextLayout : virtual IGraphicsResource { virtual Rect GetTextBounds(bool includingTrailingSpace = false) = 0; virtual std::vector TextRangeRect(const TextRange& text_range) = 0; - virtual Point TextSinglePoint(Index position, bool trailing) = 0; + // Width is always 0, height is line height. + virtual Rect TextSinglePoint(Index position, bool trailing) = 0; virtual TextHitTestResult HitTest(const Point& point) = 0; }; } // namespace cru::platform::graphics diff --git a/include/cru/ui/render/TextRenderObject.hpp b/include/cru/ui/render/TextRenderObject.hpp index ef39b7a6..68d2d0ce 100644 --- a/include/cru/ui/render/TextRenderObject.hpp +++ b/include/cru/ui/render/TextRenderObject.hpp @@ -43,7 +43,7 @@ class TextRenderObject : public RenderObject { void SetFont(std::shared_ptr font); std::vector TextRangeRect(const TextRange& text_range); - Point TextSinglePoint(gsl::index position, bool trailing); + Rect TextSinglePoint(gsl::index position, bool trailing); platform::graphics::TextHitTestResult TextHitTest(const Point& point); std::optional GetSelectionRange() const { diff --git a/src/osx/graphics/quartz/Painter.cpp b/src/osx/graphics/quartz/Painter.cpp index 5657e048..798e1256 100644 --- a/src/osx/graphics/quartz/Painter.cpp +++ b/src/osx/graphics/quartz/Painter.cpp @@ -158,16 +158,7 @@ void QuartzCGContextPainter::DrawText(const Point& offset, color = colors::black; } - auto bounds = tl->GetTextBounds(); - - bounds.width += bounds.left; - bounds.height += bounds.top; - bounds.left = bounds.top = 0; - - Matrix transform = - Matrix::Translation(-bounds.width / 2, -bounds.height / 2) * - Matrix::Scale(1, -1) * - Matrix::Translation(bounds.width / 2, bounds.height / 2); + Matrix transform = tl->GetTransform(); CGContextSaveGState(cg_context_); diff --git a/src/osx/graphics/quartz/TextLayout.cpp b/src/osx/graphics/quartz/TextLayout.cpp index 5145f9f9..d30c386f 100644 --- a/src/osx/graphics/quartz/TextLayout.cpp +++ b/src/osx/graphics/quartz/TextLayout.cpp @@ -72,83 +72,21 @@ void OsxCTTextLayout::SetMaxHeight(float max_height) { } Rect OsxCTTextLayout::GetTextBounds(bool includingTrailingSpace) { - float left = std::numeric_limits::max(); - float top = std::numeric_limits::max(); - float right = 0; - float bottom = 0; - - for (int i = 0; i < line_count_; i++) { - auto line = lines_[i]; - const auto& line_origin = line_origins_[i]; - - CGRect line_rect = CTLineGetImageBounds(line, nullptr); - if (includingTrailingSpace) { - float trailingWidth = CTLineGetTrailingWhitespaceWidth(line); - line_rect.size.width += trailingWidth; - } - - line_rect.origin.x += line_origin.x; - line_rect.origin.y += line_origin.y; - - left = std::min(line_rect.origin.x, left); - top = std::min(line_rect.origin.y, top); - right = std::max(line_rect.origin.x + line_rect.size.width, right); - bottom = - std::max(line_rect.origin.y + line_rect.size.height, bottom); - } - - return Rect::FromVertices(left, top, right, bottom); + return transform_.TransformRect(DoGetTextBounds(includingTrailingSpace)); } std::vector OsxCTTextLayout::TextRangeRect(const TextRange& text_range) { - const auto r = text_.RangeFromCodeUnitToCodePoint(text_range).Normalize(); + std::vector results = DoTextRangeRect(text_range); - std::vector results; - - for (int i = 0; i < line_count_; i++) { - auto line = lines_[i]; - const auto& line_origin = line_origins_[i]; - - Range range = Convert(CTLineGetStringRange(line)); - range = range.CoerceInto(r.GetStart(), r.GetEnd()); - - if (range.count) { - auto line_rect = CTLineGetImageBounds(line, nullptr); - line_rect.origin.x += line_origin.x; - line_rect.origin.y += line_origin.y; - float start_offset = - CTLineGetOffsetForStringIndex(line, range.GetStart(), nullptr); - float end_offset = - CTLineGetOffsetForStringIndex(line, range.GetEnd(), nullptr); - line_rect.origin.x += start_offset; - line_rect.size.width = end_offset - start_offset; - results.push_back(Convert(line_rect)); - } + for (auto& rect : results) { + rect = transform_.TransformRect(rect); } return results; } -Point OsxCTTextLayout::TextSinglePoint(Index position, bool trailing) { - position = text_.IndexFromCodeUnitToCodePoint(position); - for (int i = 0; i < line_count_; i++) { - auto line = lines_[i]; - const auto& line_origin = line_origins_[i]; - - Range range = Convert(CTLineGetStringRange(line)); - if (range.GetStart() <= position && position < range.GetEnd()) { - auto offset = CTLineGetOffsetForStringIndex(line, position, nullptr); - return Point(line_origin.x + offset, line_origin.y); - } else if (position == range.GetEnd()) { - return Point( - line_origin.x + CTLineGetImageBounds(line, nullptr).size.width, - line_origin.y); - } - } - - if (lines_.empty()) return Point{}; - - return Convert(CTLineGetImageBounds(lines_.back(), nullptr)).GetRightTop(); +Rect OsxCTTextLayout::TextSinglePoint(Index position, bool trailing) { + return transform_.TransformRect(DoTextSinglePoint(position, trailing)); } TextHitTestResult OsxCTTextLayout::HitTest(const Point& point) { @@ -213,6 +151,13 @@ void OsxCTTextLayout::RecreateFrame() { for (int i = 0; i < line_count_; i++) { lines_[i] = static_cast(CFArrayGetValueAtIndex(lines, i)); } + + auto bounds = DoGetTextBounds(false); + + transform_ = + Matrix::Translation(-bounds.GetRight() / 2, -bounds.GetBottom() / 2) * + Matrix::Scale(1, -1) * + Matrix::Translation(bounds.GetRight() / 2, bounds.GetBottom() / 2); } CTFrameRef OsxCTTextLayout::CreateFrameWithColor(const Color& color) { @@ -243,4 +188,94 @@ String OsxCTTextLayout::GetDebugString() { max_height_); } +Rect OsxCTTextLayout::DoGetTextBounds(bool includingTrailingSpace) { + if (text_.empty()) return Rect{}; + + float left = std::numeric_limits::max(); + float top = std::numeric_limits::max(); + float right = 0; + float bottom = 0; + + for (int i = 0; i < line_count_; i++) { + auto line = lines_[i]; + const auto& line_origin = line_origins_[i]; + + CGRect line_rect = CTLineGetImageBounds(line, nullptr); + if (includingTrailingSpace) { + float trailingWidth = CTLineGetTrailingWhitespaceWidth(line); + line_rect.size.width += trailingWidth; + } + + line_rect.origin.x += line_origin.x; + line_rect.origin.y += line_origin.y; + + left = std::min(line_rect.origin.x, left); + top = std::min(line_rect.origin.y, top); + right = std::max(line_rect.origin.x + line_rect.size.width, right); + bottom = + std::max(line_rect.origin.y + line_rect.size.height, bottom); + } + + return Rect::FromVertices(left, top, right, bottom); +} + +std::vector OsxCTTextLayout::DoTextRangeRect( + const TextRange& text_range) { + const auto r = text_.RangeFromCodeUnitToCodePoint(text_range).Normalize(); + + std::vector results; + + for (int i = 0; i < line_count_; i++) { + auto line = lines_[i]; + const auto& line_origin = line_origins_[i]; + + Range range = Convert(CTLineGetStringRange(line)); + range = range.CoerceInto(r.GetStart(), r.GetEnd()); + + if (range.count) { + auto line_rect = CTLineGetImageBounds(line, nullptr); + line_rect.origin.x += line_origin.x; + line_rect.origin.y += line_origin.y; + float start_offset = + CTLineGetOffsetForStringIndex(line, range.GetStart(), nullptr); + float end_offset = + CTLineGetOffsetForStringIndex(line, range.GetEnd(), nullptr); + line_rect.origin.x += start_offset; + line_rect.size.width = end_offset - start_offset; + results.push_back(Convert(line_rect)); + } + } + + return results; +} + +Rect OsxCTTextLayout::DoTextSinglePoint(Index position, bool trailing) { + if (lines_.empty()) return Rect{0, 0, 0, font_->GetFontSize()}; + + position = text_.IndexFromCodeUnitToCodePoint(position); + for (int i = 0; i < line_count_; i++) { + auto line = lines_[i]; + const auto& line_origin = line_origins_[i]; + + auto rect = Convert(CTLineGetImageBounds(line, nullptr)); + rect.left += line_origin.x; + rect.top += line_origin.y; + + Range range = Convert(CTLineGetStringRange(line)); + if (range.GetStart() <= position && position < range.GetEnd()) { + auto offset = CTLineGetOffsetForStringIndex(line, position, nullptr); + return Rect(rect.left + offset, rect.top, 0, rect.height); + } else if (position == range.GetEnd()) { + return Rect(rect.GetRight(), rect.top, 0, rect.height); + } + } + + // TODO: Complete logic here. + + auto rect = Convert(CTLineGetImageBounds(lines_.back(), nullptr)); + rect.left = rect.GetRight(); + rect.width = 0; + return rect; +} + } // namespace cru::platform::graphics::osx::quartz diff --git a/src/osx/gui/Window.mm b/src/osx/gui/Window.mm index 9a71d271..356c6be6 100644 --- a/src/osx/gui/Window.mm +++ b/src/osx/gui/Window.mm @@ -27,7 +27,6 @@ #include #include -#include #include #include #include @@ -496,11 +495,11 @@ const std::unordered_set bypass_codes{ auto ss = Convert(s); - cru::log::TagDebug(u"CruView", - u"Received setMarkedText string: {}, selected range: ({}, {}), " - u"replacement range: ({}, {}).", - ss, selectedRange.location, selectedRange.length, replacementRange.location, - replacementRange.length); + // cru::log::TagDebug(u"CruView", + // u"Received setMarkedText string: {}, selected range: ({}, {}), " + // u"replacement range: ({}, {}).", + // ss, selectedRange.location, selectedRange.length, replacementRange.location, + // replacementRange.length); if (_input_context_text == nil) { _input_context_text = [[NSMutableAttributedString alloc] init]; @@ -557,8 +556,8 @@ const std::unordered_set bypass_codes{ _input_context_p->SetCompositionText(cru::platform::gui::CompositionText()); cru::String ss = Convert(s); - cru::log::TagDebug(u"CruView", u"Finish composition: {}, replacement range: ({}, {})", ss, - replacementRange.location, replacementRange.length); + // cru::log::TagDebug(u"CruView", u"Finish composition: {}, replacement range: ({}, {})", ss, + // replacementRange.location, replacementRange.length); _input_context_p->RaiseCompositionEvent(); _input_context_p->RaiseCompositionEndEvent(); diff --git a/src/ui/Helper.hpp b/src/ui/Helper.hpp index 27f1d7e6..23d24b66 100644 --- a/src/ui/Helper.hpp +++ b/src/ui/Helper.hpp @@ -6,7 +6,6 @@ namespace graphics { struct IGraphicsFactory; } namespace native { -struct ICursor; struct IUiApplication; } // namespace native } // namespace cru::platform diff --git a/src/ui/render/TextRenderObject.cpp b/src/ui/render/TextRenderObject.cpp index 189c5532..df95b5f4 100644 --- a/src/ui/render/TextRenderObject.cpp +++ b/src/ui/render/TextRenderObject.cpp @@ -62,7 +62,7 @@ std::vector TextRenderObject::TextRangeRect(const TextRange& text_range) { return text_layout_->TextRangeRect(text_range); } -Point TextRenderObject::TextSinglePoint(gsl::index position, bool trailing) { +Rect TextRenderObject::TextSinglePoint(gsl::index position, bool trailing) { return text_layout_->TextSinglePoint(position, trailing); } @@ -134,8 +134,8 @@ Rect TextRenderObject::GetCaretRectInContent() { const auto font_height = this->font_->GetFontSize(); const auto caret_width = this->caret_width_; - auto rect = Rect{caret_top_center.x - caret_width / 2.0f, caret_top_center.y, - caret_width, font_height}; + auto rect = Rect{caret_top_center.left - caret_width / 2.0f, + caret_top_center.top, caret_width, caret_top_center.height}; return rect; } -- cgit v1.2.3