diff options
author | crupest <crupest@outlook.com> | 2021-11-16 21:02:21 +0800 |
---|---|---|
committer | crupest <crupest@outlook.com> | 2021-11-16 21:02:21 +0800 |
commit | 835ec1f25454cd14c5eda45b5bcb614c894fc948 (patch) | |
tree | 3b077988ba8ad6d02fc5a9307c90017b49e20e45 /src | |
parent | 59844d990aa72d1f69a3bd30efc94e6d23fa1468 (diff) | |
download | cru-835ec1f25454cd14c5eda45b5bcb614c894fc948.tar.gz cru-835ec1f25454cd14c5eda45b5bcb614c894fc948.tar.bz2 cru-835ec1f25454cd14c5eda45b5bcb614c894fc948.zip |
...
Diffstat (limited to 'src')
-rw-r--r-- | src/osx/graphics/quartz/TextLayout.cpp | 191 | ||||
-rw-r--r-- | src/ui/controls/TextBox.cpp | 1 | ||||
-rw-r--r-- | src/ui/render/TextRenderObject.cpp | 6 |
3 files changed, 158 insertions, 40 deletions
diff --git a/src/osx/graphics/quartz/TextLayout.cpp b/src/osx/graphics/quartz/TextLayout.cpp index 2cd750d0..e47c0109 100644 --- a/src/osx/graphics/quartz/TextLayout.cpp +++ b/src/osx/graphics/quartz/TextLayout.cpp @@ -1,4 +1,5 @@ #include "cru/osx/graphics/quartz/TextLayout.hpp" +#include "cru/common/StringUtil.hpp" #include "cru/osx/Convert.hpp" #include "cru/osx/graphics/quartz/Convert.hpp" #include "cru/osx/graphics/quartz/Resource.hpp" @@ -45,11 +46,45 @@ void OsxCTTextLayout::SetFont(std::shared_ptr<IFont> font) { } void OsxCTTextLayout::SetText(String new_text) { + if (new_text == text_) return; + text_ = std::move(new_text); - CFRelease(cf_attributed_text_); + if (text_.empty()) { + head_empty_line_count_ = 0; + tail_empty_line_count_ = 1; - CFStringRef s = Convert(text_); + actual_text_ = {}; + } else { + head_empty_line_count_ = 0; + tail_empty_line_count_ = 0; + + for (auto i = text_.cbegin(); i != text_.cend(); ++i) { + if (*i == u'\n') { + head_empty_line_count_++; + } else { + break; + } + } + + for (auto i = text_.crbegin(); i != text_.crend(); ++i) { + if (*i == u'\n') { + tail_empty_line_count_++; + } else { + break; + } + } + + if (text_.size() == tail_empty_line_count_) { + head_empty_line_count_ = 1; + actual_text_ = {}; + } else { + actual_text_ = String(text_.cbegin() + head_empty_line_count_, + text_.cend() - tail_empty_line_count_); + } + } + + CFStringRef s = Convert(actual_text_); cf_attributed_text_ = CFAttributedStringCreateMutable(nullptr, 0); CFAttributedStringReplaceString(cf_attributed_text_, CFRangeMake(0, 0), s); Ensures(cf_attributed_text_); @@ -72,33 +107,91 @@ void OsxCTTextLayout::SetMaxHeight(float max_height) { RecreateFrame(); } +bool OsxCTTextLayout::IsEditMode() { return edit_mode_; } + +void OsxCTTextLayout::SetEditMode(bool enable) { + edit_mode_ = enable; + RecreateFrame(); +} + Rect OsxCTTextLayout::GetTextBounds(bool includingTrailingSpace) { - return transform_.TransformRect(DoGetTextBounds(includingTrailingSpace)); + auto result = DoGetTextBoundsIncludingEmptyLines(includingTrailingSpace); + return Rect(0, 0, result.size.width, result.size.height); } std::vector<Rect> OsxCTTextLayout::TextRangeRect(const TextRange& text_range) { - std::vector<Rect> results = DoTextRangeRect(text_range); + if (text_.empty()) return {}; + + auto tr = text_range; + tr.position += head_empty_line_count_; + std::vector<CGRect> results = DoTextRangeRect(tr); + std::vector<Rect> r; for (auto& rect : results) { - rect = transform_.TransformRect(rect); + r.push_back(transform_.TransformRect(Convert(rect))); } - return results; + return r; } Rect OsxCTTextLayout::TextSinglePoint(Index position, bool trailing) { - return transform_.TransformRect(DoTextSinglePoint(position, trailing)); + Expects(position >= 0 && position <= text_.size()); + + if (text_.empty()) return {0, 0, 0, font_->GetFontSize()}; + + if (position < head_empty_line_count_) { + return {0, position * font_->GetFontSize(), 0, font_->GetFontSize()}; + } else if (position > text_.size() - tail_empty_line_count_) { + return {0, + static_cast<float>(DoGetTextBounds().size.height) + + (head_empty_line_count_ + position - + (text_.size() - tail_empty_line_count_) - 1) * + font_->GetFontSize(), + 0, font_->GetFontSize()}; + } else { + auto result = + DoTextSinglePoint(position - head_empty_line_count_, trailing); + return transform_.TransformRect(Convert(result)); + } } TextHitTestResult OsxCTTextLayout::HitTest(const Point& point) { - auto p = transform_.Inverted()->TransformPoint(point); + if (point.y < head_empty_line_count_ * font_->GetFontSize()) { + if (point.y < 0) { + return {0, false, false}; + } else { + for (int i = 0; i < head_empty_line_count_; ++i) { + if (point.y < i * font_->GetFontSize()) { + return {i, false, false}; + } + } + } + } + + auto text_bounds = DoGetTextBounds(); + + auto text_height = static_cast<float>(text_bounds.size.height); + auto th = text_height + head_empty_line_count_ * font_->GetFontSize(); + if (point.y >= th) { + for (int i = 0; i < tail_empty_line_count_; ++i) { + if (point.y < th + i * font_->GetFontSize()) { + return {text_.size() - i, false, false}; + } + } + } + + auto p = point; + p.y -= head_empty_line_count_ * font_->GetFontSize(); + p.x += text_bounds.origin.x; + p.y += text_bounds.origin.y; + p.y = text_height - p.y; for (int i = 0; i < line_count_; i++) { auto line = lines_[i]; const auto& line_origin = line_origins_[i]; - auto range = - text_.RangeFromCodePointToCodeUnit(Convert(CTLineGetStringRange(line))); + auto range = actual_text_.RangeFromCodePointToCodeUnit( + Convert(CTLineGetStringRange(line))); auto bounds = CTLineGetImageBounds(line, nullptr); bounds.origin.x += line_origin.x; @@ -118,13 +211,19 @@ TextHitTestResult OsxCTTextLayout::HitTest(const Point& point) { pp.y = bounds.origin.y; if (pp.x < bounds.origin.x) { - return TextHitTestResult{range.position, false, false}; + return TextHitTestResult{range.position + head_empty_line_count_, false, + false}; } else if (pp.x > bounds.origin.x + bounds.size.width) { - return TextHitTestResult{range.GetEnd(), false, false}; + auto po = text_.IndexFromCodePointToCodeUnit(range.GetEnd()); + if (po != text_.size()) { + Utf16PreviousCodePoint(text_, po, &po); + } + return TextHitTestResult{po + head_empty_line_count_, false, false}; } else { int position = CTLineGetStringIndexForPosition( line, CGPointMake(pp.x - line_origin.x, pp.y - line_origin.y)); - return TextHitTestResult{text_.IndexFromCodePointToCodeUnit(position), + return TextHitTestResult{text_.IndexFromCodePointToCodeUnit(position) + + head_empty_line_count_, false, true}; } } @@ -181,9 +280,11 @@ void OsxCTTextLayout::RecreateFrame() { auto bounds = DoGetTextBounds(false); transform_ = - Matrix::Translation(-bounds.GetRight() / 2, -bounds.GetBottom() / 2) * + Matrix::Translation(-bounds.origin.x, -bounds.origin.y) * + Matrix::Translation(-bounds.size.width / 2, -bounds.size.height / 2) * Matrix::Scale(1, -1) * - Matrix::Translation(bounds.GetRight() / 2, bounds.GetBottom() / 2); + Matrix::Translation(bounds.size.width / 2, bounds.size.height / 2) * + Matrix::Translation(0, head_empty_line_count_ * font_->GetFontSize()); } CTFrameRef OsxCTTextLayout::CreateFrameWithColor(const Color& color) { @@ -214,13 +315,13 @@ String OsxCTTextLayout::GetDebugString() { max_height_); } -Rect OsxCTTextLayout::DoGetTextBounds(bool includingTrailingSpace) { - if (text_.empty()) return Rect{}; +CGRect OsxCTTextLayout::DoGetTextBounds(bool includingTrailingSpace) { + if (actual_text_.empty()) return CGRect{}; float left = std::numeric_limits<float>::max(); - float top = std::numeric_limits<float>::max(); + float bottom = std::numeric_limits<float>::max(); float right = 0; - float bottom = 0; + float top = 0; for (int i = 0; i < line_count_; i++) { auto line = lines_[i]; @@ -236,20 +337,29 @@ Rect OsxCTTextLayout::DoGetTextBounds(bool includingTrailingSpace) { line_rect.origin.y += line_origin.y; left = std::min<float>(line_rect.origin.x, left); - top = std::min<float>(line_rect.origin.y, top); + bottom = std::min<float>(line_rect.origin.y, bottom); right = std::max<float>(line_rect.origin.x + line_rect.size.width, right); - bottom = - std::max<float>(line_rect.origin.y + line_rect.size.height, bottom); + top = std::max<float>(line_rect.origin.y + line_rect.size.height, top); } - return Rect::FromVertices(left, top, right, bottom); + return CGRectMake(left, bottom, right - left, top - bottom); } -std::vector<Rect> OsxCTTextLayout::DoTextRangeRect( +CGRect OsxCTTextLayout::DoGetTextBoundsIncludingEmptyLines( + bool includingTrailingSpace) { + auto result = DoGetTextBounds(includingTrailingSpace); + + result.size.height += head_empty_line_count_ * font_->GetFontSize(); + result.size.height += tail_empty_line_count_ * font_->GetFontSize(); + + return result; +} + +std::vector<CGRect> OsxCTTextLayout::DoTextRangeRect( const TextRange& text_range) { const auto r = text_.RangeFromCodeUnitToCodePoint(text_range).Normalize(); - std::vector<Rect> results; + std::vector<CGRect> results; for (int i = 0; i < line_count_; i++) { auto line = lines_[i]; @@ -268,38 +378,39 @@ std::vector<Rect> OsxCTTextLayout::DoTextRangeRect( 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)); + results.push_back(line_rect); } } return results; } -Rect OsxCTTextLayout::DoTextSinglePoint(Index position, bool trailing) { - if (lines_.empty()) return Rect{0, 0, 0, font_->GetFontSize()}; +CGRect OsxCTTextLayout::DoTextSinglePoint(Index position, bool trailing) { + Expects(position >= 0 && position <= actual_text_.size()); + + if (actual_text_.empty()) return CGRectMake(0, 0, 0, font_->GetFontSize()); - position = text_.IndexFromCodeUnitToCodePoint(position); + position = actual_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; + auto rect = CTLineGetImageBounds(line, nullptr); + rect.origin.x += line_origin.x; + rect.origin.y += 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); + return CGRectMake(rect.origin.x + offset, rect.origin.y, 0, + rect.size.height); } } - // TODO: Complete logic here. - - auto rect = Convert(CTLineGetImageBounds(lines_.back(), nullptr)); - rect.left = rect.GetRight(); - rect.width = 0; - return rect; + auto rect = CTLineGetImageBounds(lines_.back(), nullptr); + rect.origin.x += line_origins_.back().x; + rect.origin.y += line_origins_.back().y; + return CGRectMake(rect.origin.x + rect.size.width, rect.origin.y, 0, + rect.size.height); } - } // namespace cru::platform::graphics::osx::quartz diff --git a/src/ui/controls/TextBox.cpp b/src/ui/controls/TextBox.cpp index bfc98c06..622401c4 100644 --- a/src/ui/controls/TextBox.cpp +++ b/src/ui/controls/TextBox.cpp @@ -20,6 +20,7 @@ TextBox::TextBox() text_render_object_ = std::make_unique<TextRenderObject>( theme_resources->text_brush, theme_resources->default_font, theme_resources->text_selection_brush, theme_resources->caret_brush); + text_render_object_->SetEditMode(true); border_render_object_->AddChild(scroll_render_object_.get(), 0); scroll_render_object_->AddChild(text_render_object_.get(), 0); diff --git a/src/ui/render/TextRenderObject.cpp b/src/ui/render/TextRenderObject.cpp index df95b5f4..e1036072 100644 --- a/src/ui/render/TextRenderObject.cpp +++ b/src/ui/render/TextRenderObject.cpp @@ -58,6 +58,12 @@ void TextRenderObject::SetFont( text_layout_->SetFont(std::move(font)); } +bool TextRenderObject::IsEditMode() { return text_layout_->IsEditMode(); } + +void TextRenderObject::SetEditMode(bool enable) { + text_layout_->SetEditMode(enable); +} + std::vector<Rect> TextRenderObject::TextRangeRect(const TextRange& text_range) { return text_layout_->TextRangeRect(text_range); } |