diff --git a/data/strings/en.ini b/data/strings/en.ini index 5be9935a0..0eab13484 100644 --- a/data/strings/en.ini +++ b/data/strings/en.ini @@ -1834,6 +1834,18 @@ pixel_scale = Pixel Scale with_vars = Use CSS3 Variables generate_html = Generate Sample HTML File +[shape] +fill = Fill +stroke = Stroke +stroke_width = Stroke Width + +[text_tool] +font_family = Font Family +font_size = Font Size +bold = Bold +italic = Italic +more_options = More Options + [timeline_conf] position = Position: left = &Left diff --git a/laf b/laf index 375629b7b..a2bb9ec7f 160000 --- a/laf +++ b/laf @@ -1 +1 @@ -Subproject commit 375629b7bda67f57988804a12e78c55afe005c9d +Subproject commit a2bb9ec7fb98354279a2c49870a4a47a67a8e86e diff --git a/src/app/commands/cmd_paste_text.cpp b/src/app/commands/cmd_paste_text.cpp index d67175657..976d1d3e3 100644 --- a/src/app/commands/cmd_paste_text.cpp +++ b/src/app/commands/cmd_paste_text.cpp @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2019-2024 Igara Studio S.A. +// Copyright (C) 2019-2025 Igara Studio S.A. // Copyright (C) 2001-2018 David Capello // // This program is distributed under the terms of @@ -10,6 +10,7 @@ #endif #include "app/app.h" +#include "app/color_utils.h" #include "app/commands/command.h" #include "app/console.h" #include "app/context.h" @@ -88,10 +89,10 @@ void PasteTextCommand::onExecute(Context* ctx) std::string text = window.userText()->text(); app::Color color = window.fontColor()->getColor(); - doc::ImageRef image = render_text( - fontInfo, - text, - gfx::rgba(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha())); + ui::Paint paint = window.fontFace()->paint(); + paint.color(color_utils::color_for_ui(color)); + + doc::ImageRef image = render_text(fontInfo, text, paint); if (image) { Sprite* sprite = editor->sprite(); if (image->pixelFormat() != sprite->pixelFormat()) { diff --git a/src/app/ui/context_bar.cpp b/src/app/ui/context_bar.cpp index 334c238d8..22e610ad9 100644 --- a/src/app/ui/context_bar.cpp +++ b/src/app/ui/context_bar.cpp @@ -2559,6 +2559,11 @@ FontInfo ContextBar::fontInfo() const return m_fontSelector->info(); } +FontEntry* ContextBar::fontEntry() +{ + return m_fontSelector; +} + render::DitheringMatrix ContextBar::ditheringMatrix() { return m_ditheringSelector->ditheringMatrix(); diff --git a/src/app/ui/context_bar.h b/src/app/ui/context_bar.h index 1bb0d3496..1db6754f2 100644 --- a/src/app/ui/context_bar.h +++ b/src/app/ui/context_bar.h @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2018-2024 Igara Studio S.A. +// Copyright (C) 2018-2025 Igara Studio S.A. // Copyright (C) 2001-2017 David Capello // // This program is distributed under the terms of @@ -90,6 +90,7 @@ public: // For text tool FontInfo fontInfo() const; + FontEntry* fontEntry(); // For gradients render::DitheringMatrix ditheringMatrix(); diff --git a/src/app/ui/editor/writing_text_state.cpp b/src/app/ui/editor/writing_text_state.cpp index ca444a1b6..f36db7884 100644 --- a/src/app/ui/editor/writing_text_state.cpp +++ b/src/app/ui/editor/writing_text_state.cpp @@ -15,6 +15,7 @@ #include "app/commands/command.h" #include "app/extra_cel.h" #include "app/fonts/font_info.h" +#include "app/i18n/strings.h" #include "app/pref/preferences.h" #include "app/site.h" #include "app/tx.h" @@ -43,12 +44,42 @@ #include "os/skia/skia_surface.h" #endif +#include + namespace app { using namespace ui; +// Get ui::Paint to render text from context bar options / preferences +static ui::Paint get_paint_for_text() +{ + ui::Paint paint; + if (auto* app = App::instance()) { + if (auto* ctxBar = app->contextBar()) + paint = ctxBar->fontEntry()->paint(); + } + paint.color(color_utils::color_for_ui(Preferences::instance().colorBar.fgColor())); + return paint; +} + +static gfx::RectF calc_blob_bounds(const text::TextBlobRef& blob) +{ + gfx::RectF bounds = get_text_blob_required_bounds(blob); + ui::Paint paint = get_paint_for_text(); + if (paint.style() == ui::Paint::Style::Stroke || + paint.style() == ui::Paint::Style::StrokeAndFill) { + bounds.enlarge(std::ceil(paint.strokeWidth())); + } + return bounds; +} + class WritingTextState::TextEditor : public Entry { public: + enum TextPreview { + Intermediate, // With selection preview / user interface + Final, // Final to be rendered in the cel + }; + TextEditor(Editor* editor, const Site& site, const gfx::Rect& bounds) : Entry(4096, "") , m_editor(editor) @@ -61,7 +92,7 @@ public: setPersistSelection(true); createExtraCel(site, bounds); - renderExtraCelBase(); + renderExtraCel(TextPreview::Intermediate); FontInfo fontInfo = App::instance()->contextBar()->fontInfo(); if (auto font = Fonts::instance()->fontFromInfo(fontInfo)) @@ -76,36 +107,37 @@ public: // Returns the extra cel with the text rendered (but without the // selected text highlighted). - ExtraCelRef extraCel() + ExtraCelRef extraCel(const TextPreview textPreview) { - renderExtraCelBase(); - renderExtraCelText(false); + renderExtraCel(textPreview); return m_extraCel; } - void setExtraCelBounds(const gfx::Rect& bounds) + void setExtraCelBounds(const gfx::RectF& bounds) { doc::Image* extraImg = m_extraCel->image(); - if (!extraImg || bounds.w != extraImg->width() || bounds.h != extraImg->height()) { + if (!extraImg || std::ceil(bounds.w) != extraImg->width() || + std::ceil(bounds.h) != extraImg->height()) { createExtraCel(m_editor->getSite(), bounds); } else { + m_baseBounds = bounds; m_extraCel->cel()->setBounds(bounds); } - renderExtraCelBase(); - renderExtraCelText(true); + renderExtraCel(TextPreview::Intermediate); } - obs::signal NewRequiredBounds; + obs::signal NewRequiredBounds; private: void createExtraCel(const Site& site, const gfx::Rect& bounds) { + m_baseBounds = bounds; m_extraCel->create(ExtraCel::Purpose::TextPreview, site.tilemapMode(), site.sprite(), bounds, - bounds.size(), + gfx::Size(std::ceil(bounds.w), std::ceil(bounds.h)), site.frame(), 255); @@ -176,7 +208,7 @@ private: // Notify that we could make the text editor bigger to show this // text blob. - NewRequiredBounds(get_text_blob_required_size(blob)); + NewRequiredBounds(calc_blob_bounds(blob)); } void onPaint(PaintEvent& ev) override @@ -205,8 +237,7 @@ private: } // Render extra cel with text + selected text - renderExtraCelBase(); - renderExtraCelText(true); + renderExtraCel(TextPreview::Intermediate); m_doc->setExtraCel(m_extraCel); // Paint caret @@ -227,76 +258,80 @@ private: } } - void renderExtraCelBase() + void renderExtraCel(const TextPreview textPreview) { doc::Image* extraImg = m_extraCel->image(); ASSERT(extraImg); if (!extraImg) return; - const doc::Cel* extraCel = m_extraCel->cel(); + extraImg->clear(extraImg->maskColor()); + + text::TextBlobRef blob = textBlob(); + doc::ImageRef blobImage; + gfx::RectF bounds; + if (blob) { + const ui::Paint paint = get_paint_for_text(); + bounds = calc_blob_bounds(blob); + blobImage = render_text_blob(blob, bounds, get_paint_for_text()); + if (!blobImage) + return; + + // Invert selected range in the image + if (textPreview == TextPreview::Intermediate) { + Range range; + getEntryThemeInfo(nullptr, nullptr, nullptr, &range); + if (!range.isEmpty()) { + gfx::RectF selectedBounds = getCharBoxBounds(range.from) | getCharBoxBounds(range.to - 1); + + if (!selectedBounds.isEmpty()) { + selectedBounds.offset(-bounds.origin()); + +#ifdef LAF_SKIA + sk_sp skSurface = wrap_docimage_in_sksurface(blobImage.get()); + os::SurfaceRef surface = base::make_ref(skSurface); + + os::Paint paint2 = paint; + paint2.blendMode(os::BlendMode::Xor); + paint2.style(os::Paint::Style::Fill); + surface->drawRect(selectedBounds, paint2); +#endif // LAF_SKIA + } + } + } + } + + doc::Cel* extraCel = m_extraCel->cel(); ASSERT(extraCel); if (!extraCel) return; - extraImg->clear(extraImg->maskColor()); + extraCel->setPosition(m_baseBounds.x + bounds.x, m_baseBounds.y + bounds.y); + render::Render().renderLayer(extraImg, m_editor->layer(), m_editor->frame(), gfx::Clip(0, 0, extraCel->bounds()), doc::BlendMode::SRC); - } - void renderExtraCelText(const bool withSelection) - { - const auto textColor = color_utils::color_for_image(Preferences::instance().colorBar.fgColor(), - IMAGE_RGB); - - text::TextBlobRef blob = textBlob(); - if (!blob) - return; - - doc::ImageRef image = render_text_blob(blob, textColor); - if (!image) - return; - - // Invert selected range in the image - if (withSelection) { - Range range; - getEntryThemeInfo(nullptr, nullptr, nullptr, &range); - if (!range.isEmpty()) { - gfx::RectF selectedBounds = getCharBoxBounds(range.from) | getCharBoxBounds(range.to - 1); - - if (!selectedBounds.isEmpty()) { -#ifdef LAF_SKIA - sk_sp skSurface = wrap_docimage_in_sksurface(image.get()); - os::SurfaceRef surface = base::make_ref(skSurface); - - os::Paint paint; - paint.blendMode(os::BlendMode::Xor); - paint.color(textColor); - surface->drawRect(selectedBounds, paint); -#endif // LAF_SKIA - } - } + if (blobImage) { + doc::blend_image(extraImg, + blobImage.get(), + gfx::Clip(blobImage->bounds().size()), + m_doc->sprite()->palette(m_editor->frame()), + 255, + doc::BlendMode::NORMAL); } - - doc::Image* extraImg = m_extraCel->image(); - ASSERT(extraImg); - if (!extraImg) - return; - - doc::blend_image(extraImg, - image.get(), - gfx::Clip(image->bounds().size()), - m_doc->sprite()->palette(m_editor->frame()), - 255, - doc::BlendMode::NORMAL); } Editor* m_editor; Doc* m_doc; ExtraCelRef m_extraCel; + + // Initial bounds for the entry field. This can be modified later to + // render the text in case some initial letter/glyph needs some + // extra room at the left side. + gfx::Rect m_baseBounds; }; WritingTextState::WritingTextState(Editor* editor, const gfx::Rect& bounds) @@ -312,10 +347,10 @@ WritingTextState::WritingTextState(Editor* editor, const gfx::Rect& bounds) m_fontChangeConn = App::instance()->contextBar()->FontChange.connect(&WritingTextState::onFontChange, this); - m_entry->NewRequiredBounds.connect([this](const gfx::Size& blobSize) { - if (m_bounds.w < blobSize.w || m_bounds.h < blobSize.h) { - m_bounds.w = std::max(m_bounds.w, blobSize.w); - m_bounds.h = std::max(m_bounds.h, blobSize.h); + m_entry->NewRequiredBounds.connect([this](const gfx::RectF& blobBounds) { + if (m_bounds.w < blobBounds.w || m_bounds.h < blobBounds.h) { + m_bounds.w = std::max(m_bounds.w, blobBounds.w); + m_bounds.h = std::max(m_bounds.h, blobBounds.h); m_entry->setExtraCelBounds(m_bounds); m_entry->setBounds(calcEntryBounds()); } @@ -388,11 +423,11 @@ void WritingTextState::onCommitMouseMove(Editor* editor, const gfx::PointF& spri if (!m_movingBounds) return; - gfx::Point delta(spritePos - m_cursorStart); + gfx::PointF delta(spritePos - m_cursorStart); if (delta.x == 0 && delta.y == 0) return; - m_bounds.setOrigin(gfx::Point(delta + m_boundsOrigin)); + m_bounds.setOrigin(delta + m_boundsOrigin); m_entry->setExtraCelBounds(m_bounds); m_entry->setBounds(calcEntryBounds()); } @@ -475,8 +510,8 @@ EditorState::LeaveAction WritingTextState::onLeaveState(Editor* editor, EditorSt // Paints the text in the active layer/sprite creating an // undoable transaction. Site site = m_editor->getSite(); - ExtraCelRef extraCel = m_entry->extraCel(); - Tx tx(site.document(), "Text Tool"); + ExtraCelRef extraCel = m_entry->extraCel(TextEditor::Final); + Tx tx(site.document(), Strings::tools_text()); ExpandCelCanvas expand(site, site.layer(), TiledMode::NONE, tx, ExpandCelCanvas::None); expand.validateDestCanvas(gfx::Region(extraCel->cel()->bounds())); @@ -527,7 +562,7 @@ void WritingTextState::onFontChange(const FontInfo& fontInfo, FontEntry::From fr // This is useful to show changes to the anti-alias option // immediately. - auto dummy = m_entry->extraCel(); + auto dummy = m_entry->extraCel(TextEditor::Intermediate); if (fromField == FontEntry::From::Popup) { if (m_entry) diff --git a/src/app/ui/editor/writing_text_state.h b/src/app/ui/editor/writing_text_state.h index bd01bc918..3422acab1 100644 --- a/src/app/ui/editor/writing_text_state.h +++ b/src/app/ui/editor/writing_text_state.h @@ -1,5 +1,5 @@ // Aseprite -// Copyright (c) 2022-2024 Igara Studio S.A. +// Copyright (c) 2022-2025 Igara Studio S.A. // // This program is distributed under the terms of // the End-User License Agreement for Aseprite. @@ -59,7 +59,7 @@ private: DelayedMouseMove m_delayedMouseMove; Editor* m_editor; - gfx::Rect m_bounds; + gfx::RectF m_bounds; std::unique_ptr m_entry; // True if the text was discarded. @@ -71,7 +71,7 @@ private: bool m_mouseMoveReceived = false; bool m_movingBounds = false; gfx::PointF m_cursorStart; - gfx::Point m_boundsOrigin; + gfx::PointF m_boundsOrigin; obs::scoped_connection m_beforeCmdConn; obs::scoped_connection m_fontChangeConn; diff --git a/src/app/ui/font_entry.cpp b/src/app/ui/font_entry.cpp index 9f626120c..c0ab2f341 100644 --- a/src/app/ui/font_entry.cpp +++ b/src/app/ui/font_entry.cpp @@ -12,10 +12,12 @@ #include "app/app.h" #include "app/console.h" +#include "app/i18n/strings.h" #include "app/recent_files.h" #include "app/ui/font_popup.h" #include "app/ui/skin/skin_theme.h" #include "base/contains.h" +#include "base/convert_to.h" #include "base/scoped_value.h" #include "fmt/format.h" #include "ui/display.h" @@ -262,22 +264,88 @@ void FontEntry::FontSize::onEntryChange() Change(); } -FontEntry::FontStyle::FontStyle() : ButtonSet(3, true) +FontEntry::FontStyle::FontStyle(ui::TooltipManager* tooltips) : ButtonSet(3, true) { addItem("B"); addItem("I"); addItem("..."); setMultiMode(MultiMode::Set); + + tooltips->addTooltipFor(getItem(0), Strings::text_tool_bold(), BOTTOM); + tooltips->addTooltipFor(getItem(1), Strings::text_tool_italic(), BOTTOM); + tooltips->addTooltipFor(getItem(2), Strings::text_tool_more_options(), BOTTOM); } -FontEntry::FontEntry() +FontEntry::FontStroke::FontStroke(ui::TooltipManager* tooltips) : m_fill(2) +{ + auto* theme = skin::SkinTheme::get(this); + + m_fill.addItem(theme->parts.toolFilledRectangle(), theme->styles.contextBarButton()); + m_fill.addItem(theme->parts.toolRectangle(), theme->styles.contextBarButton()); + m_fill.setSelectedItem(0); + m_fill.ItemChange.connect([this] { Change(); }); + + m_stroke.setText("0"); + m_stroke.setSuffix("pt"); + m_stroke.ValueChange.connect([this] { Change(); }); + + addChild(&m_fill); + addChild(&m_stroke); + + tooltips->addTooltipFor(m_fill.getItem(0), Strings::shape_fill(), BOTTOM); + tooltips->addTooltipFor(m_fill.getItem(1), Strings::shape_stroke(), BOTTOM); + tooltips->addTooltipFor(&m_stroke, Strings::shape_stroke_width(), BOTTOM); +} + +bool FontEntry::FontStroke::fill() const +{ + return const_cast(this)->m_fill.getItem(0)->isSelected(); +} + +float FontEntry::FontStroke::stroke() const +{ + return m_stroke.textDouble(); +} + +FontEntry::FontStroke::WidthEntry::WidthEntry() : ui::IntEntry(0, 100, this) +{ +} + +void FontEntry::FontStroke::WidthEntry::onValueChange() +{ + ui::IntEntry::onValueChange(); + ValueChange(); +} + +bool FontEntry::FontStroke::WidthEntry::onAcceptUnicodeChar(int unicodeChar) +{ + return (IntEntry::onAcceptUnicodeChar(unicodeChar) || unicodeChar == '.'); +} + +std::string FontEntry::FontStroke::WidthEntry::onGetTextFromValue(int value) +{ + return fmt::format("{:.1f}", value / 10.0); +} + +int FontEntry::FontStroke::WidthEntry::onGetValueFromText(const std::string& text) +{ + return int(10.0 * base::convert_to(text)); +} + +FontEntry::FontEntry() : m_style(&m_tooltips), m_stroke(&m_tooltips) { m_face.setExpansive(true); m_size.setExpansive(false); m_style.setExpansive(false); + + addChild(&m_tooltips); addChild(&m_face); addChild(&m_size); addChild(&m_style); + addChild(&m_stroke); + + m_tooltips.addTooltipFor(&m_face, Strings::text_tool_font_family(), BOTTOM); + m_tooltips.addTooltipFor(m_size.getEntryWidget(), Strings::text_tool_font_size(), BOTTOM); m_face.setMinSize(gfx::Size(128 * guiscale(), 0)); @@ -299,6 +367,7 @@ FontEntry::FontEntry() }); m_style.ItemChange.connect(&FontEntry::onStyleItemClick, this); + m_stroke.Change.connect(&FontEntry::onStrokeChange, this); } // Defined here as FontPopup type is not fully defined in the header @@ -327,6 +396,28 @@ void FontEntry::setInfo(const FontInfo& info, const From fromField) FontChange(m_info, fromField); } +ui::Paint FontEntry::paint() +{ + ui::Paint paint; + const float stroke = m_stroke.stroke(); + + ui::Paint::Style style = ui::Paint::StrokeAndFill; + if (m_stroke.fill()) { + if (stroke <= 0.0f) + style = ui::Paint::Fill; + else + paint.strokeWidth(stroke); + } + else { + style = ui::Paint::Stroke; + paint.strokeWidth(stroke); + } + + paint.style(style); + + return paint; +} + void FontEntry::onStyleItemClick(ButtonSet::Item* item) { text::FontStyle style = m_info.style(); @@ -404,4 +495,9 @@ void FontEntry::onStyleItemClick(ButtonSet::Item* item) } } +void FontEntry::onStrokeChange() +{ + FontChange(m_info, From::Paint); +} + } // namespace app diff --git a/src/app/ui/font_entry.h b/src/app/ui/font_entry.h index 570accdec..224c88936 100644 --- a/src/app/ui/font_entry.h +++ b/src/app/ui/font_entry.h @@ -14,6 +14,9 @@ #include "ui/box.h" #include "ui/button.h" #include "ui/combobox.h" +#include "ui/int_entry.h" +#include "ui/paint.h" +#include "ui/tooltips.h" #include @@ -30,6 +33,7 @@ public: Flags, Hinting, Popup, + Paint, }; FontEntry(); @@ -38,10 +42,13 @@ public: FontInfo info() { return m_info; } void setInfo(const FontInfo& info, From from); + ui::Paint paint(); + obs::signal FontChange; private: void onStyleItemClick(ButtonSet::Item* item); + void onStrokeChange(); class FontFace : public SearchEntry { public: @@ -73,13 +80,40 @@ private: class FontStyle : public ButtonSet { public: - FontStyle(); + FontStyle(ui::TooltipManager* tooltips); }; + class FontStroke : public HBox { + public: + FontStroke(ui::TooltipManager* tooltips); + bool fill() const; + float stroke() const; + obs::signal Change; + + private: + class WidthEntry : public ui::IntEntry, + public ui::SliderDelegate { + public: + WidthEntry(); + obs::signal ValueChange; + + private: + void onValueChange() override; + bool onAcceptUnicodeChar(int unicodeChar) override; + // SliderDelegate impl + std::string onGetTextFromValue(int value) override; + int onGetValueFromText(const std::string& text) override; + }; + ButtonSet m_fill; + WidthEntry m_stroke; + }; + + ui::TooltipManager m_tooltips; FontInfo m_info; FontFace m_face; FontSize m_size; FontStyle m_style; + FontStroke m_stroke; bool m_lockFace = false; }; diff --git a/src/app/ui/font_popup.cpp b/src/app/ui/font_popup.cpp index 8f1ba6812..891b5bb80 100644 --- a/src/app/ui/font_popup.cpp +++ b/src/app/ui/font_popup.cpp @@ -194,7 +194,11 @@ private: if (!blob) return; - doc::ImageRef image = render_text_blob(blob, gfx::rgba(0, 0, 0)); + ui::Paint paint; + paint.color(gfx::rgba(0, 0, 0)); + paint.style(ui::Paint::Fill); + const gfx::RectF textBounds = get_text_blob_required_bounds(blob); + doc::ImageRef image = render_text_blob(blob, textBounds, paint); if (!image) return; diff --git a/src/app/util/render_text.cpp b/src/app/util/render_text.cpp index fc68d9909..29418f3cb 100644 --- a/src/app/util/render_text.cpp +++ b/src/app/util/render_text.cpp @@ -60,7 +60,7 @@ private: } // anonymous namespace -gfx::Size get_text_blob_required_size(const text::TextBlobRef& blob) +gfx::RectF get_text_blob_required_bounds(const text::TextBlobRef& blob) { ASSERT(blob != nullptr); @@ -74,32 +74,28 @@ gfx::Size get_text_blob_required_size(const text::TextBlobRef& blob) bounds.w = 1; if (bounds.h < 1) bounds.h = 1; - return gfx::Size(std::ceil(bounds.w), std::ceil(bounds.h)); + return bounds; } -doc::ImageRef render_text_blob(const text::TextBlobRef& blob, gfx::Color color) +doc::ImageRef render_text_blob(const text::TextBlobRef& blob, + const gfx::RectF& textBounds, + const ui::Paint& paint) { ASSERT(blob != nullptr); - os::Paint paint; - // TODO offer Stroke, StrokeAndFill, and Fill styles - paint.style(os::Paint::Fill); - paint.color(color); - - gfx::Size blobSize = get_text_blob_required_size(blob); - - doc::ImageRef image(doc::Image::create(doc::IMAGE_RGB, blobSize.w, blobSize.h)); + doc::ImageRef image( + doc::Image::create(doc::IMAGE_RGB, std::ceil(textBounds.w), std::ceil(textBounds.h))); #ifdef LAF_SKIA sk_sp skSurface = wrap_docimage_in_sksurface(image.get()); os::SurfaceRef surface = base::make_ref(skSurface); - text::draw_text(surface.get(), blob, gfx::PointF(0, 0), &paint); + text::draw_text(surface.get(), blob, -textBounds.origin(), &paint); #endif // LAF_SKIA return image; } -doc::ImageRef render_text(const FontInfo& fontInfo, const std::string& text, gfx::Color color) +doc::ImageRef render_text(const FontInfo& fontInfo, const std::string& text, const ui::Paint& paint) { Fonts* fonts = Fonts::instance(); ASSERT(fonts); @@ -113,10 +109,6 @@ doc::ImageRef render_text(const FontInfo& fontInfo, const std::string& text, gfx const text::FontMgrRef fontMgr = fonts->fontMgr(); ASSERT(fontMgr); - os::Paint paint; - paint.style(os::Paint::StrokeAndFill); - paint.color(color); - // We have to measure all text runs which might use different // fonts (e.g. if the given font is not enough to shape other code // points/languages). diff --git a/src/app/util/render_text.h b/src/app/util/render_text.h index c96f6dbdc..8e2c1b9da 100644 --- a/src/app/util/render_text.h +++ b/src/app/util/render_text.h @@ -11,25 +11,28 @@ #include "doc/image_ref.h" #include "gfx/color.h" +#include "gfx/rect.h" #include "text/text_blob.h" +#include "ui/paint.h" #include namespace app { -class Color; class FontInfo; -namespace skin { -class SkinTheme; -} -// Returns the exact bounds that are required to draw this TextBlob, -// i.e. the image size that will be required in render_text_blob(). -gfx::Size get_text_blob_required_size(const text::TextBlobRef& blob); +// Returns the exact bounds that are required to draw this TextBlob in +// the origin point (0, 0), i.e. the image size that will be required +// in render_text_blob(). +gfx::RectF get_text_blob_required_bounds(const text::TextBlobRef& blob); -doc::ImageRef render_text_blob(const text::TextBlobRef& blob, gfx::Color color); +doc::ImageRef render_text_blob(const text::TextBlobRef& blob, + const gfx::RectF& textBounds, + const ui::Paint& paint); -doc::ImageRef render_text(const FontInfo& fontInfo, const std::string& text, gfx::Color color); +doc::ImageRef render_text(const FontInfo& fontInfo, + const std::string& text, + const ui::Paint& paint); } // namespace app diff --git a/src/ui/int_entry.cpp b/src/ui/int_entry.cpp index 72429d412..90d0d53d2 100644 --- a/src/ui/int_entry.cpp +++ b/src/ui/int_entry.cpp @@ -115,8 +115,8 @@ bool IntEntry::onProcessMessage(Message* msg) case kKeyDownMessage: if (hasFocus() && !isReadOnly()) { KeyMessage* keymsg = static_cast(msg); - int chr = keymsg->unicodeChar(); - if (chr >= 32 && (chr < '0' || chr > '9')) { + const int chr = keymsg->unicodeChar(); + if (chr >= 32 && !onAcceptUnicodeChar(chr)) { // "Eat" all keys that aren't number return true; } @@ -166,6 +166,11 @@ void IntEntry::onValueChange() // Do nothing } +bool IntEntry::onAcceptUnicodeChar(const int unicodeChar) +{ + return (unicodeChar >= '0' && unicodeChar <= '9'); +} + void IntEntry::openPopup() { m_slider->setValue(getValue()); diff --git a/src/ui/int_entry.h b/src/ui/int_entry.h index 4d3d2f8d8..21734a593 100644 --- a/src/ui/int_entry.h +++ b/src/ui/int_entry.h @@ -1,5 +1,5 @@ // Aseprite UI Library -// Copyright (C) 2022 Igara Studio S.A. +// Copyright (C) 2022-2025 Igara Studio S.A. // Copyright (C) 2001-2017 David Capello // // This file is released under the terms of the MIT license. @@ -36,6 +36,7 @@ protected: // New events virtual void onValueChange(); + virtual bool onAcceptUnicodeChar(int unicodeChar); int m_min; int m_max;