mirror of https://github.com/aseprite/aseprite.git
Add options to stroke/fill text (fix #5271)
build-auto / build-auto (Debug, macos-latest) (push) Has been cancelled
Details
build-auto / build-auto (Debug, ubuntu-latest) (push) Has been cancelled
Details
build-auto / build-auto (Debug, windows-latest) (push) Has been cancelled
Details
build-auto / build-auto (RelWithDebInfo, macos-latest) (push) Has been cancelled
Details
build-auto / build-auto (RelWithDebInfo, ubuntu-latest) (push) Has been cancelled
Details
build-auto / build-auto (RelWithDebInfo, windows-latest) (push) Has been cancelled
Details
build / build (Debug, macos-latest, lua, cli) (push) Has been cancelled
Details
build / build (Debug, macos-latest, noscripts, cli) (push) Has been cancelled
Details
build / build (Debug, ubuntu-latest, lua, cli) (push) Has been cancelled
Details
build / build (Debug, ubuntu-latest, noscripts, cli) (push) Has been cancelled
Details
build / build (Debug, windows-latest, lua, cli) (push) Has been cancelled
Details
build / build (Debug, windows-latest, noscripts, cli) (push) Has been cancelled
Details
build / build (RelWithDebInfo, macos-latest, lua, gui) (push) Has been cancelled
Details
build / build (RelWithDebInfo, ubuntu-latest, lua, gui) (push) Has been cancelled
Details
build / build (RelWithDebInfo, windows-latest, lua, gui) (push) Has been cancelled
Details
build-auto / build-auto (Debug, macos-latest) (push) Has been cancelled
Details
build-auto / build-auto (Debug, ubuntu-latest) (push) Has been cancelled
Details
build-auto / build-auto (Debug, windows-latest) (push) Has been cancelled
Details
build-auto / build-auto (RelWithDebInfo, macos-latest) (push) Has been cancelled
Details
build-auto / build-auto (RelWithDebInfo, ubuntu-latest) (push) Has been cancelled
Details
build-auto / build-auto (RelWithDebInfo, windows-latest) (push) Has been cancelled
Details
build / build (Debug, macos-latest, lua, cli) (push) Has been cancelled
Details
build / build (Debug, macos-latest, noscripts, cli) (push) Has been cancelled
Details
build / build (Debug, ubuntu-latest, lua, cli) (push) Has been cancelled
Details
build / build (Debug, ubuntu-latest, noscripts, cli) (push) Has been cancelled
Details
build / build (Debug, windows-latest, lua, cli) (push) Has been cancelled
Details
build / build (Debug, windows-latest, noscripts, cli) (push) Has been cancelled
Details
build / build (RelWithDebInfo, macos-latest, lua, gui) (push) Has been cancelled
Details
build / build (RelWithDebInfo, ubuntu-latest, lua, gui) (push) Has been cancelled
Details
build / build (RelWithDebInfo, windows-latest, lua, gui) (push) Has been cancelled
Details
This commit is contained in:
parent
11a7b061ff
commit
5c4daff128
|
@ -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
|
||||
|
|
2
laf
2
laf
|
@ -1 +1 @@
|
|||
Subproject commit 375629b7bda67f57988804a12e78c55afe005c9d
|
||||
Subproject commit a2bb9ec7fb98354279a2c49870a4a47a67a8e86e
|
|
@ -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()) {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 <cmath>
|
||||
|
||||
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<void(const gfx::Size&)> NewRequiredBounds;
|
||||
obs::signal<void(const gfx::RectF&)> 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> skSurface = wrap_docimage_in_sksurface(blobImage.get());
|
||||
os::SurfaceRef surface = base::make_ref<os::SkiaSurface>(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> skSurface = wrap_docimage_in_sksurface(image.get());
|
||||
os::SurfaceRef surface = base::make_ref<os::SkiaSurface>(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)
|
||||
|
|
|
@ -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<TextEditor> 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;
|
||||
|
|
|
@ -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<FontStroke*>(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<double>(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
|
||||
|
|
|
@ -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 <string>
|
||||
|
||||
|
@ -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<void(const FontInfo&, From)> 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<void()> Change;
|
||||
|
||||
private:
|
||||
class WidthEntry : public ui::IntEntry,
|
||||
public ui::SliderDelegate {
|
||||
public:
|
||||
WidthEntry();
|
||||
obs::signal<void()> 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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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> skSurface = wrap_docimage_in_sksurface(image.get());
|
||||
os::SurfaceRef surface = base::make_ref<os::SkiaSurface>(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).
|
||||
|
|
|
@ -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 <string>
|
||||
|
||||
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
|
||||
|
||||
|
|
|
@ -115,8 +115,8 @@ bool IntEntry::onProcessMessage(Message* msg)
|
|||
case kKeyDownMessage:
|
||||
if (hasFocus() && !isReadOnly()) {
|
||||
KeyMessage* keymsg = static_cast<KeyMessage*>(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());
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue