diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/osx/graphics/quartz/TextLayout.cpp | 134 | ||||
-rw-r--r-- | src/osx/gui/Window.mm | 4 | ||||
-rw-r--r-- | src/ui/controls/TextBox.cpp | 4 | ||||
-rw-r--r-- | src/ui/host/WindowHost.cpp | 9 |
4 files changed, 78 insertions, 73 deletions
diff --git a/src/osx/graphics/quartz/TextLayout.cpp b/src/osx/graphics/quartz/TextLayout.cpp index c3a138f2..8e5022a9 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/Base.hpp" #include "cru/common/StringUtil.hpp" #include "cru/osx/Convert.hpp" #include "cru/osx/graphics/quartz/Convert.hpp" @@ -22,15 +23,7 @@ OsxCTTextLayout::OsxCTTextLayout(IGraphicsFactory* graphics_factory, text_(str) { Expects(font_); - CFStringRef s = Convert(text_); - cf_attributed_text_ = CFAttributedStringCreateMutable(nullptr, 0); - CFAttributedStringReplaceString(cf_attributed_text_, CFRangeMake(0, 0), s); - Ensures(cf_attributed_text_); - CFAttributedStringSetAttribute( - cf_attributed_text_, - CFRangeMake(0, CFAttributedStringGetLength(cf_attributed_text_)), - kCTFontAttributeName, font_->GetCTFont()); - CFRelease(s); + DoSetText(std::move(text_)); RecreateFrame(); } @@ -45,10 +38,8 @@ void OsxCTTextLayout::SetFont(std::shared_ptr<IFont> font) { RecreateFrame(); } -void OsxCTTextLayout::SetText(String new_text) { - if (new_text == text_) return; - - text_ = std::move(new_text); +void OsxCTTextLayout::DoSetText(String text) { + text_ = std::move(text); if (text_.empty()) { head_empty_line_count_ = 0; @@ -93,6 +84,13 @@ void OsxCTTextLayout::SetText(String new_text) { CFRangeMake(0, CFAttributedStringGetLength(cf_attributed_text_)), kCTFontAttributeName, font_->GetCTFont()); CFRelease(s); +} + +void OsxCTTextLayout::SetText(String new_text) { + if (new_text == text_) return; + + CFRelease(cf_attributed_text_); + DoSetText(std::move(new_text)); RecreateFrame(); } @@ -125,7 +123,8 @@ std::vector<Rect> OsxCTTextLayout::TextRangeRect(const TextRange& text_range) { if (text_.empty()) return {}; auto tr = text_range; - tr.position += head_empty_line_count_; + text_range.CoerceInto(head_empty_line_count_, + text_.size() - tail_empty_line_count_); std::vector<CGRect> results = DoTextRangeRect(tr); std::vector<Rect> r; @@ -185,20 +184,18 @@ TextHitTestResult OsxCTTextLayout::HitTest(const Point& point) { 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 line_origin = line_origins_[i]; - auto range = actual_text_.RangeFromCodePointToCodeUnit( - Convert(CTLineGetStringRange(line))); + auto range = CTLineGetStringRange(line); - auto bounds = CTLineGetImageBounds(line, nullptr); - bounds.origin.x += line_origin.x; - bounds.origin.y += line_origin.y; + CGRect bounds{line_origin.x, line_origin.y - line_descents_[i], + CTLineGetOffsetForStringIndex( + line, range.location + range.length, nullptr), + line_heights_[i]}; bool force_inside = false; if (i == 0 && p.y >= bounds.origin.y + bounds.size.height) { @@ -214,17 +211,21 @@ TextHitTestResult OsxCTTextLayout::HitTest(const Point& point) { pp.y = bounds.origin.y; if (pp.x < bounds.origin.x) { - return TextHitTestResult{range.position + head_empty_line_count_, false, - false}; + return TextHitTestResult{ + actual_text_.IndexFromCodePointToCodeUnit(range.location) + + head_empty_line_count_, + false, false}; } else if (pp.x > bounds.origin.x + bounds.size.width) { - auto po = text_.IndexFromCodePointToCodeUnit(range.GetEnd()); - if (po != text_.size()) { + auto po = actual_text_.IndexFromCodePointToCodeUnit(range.location + + range.length); + if (po != actual_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)); + line, + CGPointMake(pp.x - line_origins_[i].x, pp.y - line_origins_[i].y)); return TextHitTestResult{text_.IndexFromCodePointToCodeUnit(position) + head_empty_line_count_, false, true}; @@ -239,6 +240,9 @@ void OsxCTTextLayout::ReleaseResource() { line_count_ = 0; line_origins_.clear(); lines_.clear(); + line_ascents_.clear(); + line_descents_.clear(); + line_heights_.clear(); if (ct_framesetter_) CFRelease(ct_framesetter_); if (ct_frame_) CFRelease(ct_frame_); } @@ -275,20 +279,29 @@ void OsxCTTextLayout::RecreateFrame() { line_count_ = CFArrayGetCount(lines); lines_.resize(line_count_); line_origins_.resize(line_count_); + line_ascents_.resize(line_count_); + line_descents_.resize(line_count_); + line_heights_.resize(line_count_); CTFrameGetLineOrigins(ct_frame_, CFRangeMake(0, 0), line_origins_.data()); for (int i = 0; i < line_count_; i++) { lines_[i] = static_cast<CTLineRef>(CFArrayGetValueAtIndex(lines, i)); + double ascent, descent; + CTLineGetTypographicBounds(lines_[i], &ascent, &descent, nullptr); + line_ascents_[i] = static_cast<float>(ascent); + line_descents_[i] = static_cast<float>(descent); + line_heights_[i] = line_ascents_[i] + line_descents_[i]; } auto bounds = DoGetTextBounds(false); text_bounds_without_trailing_space_ = bounds; text_bounds_with_trailing_space_ = DoGetTextBounds(true); + auto right = bounds.origin.x + bounds.size.width; + auto bottom = bounds.origin.y + bounds.size.height; + transform_ = - 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.size.width / 2, bounds.size.height / 2) * + Matrix::Translation(-right / 2, -bottom / 2) * Matrix::Scale(1, -1) * + Matrix::Translation(right / 2, bottom / 2) * Matrix::Translation(0, head_empty_line_count_ * font_->GetFontSize()); } @@ -323,28 +336,20 @@ String OsxCTTextLayout::GetDebugString() { CGRect OsxCTTextLayout::DoGetTextBounds(bool includingTrailingSpace) { if (actual_text_.empty()) return CGRect{}; + auto rects = DoTextRangeRect(TextRange{0, actual_text_.size()}); + float left = std::numeric_limits<float>::max(); float bottom = std::numeric_limits<float>::max(); float right = 0; float top = 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<float>(line_rect.origin.x, left); - bottom = std::min<float>(line_rect.origin.y, bottom); - right = std::max<float>(line_rect.origin.x + line_rect.size.width, right); - top = std::max<float>(line_rect.origin.y + line_rect.size.height, top); + for (auto& rect : rects) { + if (rect.origin.x < left) left = rect.origin.x; + if (rect.origin.y < bottom) bottom = rect.origin.y; + if (rect.origin.x + rect.size.width > right) + right = rect.origin.x + rect.size.width; + if (rect.origin.y + rect.size.height > top) + top = rect.origin.y + rect.size.height; } return CGRectMake(left, bottom, right - left, top - bottom); @@ -363,21 +368,21 @@ CGRect OsxCTTextLayout::DoGetTextBoundsIncludingEmptyLines( std::vector<CGRect> OsxCTTextLayout::DoTextRangeRect( const TextRange& text_range) { - const auto r = text_.RangeFromCodeUnitToCodePoint(text_range).Normalize(); + const auto r = + actual_text_.RangeFromCodeUnitToCodePoint(text_range).Normalize(); std::vector<CGRect> results; for (int i = 0; i < line_count_; i++) { auto line = lines_[i]; - const auto& line_origin = line_origins_[i]; + 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; + CGRect line_rect{line_origin.x, line_origin.y - line_descents_[i], 0, + line_heights_[i]}; float start_offset = CTLineGetOffsetForStringIndex(line, range.GetStart(), nullptr); float end_offset = @@ -397,26 +402,21 @@ CGRect OsxCTTextLayout::DoTextSinglePoint(Index position, bool trailing) { if (actual_text_.empty()) return CGRectMake(0, 0, 0, font_->GetFontSize()); 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 = CTLineGetImageBounds(line, nullptr); - rect.origin.x += line_origin.x; - rect.origin.y += line_origin.y; + auto line_origin = line_origins_[i]; - Range range = Convert(CTLineGetStringRange(line)); - if (range.GetStart() <= position && position < range.GetEnd()) { + CFRange range = CTLineGetStringRange(line); + if (range.location <= position && + position < range.location + range.length || + i == line_count_ - 1 && position == range.location + range.length) { auto offset = CTLineGetOffsetForStringIndex(line, position, nullptr); - return CGRectMake(rect.origin.x + offset, rect.origin.y, 0, - rect.size.height); + return CGRectMake(offset + line_origin.x, + line_origin.y - line_descents_[i], 0, line_heights_[i]); } } - 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); + UnreachableCode(); } } // namespace cru::platform::graphics::osx::quartz diff --git a/src/osx/gui/Window.mm b/src/osx/gui/Window.mm index 06bbaff9..6e4c8497 100644 --- a/src/osx/gui/Window.mm +++ b/src/osx/gui/Window.mm @@ -259,9 +259,9 @@ Size OsxWindow::GetClientSize() { return p_->content_rect_.GetSize(); } void OsxWindow::SetClientSize(const Size& size) { if (p_->window_) { - auto rect = GetWindowRect(); + auto rect = GetClientRect(); rect.SetSize(size); - SetWindowRect(rect); + SetClientRect(rect); } else { p_->content_rect_.SetSize(size); } diff --git a/src/ui/controls/TextBox.cpp b/src/ui/controls/TextBox.cpp index c4912307..47706174 100644 --- a/src/ui/controls/TextBox.cpp +++ b/src/ui/controls/TextBox.cpp @@ -46,6 +46,10 @@ render::RenderObject* TextBox::GetRenderObject() const { return border_render_object_.get(); } +bool TextBox::GetMultiLine() const { return service_->IsMultiLine(); } + +void TextBox::SetMultiLine(bool value) { service_->SetMultiLine(value); } + gsl::not_null<render::TextRenderObject*> TextBox::GetTextRenderObject() { return text_render_object_.get(); } diff --git a/src/ui/host/WindowHost.cpp b/src/ui/host/WindowHost.cpp index 034d9bd8..9766e85e 100644 --- a/src/ui/host/WindowHost.cpp +++ b/src/ui/host/WindowHost.cpp @@ -181,10 +181,11 @@ void WindowHost::Relayout() { void WindowHost::RelayoutWithSize(const Size& available_size, bool set_window_size_to_fit_content) { root_render_object_->Measure( - render::MeasureRequirement{available_size, - IsLayoutPreferToFillWindow() - ? render::MeasureSize(available_size) - : render::MeasureSize::NotSpecified()}, + render::MeasureRequirement{ + available_size, + !set_window_size_to_fit_content && IsLayoutPreferToFillWindow() + ? render::MeasureSize(available_size) + : render::MeasureSize::NotSpecified()}, render::MeasureSize::NotSpecified()); if (set_window_size_to_fit_content) { |