diff --git a/README.md b/README.md index ed380d63c..3f1980b4c 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ features are: * Real-time **animation preview**. * [**Multiple editors**](http://www.aseprite.org/docs/workspace/#drag-and-drop-tabs) support. * Pixel-art specific tools like filled **Contour**, **Polygon**, [**Shading**](http://www.aseprite.org/docs/shading/) mode, etc. -* **Onion skinning** +* [**Onion skinning**](https://www.aseprite.org/docs/animation/#onion-skinning) ## Issues diff --git a/src/app/commands/cmd_keyboard_shortcuts.cpp b/src/app/commands/cmd_keyboard_shortcuts.cpp index a6bea3fea..59fe1ef8d 100644 --- a/src/app/commands/cmd_keyboard_shortcuts.cpp +++ b/src/app/commands/cmd_keyboard_shortcuts.cpp @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2001-2016 David Capello +// Copyright (C) 2001-2017 David Capello // // This program is distributed under the terms of // the End-User License Agreement for Aseprite. @@ -211,7 +211,7 @@ private: g->fillRect(bg, bounds); bounds.shrink(border()); - g->drawUIString(text(), fg, bg, + g->drawUIText(text(), fg, bg, gfx::Point( bounds.x + m_level*16 * guiscale(), bounds.y + 2*guiscale())); @@ -224,7 +224,7 @@ private: for (const Accelerator& accel : m_key->accels()) { if (i != m_hotAccel || !m_changeButton) { - g->drawString(accel.toString(), fg, bg, + g->drawText(accel.toString(), fg, bg, gfx::Point(bounds.x + g_sep, y + 2*guiscale())); } @@ -258,7 +258,7 @@ private: int maxi = (accels && accels->size() > 1 ? accels->size(): 1); for (int i=0; isize() ? (*accels)[i].toString().c_str(): ""), font()); gfx::Rect itemBounds(bounds.x + g_sep, y, w, dh); @@ -295,7 +295,7 @@ private: m_deleteButton->setBounds(gfx::Rect( itemBounds.x + itemBounds.w + 2*guiscale(), itemBounds.y, - Graphics::measureUIStringLength( + Graphics::measureUITextLength( label, font()) + 4*guiscale(), itemBounds.h)); m_deleteButton->setText(label); @@ -312,7 +312,7 @@ private: setup_mini_look(m_addButton.get()); addChild(m_addButton.get()); - itemBounds.w = 8*guiscale() + Graphics::measureUIStringLength("Add", font()); + itemBounds.w = 8*guiscale() + Graphics::measureUITextLength("Add", font()); itemBounds.x -= itemBounds.w + 2*guiscale(); m_addButton->setBgColor(gfx::ColorNone); diff --git a/src/app/ui/app_menuitem.cpp b/src/app/ui/app_menuitem.cpp index fce9c6c6a..8e759f1c4 100644 --- a/src/app/ui/app_menuitem.cpp +++ b/src/app/ui/app_menuitem.cpp @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2001-2015 David Capello +// Copyright (C) 2001-2017 David Capello // // This program is distributed under the terms of // the End-User License Agreement for Aseprite. @@ -99,7 +99,7 @@ void AppMenuItem::onSizeHint(SizeHintEvent& ev) + border().height(); if (m_key && !m_key->accels().empty()) { - size.w += Graphics::measureUIStringLength( + size.w += Graphics::measureUITextLength( m_key->accels().front().toString().c_str(), font()); } } diff --git a/src/app/ui/button_set.cpp b/src/app/ui/button_set.cpp index 906e7914b..652f624c1 100644 --- a/src/app/ui/button_set.cpp +++ b/src/app/ui/button_set.cpp @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2001-2016 David Capello +// Copyright (C) 2001-2017 David Capello // // This program is distributed under the terms of // the End-User License Agreement for Aseprite. @@ -141,8 +141,8 @@ void ButtonSet::Item::onPaint(ui::PaintEvent& ev) if (hasText()) { g->setFont(font()); - g->drawUIString(text(), fg, gfx::ColorNone, textRc.origin(), - false); + g->drawUIText(text(), fg, gfx::ColorNone, textRc.origin(), + false); } } diff --git a/src/app/ui/color_button.cpp b/src/app/ui/color_button.cpp index ec49c7376..55edceb30 100644 --- a/src/app/ui/color_button.cpp +++ b/src/app/ui/color_button.cpp @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2001-2016 David Capello +// Copyright (C) 2001-2017 David Capello // // This program is distributed under the terms of // the End-User License Agreement for Aseprite. @@ -226,7 +226,7 @@ void ColorButton::onPaint(PaintEvent& ev) gfx::Rect textrc; getTextIconInfo(NULL, &textrc); - g->drawUIString(text(), textcolor, gfx::ColorNone, textrc.origin()); + g->drawUIText(text(), textcolor, gfx::ColorNone, textrc.origin()); } void ColorButton::onClick(Event& ev) diff --git a/src/app/ui/context_bar.cpp b/src/app/ui/context_bar.cpp index 0ba6e1834..0e67c6982 100644 --- a/src/app/ui/context_bar.cpp +++ b/src/app/ui/context_bar.cpp @@ -715,8 +715,8 @@ class ContextBar::InkShadesField : public HBox { } else { g->fillRect(theme->colors.editorFace(), bounds); - g->drawAlignedUIString(text(), theme->colors.face(), gfx::ColorNone, bounds, - ui::CENTER | ui::MIDDLE); + g->drawAlignedUIText(text(), theme->colors.face(), gfx::ColorNone, bounds, + ui::CENTER | ui::MIDDLE); } } diff --git a/src/app/ui/editor/editor.cpp b/src/app/ui/editor/editor.cpp index d91b9ba9a..fdd21a1a0 100644 --- a/src/app/ui/editor/editor.cpp +++ b/src/app/ui/editor/editor.cpp @@ -1521,7 +1521,7 @@ void Editor::onPaint(ui::PaintEvent& ev) gfx::Rect vp = view->viewportBounds(); char buf[128]; sprintf(buf, "%.3f", renderElapsed); - g->drawString( + g->drawText( buf, gfx::rgba(255, 255, 255, 255), gfx::rgba(0, 0, 0, 255), diff --git a/src/app/ui/file_list.cpp b/src/app/ui/file_list.cpp index 089d6c40c..da8fe42b8 100644 --- a/src/app/ui/file_list.cpp +++ b/src/app/ui/file_list.cpp @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2001-2016 David Capello +// Copyright (C) 2001-2017 David Capello // // This program is distributed under the terms of // the End-User License Agreement for Aseprite. @@ -362,12 +362,12 @@ void FileList::onPaint(ui::PaintEvent& ev) if (fi->isFolder()) { int icon_w = font()->textLength("[+]"); - g->drawString("[+]", fgcolor, bgcolor, gfx::Point(x, y+2*guiscale())); + g->drawText("[+]", fgcolor, bgcolor, gfx::Point(x, y+2*guiscale())); x += icon_w+2*guiscale(); } // item name - g->drawString( + g->drawText( fi->displayName().c_str(), fgcolor, bgcolor, gfx::Point(x, y+2*guiscale())); diff --git a/src/app/ui/palette_view.cpp b/src/app/ui/palette_view.cpp index 5fa18f290..461be1c46 100644 --- a/src/app/ui/palette_view.cpp +++ b/src/app/ui/palette_view.cpp @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2001-2016 David Capello +// Copyright (C) 2001-2017 David Capello // // This program is distributed under the terms of // the End-User License Agreement for Aseprite. @@ -546,9 +546,9 @@ void PaletteView::onPaint(ui::PaintEvent& ev) she::Font* minifont = theme->getMiniFont(); std::string text = base::convert_to(k); g->setFont(minifont); - g->drawString(text, neg, gfx::ColorNone, - gfx::Point(box2.x + box2.w/2 - minifont->textLength(text)/2, - box2.y + box2.h/2 - minifont->height()/2)); + g->drawText(text, neg, gfx::ColorNone, + gfx::Point(box2.x + box2.w/2 - minifont->textLength(text)/2, + box2.y + box2.h/2 - minifont->height()/2)); } // Draw outlines diff --git a/src/app/ui/resources_listbox.cpp b/src/app/ui/resources_listbox.cpp index b80105f5c..223b9de56 100644 --- a/src/app/ui/resources_listbox.cpp +++ b/src/app/ui/resources_listbox.cpp @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2001-2016 David Capello +// Copyright (C) 2001-2017 David Capello // // This program is distributed under the terms of // the End-User License Agreement for Aseprite. @@ -65,10 +65,10 @@ void ResourceListItem::onPaint(PaintEvent& ev) static_cast(parent())-> paintResource(g, bounds, m_resource); - g->drawString(text(), fgcolor, gfx::ColorNone, - gfx::Point( - bounds.x + 2*guiscale(), - bounds.y + bounds.h/2 - g->measureUIString(text()).h/2)); + g->drawText(text(), fgcolor, gfx::ColorNone, + gfx::Point( + bounds.x + 2*guiscale(), + bounds.y + bounds.h/2 - g->measureUIText(text()).h/2)); } void ResourceListItem::onSizeHint(SizeHintEvent& ev) diff --git a/src/app/ui/skin/skin_theme.cpp b/src/app/ui/skin/skin_theme.cpp index f61946cc6..502c96181 100644 --- a/src/app/ui/skin/skin_theme.cpp +++ b/src/app/ui/skin/skin_theme.cpp @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2001-2016 David Capello +// Copyright (C) 2001-2017 David Capello // // This program is distributed under the terms of // the End-User License Agreement for Aseprite. @@ -32,6 +32,7 @@ #include "gfx/point.h" #include "gfx/rect.h" #include "gfx/size.h" +#include "she/draw_text.h" #include "she/font.h" #include "she/surface.h" #include "she/system.h" @@ -769,6 +770,14 @@ int SkinTheme::getScrollbarSize() return dimensions.scrollbarSize(); } +gfx::Size SkinTheme::getEntryCaretSize(Widget* widget) +{ + if (widget->font()->type() == she::FontType::kTrueType) + return gfx::Size(2*guiscale(), widget->textHeight()); + else + return gfx::Size(2*guiscale(), widget->textHeight()+2*guiscale()); +} + void SkinTheme::paintDesktop(PaintEvent& ev) { Graphics* g = ev.graphics(); @@ -850,8 +859,8 @@ void SkinTheme::paintButton(PaintEvent& ev) drawRect(g, widget->clientBounds(), part_nw.get(), bg); // text - drawTextString(g, NULL, fg, ColorNone, widget, - widget->clientChildrenBounds(), get_button_selected_offset()); + drawText(g, NULL, fg, ColorNone, widget, + widget->clientChildrenBounds(), get_button_selected_offset()); // Paint the icon if (iconInterface) { @@ -895,7 +904,7 @@ void SkinTheme::paintCheckBox(PaintEvent& ev) } // Text - drawTextString(g, NULL, ColorNone, ColorNone, widget, text, 0); + drawText(g, NULL, ColorNone, ColorNone, widget, text, 0); // Paint the icon if (iconInterface) @@ -924,13 +933,6 @@ void SkinTheme::paintEntry(PaintEvent& ev) Graphics* g = ev.graphics(); Entry* widget = static_cast(ev.getSource()); gfx::Rect bounds = widget->clientBounds(); - bool password = widget->isPassword(); - int scroll, caret, state, selbeg, selend; - const std::string& textString = widget->text(); - int c, ch, x, y, w; - int caret_x; - - widget->getEntryThemeInfo(&scroll, &caret, &state, &selbeg, &selend); // Outside borders g->fillRect(BGCOLOR, bounds); @@ -947,26 +949,49 @@ void SkinTheme::paintEntry(PaintEvent& ev) (isMiniLook ? parts.sunkenMiniNormal().get() : parts.sunkenNormal().get())), bg); - // Draw the text - bounds = widget->getEntryTextBounds(); - x = bounds.x; - y = bounds.y; + drawEntryText(g, widget); +} - base::utf8_const_iterator utf8_it = base::utf8_const_iterator(textString.begin()); - int textlen = base::utf8_length(textString); - if (scroll < textlen) - utf8_it += scroll; +namespace { - for (c=scroll; cbounds().x) + , m_y(pos.y) + , m_h(h) + { + m_widget->getEntryThemeInfo(&m_index, &m_caret, &m_state, &m_selbeg, &m_selend); + } + + int index() const { return m_index; } + bool caretDrawn() const { return m_caretDrawn; } + const gfx::Rect& textBounds() const { return m_textBounds; } + + void preProcessChar(const base::utf8_const_iterator& it, + const base::utf8_const_iterator& end, + int& chr, + gfx::Color& fg, + gfx::Color& bg, + bool& drawChar, + bool& moveCaret) override { + if (m_widget->isPassword()) + chr = '*'; // Normal text + auto& colors = SkinTheme::instance()->colors; bg = ColorNone; - gfx::Color fg = colors.text(); + fg = colors.text(); // Selected - if ((c >= selbeg) && (c <= selend)) { - if (widget->hasFocus()) + if ((m_index >= m_selbeg) && + (m_index <= m_selend)) { + if (m_widget->hasFocus()) bg = colors.selected(); else bg = colors.disabled(); @@ -974,43 +999,112 @@ void SkinTheme::paintEntry(PaintEvent& ev) } // Disabled - if (!widget->isEnabled()) { + if (!m_widget->isEnabled()) { bg = ColorNone; fg = colors.disabled(); } - w = g->measureChar(ch).w; - if (x+w > bounds.x2()) - return; - - caret_x = x; - g->drawChar(ch, fg, bg, x, y); - x += w; - - // Caret - if ((c == caret) && (state) && (widget->hasFocus())) - drawEntryCaret(g, widget, caret_x, y); + drawChar = true; + moveCaret = true; + m_bg = bg; } + bool preDrawChar(const gfx::Rect& charBounds) override { + m_textBounds |= charBounds; + m_charStartX = charBounds.x; + + if (charBounds.x2()-m_widget->bounds().x < m_widget->clientBounds().x2()) { + if (m_bg != ColorNone) { + // Fill background e.g. needed for selected/highlighted + // regions with TTF fonts where the char is smaller than the + // text bounds [m_y,m_y+m_h) + gfx::Rect fillThisRect(m_lastX-m_widget->bounds().x, + m_y, charBounds.x2()-m_lastX, m_h); + if (charBounds != fillThisRect) + m_graphics->fillRect(m_bg, fillThisRect); + } + m_lastX = charBounds.x2(); + return true; + } + else + return false; + } + + void postDrawChar(const gfx::Rect& charBounds) override { + // Caret + if (m_state && + m_index == m_caret && + m_widget->hasFocus() && + m_widget->isEnabled()) { + SkinTheme::instance()->drawEntryCaret( + m_graphics, m_widget, + m_charStartX-m_widget->bounds().x, m_y); + m_caretDrawn = true; + } + + ++m_index; + } + +private: + Entry* m_widget; + Graphics* m_graphics; + int m_index; + int m_caret; + int m_state; + int m_selbeg; + int m_selend; + gfx::Rect m_textBounds; + bool m_caretDrawn; + gfx::Color m_bg; + int m_lastX; // Last position used to fill the background + int m_y, m_h; + int m_charStartX; +}; + +} // anonymous namespace + +void SkinTheme::drawEntryText(ui::Graphics* g, ui::Entry* widget) +{ + // Draw the text + gfx::Rect bounds = widget->getEntryTextBounds(); + + DrawEntryTextDelegate delegate(widget, g, bounds.origin(), widget->textHeight()); + int scroll = delegate.index(); + + const std::string& textString = widget->text(); + base::utf8_const_iterator utf8_it((textString.begin())); + int textlen = base::utf8_length(textString); + scroll = MIN(scroll, textlen); + if (scroll) + utf8_it += scroll; + + g->drawText(utf8_it, + base::utf8_const_iterator(textString.end()), + colors.text(), ColorNone, + bounds.origin(), &delegate); + + bounds.x += delegate.textBounds().w; + // Draw suffix if there is enough space if (!widget->getSuffix().empty()) { - Rect sufBounds(x, y, - bounds.x2()-widget->childSpacing()*guiscale()-x, + Rect sufBounds(bounds.x, bounds.y, + bounds.x2()-widget->childSpacing()*guiscale()-bounds.x, widget->textHeight()); - IntersectClip clip(g, sufBounds); + IntersectClip clip(g, sufBounds & widget->clientChildrenBounds()); if (clip) { - drawTextString( + drawText( g, widget->getSuffix().c_str(), colors.entrySuffix(), ColorNone, widget, sufBounds, 0); } } - // Draw the caret if it is next of the last character - if ((c == caret) && (state) && - (widget->hasFocus()) && - (widget->isEnabled())) { - drawEntryCaret(g, widget, x, y); + // Draw caret at the end of the text + if (!delegate.caretDrawn()) { + gfx::Rect charBounds(bounds.x+widget->bounds().x, + bounds.y+widget->bounds().y, 0, widget->textHeight()); + delegate.preDrawChar(charBounds); + delegate.postDrawChar(charBounds); } } @@ -1092,7 +1186,7 @@ void SkinTheme::paintListItem(ui::PaintEvent& ev) if (widget->hasText()) { bounds.shrink(widget->border()); - drawTextString(g, NULL, fg, bg, widget, bounds, 0); + drawText(g, NULL, fg, bg, widget, bounds, 0); } } @@ -1163,7 +1257,7 @@ void SkinTheme::paintMenuItem(ui::PaintEvent& ev) Rect pos = bounds; if (!bar) pos.offset(widget->childSpacing()/2, 0); - drawTextString(g, NULL, fg, ColorNone, widget, pos, 0); + drawText(g, NULL, fg, ColorNone, widget, pos, 0); // For menu-box if (!bar) { @@ -1200,7 +1294,7 @@ void SkinTheme::paintMenuItem(ui::PaintEvent& ev) std::string buf = appMenuItem->key()->accels().front().toString(); widget->setAlign(RIGHT | MIDDLE); - drawTextString(g, buf.c_str(), fg, ColorNone, widget, pos, 0); + drawText(g, buf.c_str(), fg, ColorNone, widget, pos, 0); widget->setAlign(old_align); } } @@ -1240,7 +1334,7 @@ void SkinTheme::paintRadioButton(PaintEvent& ev) } // Text - drawTextString(g, NULL, ColorNone, ColorNone, widget, text, 0); + drawText(g, NULL, ColorNone, ColorNone, widget, text, 0); // Icon if (iconInterface) @@ -1282,7 +1376,7 @@ void SkinTheme::paintSeparator(ui::PaintEvent& ev) bounds.y + bounds.h/2 - h/2, widget->textWidth(), h); - drawTextString(g, NULL, + drawText(g, NULL, colors.separatorLabel(), BGCOLOR, widget, r, 0); } @@ -1389,7 +1483,7 @@ void SkinTheme::paintSlider(PaintEvent& ev) { IntersectClip clip(g, Rect(rc.x, rc.y, x-rc.x, rc.h)); if (clip) { - drawTextString(g, NULL, + drawText(g, NULL, colors.sliderFullText(), ColorNone, widget, rc, 0); } @@ -1398,7 +1492,7 @@ void SkinTheme::paintSlider(PaintEvent& ev) { IntersectClip clip(g, Rect(x+1, rc.y, rc.w-(x-rc.x+1), rc.h)); if (clip) { - drawTextString(g, NULL, + drawText(g, NULL, colors.sliderEmptyText(), ColorNone, widget, rc, 0); } @@ -1413,75 +1507,18 @@ void SkinTheme::paintComboBoxEntry(ui::PaintEvent& ev) Graphics* g = ev.graphics(); Entry* widget = static_cast(ev.getSource()); gfx::Rect bounds = widget->clientBounds(); - bool password = widget->isPassword(); - int scroll, caret, state, selbeg, selend; - const std::string& textString = widget->text(); - int c, ch, x, y, w; - int caret_x; - - widget->getEntryThemeInfo(&scroll, &caret, &state, &selbeg, &selend); // Outside borders g->fillRect(BGCOLOR, bounds); - gfx::Color fg, bg = colors.background(); + gfx::Color bg = colors.background(); drawRect(g, bounds, (widget->hasFocus() ? parts.sunken2Focused().get(): parts.sunken2Normal().get()), bg); - // Draw the text - x = bounds.x + widget->border().left(); - y = bounds.y + bounds.h/2 - widget->textHeight()/2; - - base::utf8_const_iterator utf8_it = base::utf8_const_iterator(textString.begin()); - int textlen = base::utf8_length(textString); - - if (scroll < textlen) - utf8_it += scroll; - - for (c=scroll; c= selbeg) && (c <= selend)) { - if (widget->hasFocus()) - bg = colors.selected(); - else - bg = colors.disabled(); - fg = colors.background(); - } - - // Disabled - if (!widget->isEnabled()) { - bg = ColorNone; - fg = colors.disabled(); - } - - w = g->measureChar(ch).w; - if (x+w > bounds.x2()-3) - return; - - caret_x = x; - g->drawChar(ch, fg, bg, x, y); - x += w; - - // Caret - if ((c == caret) && (state) && (widget->hasFocus())) - drawEntryCaret(g, widget, caret_x, y); - } - - // Draw the caret if it is next of the last character - if ((c == caret) && (state) && - (widget->hasFocus()) && - (widget->isEnabled())) { - drawEntryCaret(g, widget, x, y); - } + drawEntryText(g, widget); } void SkinTheme::paintComboBoxButton(PaintEvent& ev) @@ -1653,7 +1690,7 @@ void SkinTheme::paintPopupWindow(PaintEvent& ev) pos.shrink(window->border()); - g->drawAlignedUIString(window->text(), + g->drawAlignedUIText(window->text(), colors.text(), window->bgColor(), pos, window->align()); @@ -1749,7 +1786,7 @@ void SkinTheme::paintTooltip(PaintEvent& ev) rc.shrink(widget->border()); - g->drawAlignedUIString(widget->text(), fg, bg, rc, widget->align()); + g->drawAlignedUIText(widget->text(), fg, bg, rc, widget->align()); } gfx::Color SkinTheme::getWidgetBgColor(Widget* widget) @@ -1765,9 +1802,9 @@ gfx::Color SkinTheme::getWidgetBgColor(Widget* widget) return colors.face(); } -void SkinTheme::drawTextString(Graphics* g, const char *t, gfx::Color fg_color, gfx::Color bg_color, - Widget* widget, const Rect& rc, - int selected_offset) +void SkinTheme::drawText(Graphics* g, const char *t, gfx::Color fg_color, gfx::Color bg_color, + Widget* widget, const Rect& rc, + int selected_offset) { if (t || widget->hasText()) { Rect textrc; @@ -1777,7 +1814,7 @@ void SkinTheme::drawTextString(Graphics* g, const char *t, gfx::Color fg_color, if (!t) t = widget->text().c_str(); - textrc.setSize(g->measureUIString(t)); + textrc.setSize(g->measureUIText(t)); // Horizontally text alignment @@ -1820,13 +1857,13 @@ void SkinTheme::drawTextString(Graphics* g, const char *t, gfx::Color fg_color, if (clip) { if (!widget->isEnabled()) { // Draw white part - g->drawUIString(t, + g->drawUIText(t, colors.background(), gfx::ColorNone, textrc.origin() + Point(guiscale(), guiscale())); } - g->drawUIString(t, + g->drawUIText(t, (!widget->isEnabled() ? colors.disabled(): (gfx::geta(fg_color) > 0 ? fg_color : @@ -1839,10 +1876,11 @@ void SkinTheme::drawTextString(Graphics* g, const char *t, gfx::Color fg_color, void SkinTheme::drawEntryCaret(ui::Graphics* g, Entry* widget, int x, int y) { gfx::Color color = colors.text(); - int h = widget->textHeight(); + int textHeight = widget->textHeight(); + gfx::Size caretSize = getEntryCaretSize(widget); - for (int u=x; udrawVLine(color, u, y-1, h+2); + for (int u=x; udrawVLine(color, u, y+textHeight/2-caretSize.h/2, caretSize.h); } she::Surface* SkinTheme::getToolIcon(const char* toolId) const diff --git a/src/app/ui/skin/skin_theme.h b/src/app/ui/skin/skin_theme.h index 2507e18a6..a3c57dd80 100644 --- a/src/app/ui/skin/skin_theme.h +++ b/src/app/ui/skin/skin_theme.h @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2001-2016 David Capello +// Copyright (C) 2001-2017 David Capello // // This program is distributed under the terms of // the End-User License Agreement for Aseprite. @@ -54,6 +54,7 @@ namespace app { void getWindowMask(ui::Widget* widget, gfx::Region& region) override; void setDecorativeWidgetBounds(ui::Widget* widget) override; int getScrollbarSize() override; + gfx::Size getEntryCaretSize(ui::Widget* widget) override; void paintDesktop(ui::PaintEvent& ev) override; void paintBox(ui::PaintEvent& ev) override; @@ -116,6 +117,8 @@ namespace app { return m_colors_by_id[id]; } + void drawEntryCaret(ui::Graphics* g, ui::Entry* widget, int x, int y); + protected: void onRegenerate() override; @@ -127,10 +130,10 @@ namespace app { she::Surface* sliceSheet(she::Surface* sur, const gfx::Rect& bounds); gfx::Color getWidgetBgColor(ui::Widget* widget); - void drawTextString(ui::Graphics* g, const char *t, gfx::Color fg_color, gfx::Color bg_color, - ui::Widget* widget, const gfx::Rect& rc, - int selected_offset); - void drawEntryCaret(ui::Graphics* g, ui::Entry* widget, int x, int y); + void drawText(ui::Graphics* g, const char *t, gfx::Color fg_color, gfx::Color bg_color, + ui::Widget* widget, const gfx::Rect& rc, + int selected_offset); + void drawEntryText(ui::Graphics* g, ui::Entry* widget); void paintIcon(ui::Widget* widget, ui::Graphics* g, ui::IButtonIcon* iconInterface, int x, int y); diff --git a/src/app/ui/skin/style.cpp b/src/app/ui/skin/style.cpp index 64147afd8..3ae71d357 100644 --- a/src/app/ui/skin/style.cpp +++ b/src/app/ui/skin/style.cpp @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2001-2016 David Capello +// Copyright (C) 2001-2017 David Capello // // This program is distributed under the terms of // the End-User License Agreement for Aseprite. @@ -79,7 +79,7 @@ void TextRule::onPaint(ui::Graphics* g, const gfx::Rect& bounds, const char* tex SkinTheme* theme = static_cast(ui::CurrentTheme::get()); if (text) { - g->drawAlignedUIString(text, + g->drawAlignedUIText(text, (gfx::is_transparent(m_color) ? theme->colors.text(): m_color), diff --git a/src/app/ui/status_bar.cpp b/src/app/ui/status_bar.cpp index eb81b26f7..7db1b1527 100644 --- a/src/app/ui/status_bar.cpp +++ b/src/app/ui/status_bar.cpp @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2001-2016 David Capello +// Copyright (C) 2001-2017 David Capello // // This program is distributed under the terms of // the End-User License Agreement for Aseprite. @@ -101,8 +101,8 @@ class StatusBar::Indicators : public HBox { g->fillRect(bgColor(), rc); if (textLength() > 0) { - g->drawString(text(), textColor, ColorNone, - Point(rc.x, rc.y + rc.h/2 - font()->height()/2)); + g->drawText(text(), textColor, ColorNone, + Point(rc.x, rc.y + rc.h/2 - font()->height()/2)); } } }; diff --git a/src/app/ui/timeline.cpp b/src/app/ui/timeline.cpp index 59564eeda..0c643a9c9 100644 --- a/src/app/ui/timeline.cpp +++ b/src/app/ui/timeline.cpp @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2001-2016 David Capello +// Copyright (C) 2001-2017 David Capello // // This program is distributed under the terms of // the End-User License Agreement for Aseprite. @@ -2032,7 +2032,7 @@ void Timeline::drawFrameTags(ui::Graphics* g) bounds.y += 2*ui::guiscale(); bounds.x += 2*ui::guiscale(); - g->drawString( + g->drawText( frameTag->name(), color_utils::blackandwhite_neg(bg), gfx::ColorNone, diff --git a/src/app/util/freetype_utils.cpp b/src/app/util/freetype_utils.cpp index 3b7a5b486..142c2cfb7 100644 --- a/src/app/util/freetype_utils.cpp +++ b/src/app/util/freetype_utils.cpp @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2001-2016 David Capello +// Copyright (C) 2001-2017 David Capello // // This program is distributed under the terms of // the End-User License Agreement for Aseprite. @@ -38,54 +38,61 @@ doc::Image* render_text(const std::string& fontfile, int fontsize, face.setAntialias(antialias); // Calculate text size - gfx::Rect bounds = face.calcTextBounds(text); + gfx::Rect bounds = ft::calc_text_bounds(face, text); // Render the image and copy it to the clipboard if (!bounds.isEmpty()) { image.reset(doc::Image::create(doc::IMAGE_RGB, bounds.w, bounds.h)); doc::clear_image(image, 0); - face.forEachGlyph( - text, - [&bounds, &image, color, antialias](const ft::Glyph& glyph) { - int t, yimg = - bounds.y + int(glyph.y); + ft::ForEachGlyph feg(face); + auto it = base::utf8_const_iterator(text.begin()); + auto end = base::utf8_const_iterator(text.end()); + while (it != end) { + feg.processChar( + *it, + [&bounds, &image, color, antialias](const ft::Glyph& glyph) { + int t, yimg = - bounds.y + int(glyph.y); - for (int v=0; vrows); ++v, ++yimg) { - const uint8_t* p = glyph.bitmap->buffer + v*glyph.bitmap->pitch; - int ximg = - bounds.x + int(glyph.x); - int bit = 0; + for (int v=0; vrows); ++v, ++yimg) { + const uint8_t* p = glyph.bitmap->buffer + v*glyph.bitmap->pitch; + int ximg = - bounds.x + int(glyph.x); + int bit = 0; - for (int u=0; uwidth); ++u, ++ximg) { - int alpha; + for (int u=0; uwidth); ++u, ++ximg) { + int alpha; - if (antialias) { - alpha = *(p++); - } - else { - alpha = ((*p) & (1 << (7 - (bit++))) ? 255: 0); - if (bit == 8) { - bit = 0; - ++p; + if (antialias) { + alpha = *(p++); + } + else { + alpha = ((*p) & (1 << (7 - (bit++))) ? 255: 0); + if (bit == 8) { + bit = 0; + ++p; + } + } + + int output_alpha = MUL_UN8(doc::rgba_geta(color), alpha, t); + if (output_alpha) { + doc::color_t output_color = + doc::rgba(doc::rgba_getr(color), + doc::rgba_getg(color), + doc::rgba_getb(color), + output_alpha); + + doc::put_pixel( + image, ximg, yimg, + doc::rgba_blender_normal( + doc::get_pixel(image, ximg, yimg), + output_color)); } } - - int output_alpha = MUL_UN8(doc::rgba_geta(color), alpha, t); - if (output_alpha) { - doc::color_t output_color = - doc::rgba(doc::rgba_getr(color), - doc::rgba_getg(color), - doc::rgba_getb(color), - output_alpha); - - doc::put_pixel( - image, ximg, yimg, - doc::rgba_blender_normal( - doc::get_pixel(image, ximg, yimg), - output_color)); - } } - } - }); + }); + + ++it; + } } else { throw std::runtime_error("There is no text"); diff --git a/src/ft/face.h b/src/ft/face.h index 4436aa5d8..0d7c5cbbc 100644 --- a/src/ft/face.h +++ b/src/ft/face.h @@ -1,5 +1,5 @@ // Aseprite FreeType Wrapper -// Copyright (c) 2016 David Capello +// Copyright (c) 2016-2017 David Capello // // This file is released under the terms of the MIT license. // Read LICENSE.txt for more information. @@ -22,6 +22,8 @@ namespace ft { FT_UInt glyph_index; FT_Glyph ft_glyph; FT_Bitmap* bitmap; + double startX; + double endX; double bearingX; double bearingY; double x; @@ -29,12 +31,12 @@ namespace ft { }; template - class FaceBase { + class FaceFT { public: - FaceBase(FT_Face face) : m_face(face) { + FaceFT(FT_Face face) : m_face(face) { } - ~FaceBase() { + ~FaceFT() { if (m_face) FT_Done_Face(m_face); } @@ -81,81 +83,89 @@ namespace ft { return int(m_face->descender * y_scale); } + Cache& cache() { return m_cache; } + protected: FT_Face m_face; bool m_antialias; Cache m_cache; private: - DISABLE_COPYING(FaceBase); + DISABLE_COPYING(FaceFT); }; - template - class FaceFT : public FaceBase { + template + class ForEachGlyph { public: - using FaceBase::m_face; - using FaceBase::m_cache; - - FaceFT(FT_Face face) - : FaceBase(face) { + ForEachGlyph(FaceFT& face) + : m_face(face) + , m_useKerning(FT_HAS_KERNING((FT_Face)face) ? true: false) + , m_prevGlyph(0) + , m_x(0.0), m_y(0.0) + { } - template - void forEachGlyph(const std::string& str, Callback callback) { - bool use_kerning = (FT_HAS_KERNING(this->m_face) ? true: false); - FT_UInt prev_glyph = 0; - double x = 0, y = 0; + template + void processChar(const int chr, ProcessGlyph processGlyph) { + FT_UInt glyphIndex = m_face.cache().getGlyphIndex(m_face, chr); + double initialX = m_x; - auto it = base::utf8_const_iterator(str.begin()); - auto end = base::utf8_const_iterator(str.end()); - for (; it != end; ++it) { - FT_UInt glyph_index = this->m_cache.getGlyphIndex( - this->m_face, *it); - - if (use_kerning && prev_glyph && glyph_index) { - FT_Vector kerning; - FT_Get_Kerning(this->m_face, prev_glyph, glyph_index, - FT_KERNING_DEFAULT, &kerning); - x += kerning.x / 64.0; - } - - Glyph* glyph = this->m_cache.loadGlyph( - this->m_face, glyph_index, this->m_antialias); - if (glyph) { - glyph->bitmap = &FT_BitmapGlyph(glyph->ft_glyph)->bitmap; - glyph->x = x + glyph->bearingX; - glyph->y = y - + this->height() - + this->descender() // descender is negative - - glyph->bearingY; - - callback(*glyph); - - x += glyph->ft_glyph->advance.x / double(1 << 16); - y += glyph->ft_glyph->advance.y / double(1 << 16); - - this->m_cache.doneGlyph(glyph); - } - - prev_glyph = glyph_index; + if (m_useKerning && m_prevGlyph && glyphIndex) { + FT_Vector kerning; + FT_Get_Kerning(m_face, m_prevGlyph, glyphIndex, + FT_KERNING_DEFAULT, &kerning); + m_x += kerning.x / 64.0; } + + Glyph* glyph = m_face.cache().loadGlyph( + m_face, glyphIndex, m_face.antialias()); + if (glyph) { + glyph->bitmap = &FT_BitmapGlyph(glyph->ft_glyph)->bitmap; + glyph->x = m_x + glyph->bearingX; + glyph->y = m_y + + m_face.height() + + m_face.descender() // descender is negative + - glyph->bearingY; + + m_x += glyph->ft_glyph->advance.x / double(1 << 16); + m_y += glyph->ft_glyph->advance.y / double(1 << 16); + + glyph->startX = initialX; + glyph->endX = m_x; + + processGlyph(*glyph); + + m_face.cache().doneGlyph(glyph); + } + + m_prevGlyph = glyphIndex; } - gfx::Rect calcTextBounds(const std::string& str) { - gfx::Rect bounds(0, 0, 0, 0); + private: + FaceFT& m_face; + bool m_useKerning; + FT_UInt m_prevGlyph; + double m_x, m_y; + }; - forEachGlyph( - str, - [&bounds, this](Glyph& glyph) { + template + gfx::Rect calc_text_bounds(FaceFT& face, const std::string& str) { + gfx::Rect bounds(0, 0, 0, 0); + auto it = base::utf8_const_iterator(str.begin()); + auto end = base::utf8_const_iterator(str.end()); + ForEachGlyph feg(face); + for (; it != end; ++it) { + feg.processChar( + *it, + [&bounds](Glyph& glyph) { bounds |= gfx::Rect(int(glyph.x), int(glyph.y), glyph.bitmap->width, glyph.bitmap->rows); }); - - return bounds; } - }; + return bounds; + } class NoCache { public: diff --git a/src/she/CMakeLists.txt b/src/she/CMakeLists.txt index 4eb57d106..83039c7e4 100644 --- a/src/she/CMakeLists.txt +++ b/src/she/CMakeLists.txt @@ -1,8 +1,9 @@ # SHE -# Copyright (C) 2012-2016 David Capello +# Copyright (C) 2012-2017 David Capello set(SHE_SOURCES common/freetype_font.cpp + draw_text.cpp system.cpp) ###################################################################### diff --git a/src/she/alleg4/alleg_surface.h b/src/she/alleg4/alleg_surface.h index 5e0e3a8e6..8711df085 100644 --- a/src/she/alleg4/alleg_surface.h +++ b/src/she/alleg4/alleg_surface.h @@ -1,5 +1,5 @@ // SHE library -// Copyright (C) 2012-2016 David Capello +// Copyright (C) 2012-2017 David Capello // // This file is released under the terms of the MIT license. // Read LICENSE.txt for more information. @@ -15,7 +15,7 @@ namespace she { - class Alleg4Surface : public GenericDrawTextSurface > { + class Alleg4Surface : public GenericDrawColoredRgbaSurface { public: enum DestroyFlag { None = 0, diff --git a/src/she/common/freetype_font.cpp b/src/she/common/freetype_font.cpp index 5b827e5fb..cd09f67fc 100644 --- a/src/she/common/freetype_font.cpp +++ b/src/she/common/freetype_font.cpp @@ -1,5 +1,5 @@ // SHE library -// Copyright (C) 2016 David Capello +// Copyright (C) 2016-2017 David Capello // // This file is released under the terms of the MIT license. // Read LICENSE.txt for more information. @@ -48,17 +48,9 @@ int FreeTypeFont::height() const return int(m_face.height()); } -int FreeTypeFont::charWidth(int chr) const -{ - // TODO avoid creating a temporary string - std::wstring tmp; - tmp.push_back(chr); - return m_face.calcTextBounds(base::to_utf8(tmp)).w; -} - int FreeTypeFont::textLength(const std::string& str) const { - return m_face.calcTextBounds(str).w; + return ft::calc_text_bounds(m_face, str).w; } bool FreeTypeFont::isScalable() const diff --git a/src/she/common/freetype_font.h b/src/she/common/freetype_font.h index 4495e1e1e..88e352a9e 100644 --- a/src/she/common/freetype_font.h +++ b/src/she/common/freetype_font.h @@ -1,5 +1,5 @@ // SHE library -// Copyright (C) 2016 David Capello +// Copyright (C) 2016-2017 David Capello // // This file is released under the terms of the MIT license. // Read LICENSE.txt for more information. @@ -24,7 +24,6 @@ namespace she { void dispose() override; FontType type() override; int height() const override; - int charWidth(int chr) const override; int textLength(const std::string& str) const override; bool isScalable() const override; void setSize(int size) override; diff --git a/src/she/common/generic_surface.h b/src/she/common/generic_surface.h index 2434f8482..6d94703c4 100644 --- a/src/she/common/generic_surface.h +++ b/src/she/common/generic_surface.h @@ -1,5 +1,5 @@ // SHE library -// Copyright (C) 2012-2016 David Capello +// Copyright (C) 2012-2017 David Capello // // This file is released under the terms of the MIT license. // Read LICENSE.txt for more information. @@ -9,8 +9,8 @@ #pragma once #include "gfx/clip.h" -#include "she/common/freetype_font.h" -#include "she/common/sprite_sheet_font.h" +#include "gfx/color.h" +#include "she/surface.h" namespace she { @@ -19,7 +19,7 @@ namespace { #define MUL_UN8(a, b, t) \ ((t) = (a) * (b) + 0x80, ((((t) >> 8) + (t)) >> 8)) -gfx::Color blend(const gfx::Color backdrop, gfx::Color src) +inline gfx::Color blend(const gfx::Color backdrop, gfx::Color src) { if (gfx::geta(backdrop) == 0) return src; @@ -92,145 +92,6 @@ public: } }; -template -class GenericDrawTextSurface : public Base { -public: - - void drawChar(Font* font, gfx::Color fg, gfx::Color bg, int x, int y, int chr) override { - switch (font->type()) { - - case FontType::kSpriteSheet: { - SpriteSheetFont* ssFont = static_cast(font); - - gfx::Rect charBounds = ssFont->getCharBounds(chr); - if (!charBounds.isEmpty()) { - Surface* sheet = ssFont->getSurfaceSheet(); - SurfaceLock lock(sheet); - this->drawColoredRgbaSurface(sheet, fg, bg, gfx::Clip(x, y, charBounds)); - } - break; - } - - case FontType::kTrueType: { - // TODO avoid a temporary string - std::wstring str; - str.push_back(chr); - drawString(font, fg, bg, x, y, base::to_utf8(str).c_str()); - break; - } - - } - } - - void drawString(Font* font, gfx::Color fg, gfx::Color bg, int x, int y, const std::string& str) override { - switch (font->type()) { - - case FontType::kSpriteSheet: { - base::utf8_const_iterator it(str.begin()), end(str.end()); - while (it != end) { - drawChar(font, fg, bg, x, y, *it); - x += font->charWidth(*it); - ++it; - } - break; - } - - case FontType::kTrueType: { - FreeTypeFont* ttFont = static_cast(font); - bool antialias = ttFont->face().antialias(); - int fg_alpha = gfx::geta(fg); - - gfx::Rect clipBounds = this->getClipBounds(); - - she::SurfaceFormatData fd; - this->getFormat(&fd); - - ttFont->face().forEachGlyph( - str, - [this, x, y, fg, fg_alpha, bg, antialias, &clipBounds, &fd](const ft::Glyph& glyph) { - gfx::Rect origDstBounds(x + int(glyph.x), - y + int(glyph.y), - int(glyph.bitmap->width), - int(glyph.bitmap->rows)); - gfx::Rect dstBounds = origDstBounds; - dstBounds &= clipBounds; - if (dstBounds.isEmpty()) - return; - - int clippedRows = dstBounds.y - origDstBounds.y; - int dst_y = dstBounds.y; - int t; - for (int v=0; vbuffer - + (v+clippedRows)*glyph.bitmap->pitch; - int dst_x = dstBounds.x; - uint32_t* dst_address = - (uint32_t*)this->getData(dst_x, dst_y); - - // Skip first clipped pixels - for (int u=0; u> fd.redShift), - ((backdrop & fd.greenMask) >> fd.greenShift), - ((backdrop & fd.blueMask) >> fd.blueShift), - ((backdrop & fd.alphaMask) >> fd.alphaShift)); - - gfx::Color output = gfx::rgba(gfx::getr(fg), - gfx::getg(fg), - gfx::getb(fg), - MUL_UN8(fg_alpha, alpha, t)); - if (gfx::geta(bg) > 0) - output = blend(blend(backdropColor, bg), output); - else - output = blend(backdropColor, output); - - *dst_address = - ((gfx::getr(output) << fd.redShift ) & fd.redMask ) | - ((gfx::getg(output) << fd.greenShift) & fd.greenMask) | - ((gfx::getb(output) << fd.blueShift ) & fd.blueMask ) | - ((gfx::geta(output) << fd.alphaShift) & fd.alphaMask); - - ++dst_address; - } - } - }); - break; - } - - } - } - -}; - } // namespace she #endif diff --git a/src/she/common/sprite_sheet_font.h b/src/she/common/sprite_sheet_font.h index 727ae2028..bc743fbe9 100644 --- a/src/she/common/sprite_sheet_font.h +++ b/src/she/common/sprite_sheet_font.h @@ -1,5 +1,5 @@ // SHE library -// Copyright (C) 2012-2016 David Capello +// Copyright (C) 2012-2017 David Capello // // This file is released under the terms of the MIT license. // Read LICENSE.txt for more information. @@ -41,15 +41,11 @@ public: return getCharBounds(' ').h; } - int charWidth(int chr) const override { - return getCharBounds(chr).w; - } - int textLength(const std::string& str) const override { base::utf8_const_iterator it(str.begin()), end(str.end()); int x = 0; while (it != end) { - x += charWidth(*it); + x += getCharBounds(*it).w; ++it; } return x; diff --git a/src/she/draw_text.cpp b/src/she/draw_text.cpp new file mode 100644 index 000000000..c9335ad05 --- /dev/null +++ b/src/she/draw_text.cpp @@ -0,0 +1,201 @@ +// SHE library +// Copyright (C) 2017 David Capello +// +// This file is released under the terms of the MIT license. +// Read LICENSE.txt for more information. + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "she/draw_text.h" + +#include "gfx/clip.h" +#include "she/common/freetype_font.h" +#include "she/common/generic_surface.h" +#include "she/common/sprite_sheet_font.h" + +namespace she { + +gfx::Rect draw_text(Surface* surface, Font* font, + base::utf8_const_iterator it, + const base::utf8_const_iterator& end, + gfx::Color fg, gfx::Color bg, + int x, int y, + DrawTextDelegate* delegate) +{ + gfx::Rect textBounds; + bool drawChar = true; + bool moveCaret = true; + + switch (font->type()) { + + case FontType::kSpriteSheet: { + SpriteSheetFont* ssFont = static_cast(font); + while (it != end) { + int chr = *it; + if (delegate) + delegate->preProcessChar(it, end, chr, fg, bg, drawChar, moveCaret); + + if (moveCaret) { + gfx::Rect charBounds = ssFont->getCharBounds(chr); + gfx::Rect outCharBounds(x, y, charBounds.w, charBounds.h); + if (delegate && !delegate->preDrawChar(outCharBounds)) + break; + + if (!charBounds.isEmpty()) { + if (surface && drawChar) { + Surface* sheet = ssFont->getSurfaceSheet(); + SurfaceLock lock(sheet); + surface->drawColoredRgbaSurface(sheet, fg, bg, gfx::Clip(x, y, charBounds)); + } + } + + textBounds |= outCharBounds; + if (delegate) + delegate->postDrawChar(outCharBounds); + + x += charBounds.w; + } + + ++it; + } + break; + } + + case FontType::kTrueType: { + FreeTypeFont* ttFont = static_cast(font); + bool antialias = ttFont->face().antialias(); + bool done = false; + int fg_alpha = gfx::geta(fg); + + gfx::Rect clipBounds; + she::SurfaceFormatData fd; + if (surface) { + clipBounds = surface->getClipBounds(); + surface->getFormat(&fd); + } + + ft::ForEachGlyph feg(ttFont->face()); + while (it != end) { + int chr = *it; + if (delegate) + delegate->preProcessChar(it, end, chr, fg, bg, drawChar, moveCaret); + + if (!moveCaret) { + ++it; + continue; + } + + feg.processChar( + chr, + [x, y, fg, fg_alpha, bg, antialias, surface, + &clipBounds, &textBounds, &fd, &done, delegate, drawChar] + (const ft::Glyph& glyph) { + gfx::Rect origDstBounds( + x + int(glyph.startX), + y + int(glyph.y), + int(glyph.endX) - int(glyph.startX), + int(glyph.bitmap->rows) ? int(glyph.bitmap->rows): 1); + + if (delegate && !delegate->preDrawChar(origDstBounds)) { + done = true; + return; + } + origDstBounds.x = x + int(glyph.x); + origDstBounds.w = int(glyph.bitmap->width); + origDstBounds.h = int(glyph.bitmap->rows); + + gfx::Rect dstBounds = origDstBounds; + if (surface) + dstBounds &= clipBounds; + + if (surface && drawChar && !dstBounds.isEmpty()) { + int clippedRows = dstBounds.y - origDstBounds.y; + int dst_y = dstBounds.y; + int t; + for (int v=0; vbuffer + + (v+clippedRows)*glyph.bitmap->pitch; + int dst_x = dstBounds.x; + uint32_t* dst_address = + (uint32_t*)surface->getData(dst_x, dst_y); + + // Skip first clipped pixels + for (int u=0; u> fd.redShift), + ((backdrop & fd.greenMask) >> fd.greenShift), + ((backdrop & fd.blueMask) >> fd.blueShift), + ((backdrop & fd.alphaMask) >> fd.alphaShift)); + + gfx::Color output = gfx::rgba(gfx::getr(fg), + gfx::getg(fg), + gfx::getb(fg), + MUL_UN8(fg_alpha, alpha, t)); + if (gfx::geta(bg) > 0) + output = blend(blend(backdropColor, bg), output); + else + output = blend(backdropColor, output); + + *dst_address = + ((gfx::getr(output) << fd.redShift ) & fd.redMask ) | + ((gfx::getg(output) << fd.greenShift) & fd.greenMask) | + ((gfx::getb(output) << fd.blueShift ) & fd.blueMask ) | + ((gfx::geta(output) << fd.alphaShift) & fd.alphaMask); + + ++dst_address; + } + } + } + + if (!origDstBounds.w) origDstBounds.w = 1; + if (!origDstBounds.h) origDstBounds.h = 1; + textBounds |= origDstBounds; + if (delegate) + delegate->postDrawChar(origDstBounds); + }); + + if (done) + break; + + ++it; + } + break; + } + + } + + return textBounds; +} + +} // namespace she diff --git a/src/she/draw_text.h b/src/she/draw_text.h new file mode 100644 index 000000000..a8ef4cc5b --- /dev/null +++ b/src/she/draw_text.h @@ -0,0 +1,62 @@ +// SHE library +// Copyright (C) 2017 David Capello +// +// This file is released under the terms of the MIT license. +// Read LICENSE.txt for more information. + +#ifndef SHE_DRAW_TEXT_H_INCLUDED +#define SHE_DRAW_TEXT_H_INCLUDED +#pragma once + +#include "base/string.h" +#include "gfx/color.h" +#include "gfx/fwd.h" + +namespace she { + + class Font; + class Surface; + class SurfaceLock; + + class DrawTextDelegate { + public: + virtual ~DrawTextDelegate() { } + + // This is called before drawing the character. Here you can + // modify the final painted character (e.g. for passwords you can + // modify chr='*') and change the specific fg/bg color for this + // char (e.g. to change the color depending if is a + // selected/highlighted portion of text). + virtual void preProcessChar(const base::utf8_const_iterator& it, + const base::utf8_const_iterator& end, + int& chr, + gfx::Color& fg, + gfx::Color& bg, + bool& drawChar, + bool& moveCaret) { + // Do nothing + } + + virtual bool preDrawChar(const gfx::Rect& charBounds) { + // Returns false if the process should stop here. + return true; + } + + virtual void postDrawChar(const gfx::Rect& charBounds) { + // Do nothing + } + }; + + // The surface can be nullptr just to process the string + // (e.g. measure how much space will use the text without drawing + // it). + gfx::Rect draw_text(Surface* surface, Font* font, + base::utf8_const_iterator it, + const base::utf8_const_iterator& end, + gfx::Color fg, gfx::Color bg, + int x, int y, + DrawTextDelegate* delegate); + +} // namespace she + +#endif diff --git a/src/she/font.h b/src/she/font.h index 340c8d7bc..b566c13bd 100644 --- a/src/she/font.h +++ b/src/she/font.h @@ -1,5 +1,5 @@ // SHE library -// Copyright (C) 2012-2016 David Capello +// Copyright (C) 2012-2017 David Capello // // This file is released under the terms of the MIT license. // Read LICENSE.txt for more information. @@ -24,7 +24,6 @@ namespace she { virtual void dispose() = 0; virtual FontType type() = 0; virtual int height() const = 0; - virtual int charWidth(int chr) const = 0; virtual int textLength(const std::string& str) const = 0; virtual bool isScalable() const = 0; virtual void setSize(int size) = 0; diff --git a/src/she/skia/skia_surface.h b/src/she/skia/skia_surface.h index 84a2ee082..712420e6e 100644 --- a/src/she/skia/skia_surface.h +++ b/src/she/skia/skia_surface.h @@ -1,5 +1,5 @@ // SHE library -// Copyright (C) 2012-2016 David Capello +// Copyright (C) 2012-2017 David Capello // // This file is released under the terms of the MIT license. // Read LICENSE.txt for more information. @@ -31,7 +31,7 @@ inline SkIRect to_skia(const gfx::Rect& rc) { return SkIRect::MakeXYWH(rc.x, rc.y, rc.w, rc.h); } -class SkiaSurface : public GenericDrawTextSurface { +class SkiaSurface : public Surface { public: SkiaSurface() : m_surface(nullptr) , m_canvas(nullptr) diff --git a/src/she/surface.h b/src/she/surface.h index c11057a0a..bbf79fc6e 100644 --- a/src/she/surface.h +++ b/src/she/surface.h @@ -1,5 +1,5 @@ // SHE library -// Copyright (C) 2012-2016 David Capello +// Copyright (C) 2012-2017 David Capello // // This file is released under the terms of the MIT license. // Read LICENSE.txt for more information. @@ -8,6 +8,7 @@ #define SHE_SURFACE_H_INCLUDED #pragma once +#include "base/string.h" #include "gfx/color.h" #include "gfx/fwd.h" #include "she/surface_format.h" @@ -63,9 +64,6 @@ namespace she { virtual void drawRgbaSurface(const Surface* src, int dstx, int dsty) = 0; virtual void drawColoredRgbaSurface(const Surface* src, gfx::Color fg, gfx::Color bg, const gfx::Clip& clip) = 0; - virtual void drawChar(Font* font, gfx::Color fg, gfx::Color bg, int x, int y, int chr) = 0; - virtual void drawString(Font* font, gfx::Color fg, gfx::Color bg, int x, int y, const std::string& str) = 0; - virtual void applyScale(int scaleFactor) = 0; virtual void* nativeHandle() = 0; }; diff --git a/src/ui/entry.cpp b/src/ui/entry.cpp index 9f9cebee6..37e3fdaf7 100644 --- a/src/ui/entry.cpp +++ b/src/ui/entry.cpp @@ -1,5 +1,5 @@ // Aseprite UI Library -// Copyright (C) 2001-2016 David Capello +// Copyright (C) 2001-2017 David Capello // // This file is released under the terms of the MIT license. // Read LICENSE.txt for more information. @@ -13,6 +13,7 @@ #include "base/bind.h" #include "base/string.h" #include "clip/clip.h" +#include "she/draw_text.h" #include "she/font.h" #include "she/system.h" #include "ui/manager.h" @@ -29,6 +30,25 @@ namespace ui { +namespace { + +class MeasureTextDelegate : public she::DrawTextDelegate { +public: + MeasureTextDelegate() { } + + gfx::Rect bounds() const { return m_bounds; } + + bool preDrawChar(const gfx::Rect& charBounds) override { + m_bounds |= charBounds; + return true; + } + +private: + gfx::Rect m_bounds; +}; + +} + Entry::Entry(const std::size_t maxsize, const char* format, ...) : Widget(kEntryWidget) , m_timer(500, this) @@ -110,32 +130,32 @@ void Entry::hideCaret() void Entry::setCaretPos(int pos) { - auto utf8_begin = base::utf8_const_iterator(text().begin()); - auto utf8_end = base::utf8_const_iterator(text().end()); + gfx::Size caretSize = theme()->getEntryCaretSize(this); int textlen = base::utf8_length(text()); - m_caret = MID(0, pos, textlen); + m_scroll = MID(0, m_scroll, textlen); // Backward scroll - if (m_scroll > m_caret) + if (m_caret < m_scroll) m_scroll = m_caret; - // Forward scroll - --m_scroll; - int c; - while (true) { - c = ++m_scroll; - auto utf8_it = utf8_begin + MID(0, c, textlen); - int x = bounds().x + border().left() - - font()->charWidth(' '); // Space for the carret - for (; utf8_it != utf8_end; ++c, ++utf8_it) { - int ch = *utf8_it; - x += font()->charWidth(ch); - if (x >= bounds().x2()-border().right()) + else if (m_caret > m_scroll) { + auto it = base::utf8_const_iterator(text().begin()) + m_scroll; + while (m_caret > m_scroll) { + MeasureTextDelegate delegate; + she::draw_text(nullptr, font(), it, it+(m_caret-m_scroll), + gfx::ColorNone, gfx::ColorNone, 0, 0, + &delegate); + + int x = bounds().x + border().left() + + delegate.bounds().w + caretSize.w; + if (x < bounds().x2() - border().right()) break; + else { + ++it; + ++m_scroll; + } } - if (m_caret < c || utf8_it == utf8_end) - break; } m_timer.start(); @@ -356,58 +376,15 @@ bool Entry::onProcessMessage(Message* msg) case kMouseMoveMessage: if (hasCapture()) { - gfx::Point mousePos = static_cast(msg)->position(); - auto utf8_begin = base::utf8_const_iterator(text().begin()); - auto utf8_end = base::utf8_const_iterator(text().end()); - int textlen = base::utf8_length(text()); - int c, x; - - bool move = true; bool is_dirty = false; + int c = getCaretFromMouse(static_cast(msg)); - // Backward scroll - if (mousePos.x < bounds().x) { - if (m_scroll > 0) { - m_caret = --m_scroll; - move = false; - is_dirty = true; - invalidate(); - } - } - // Forward scroll - else if (mousePos.x >= bounds().x2()) { - if (m_scroll < textlen - getAvailableTextLength()) { - ++m_scroll; - x = bounds().x + border().left(); - - auto utf8_it = utf8_begin + MID(0, m_scroll, textlen); - for (c=m_scroll; utf8_it != utf8_end; ++c, ++utf8_it) { - int ch = (c < textlen ? *utf8_it: ' '); - - x += font()->charWidth(ch); - if (x > bounds().x2()-border().right()) { - c--; - break; - } - } - m_caret = MID(0, c, textlen); - move = false; - is_dirty = true; - invalidate(); - } - } - - c = getCaretFromMouse(static_cast(msg)); - - if (static_cast(msg)->left() || - (move && !isPosInSelection(c))) { + if (static_cast(msg)->left() || !isPosInSelection(c)) { // Move caret - if (move) { - if (m_caret != c) { - m_caret = c; - is_dirty = true; - invalidate(); - } + if (m_caret != c) { + setCaretPos(c); + is_dirty = true; + invalidate(); } // Move selection @@ -465,7 +442,7 @@ bool Entry::onProcessMessage(Message* msg) void Entry::onSizeHint(SizeHintEvent& ev) { int w = - + font()->charWidth('w') * MIN(m_maxsize, 6) + + font()->textLength("w") * MIN(m_maxsize, 6) + 2*guiscale() + border().width(); @@ -509,37 +486,40 @@ gfx::Rect Entry::onGetEntryTextBounds() const int Entry::getCaretFromMouse(MouseMessage* mousemsg) { - base::utf8_const_iterator utf8_begin = base::utf8_const_iterator(text().begin()); - base::utf8_const_iterator utf8_end = base::utf8_const_iterator(text().end()); - int caret = m_caret; + int mouseX = mousemsg->position().x; + if (mouseX < bounds().x+border().left()) { + // Scroll to the left + return MAX(0, m_scroll-1); + } + int textlen = base::utf8_length(text()); - gfx::Rect bounds = getEntryTextBounds().offset(this->bounds().origin()); + auto it = base::utf8_const_iterator(text().begin()) + m_scroll; + int i = MIN(m_scroll, textlen); + for (; iposition().x; - mx = MID(bounds.x, mx, bounds.x2()-1); + int x = bounds().x + border().left() + delegate.bounds().w; - int x = bounds.x; - - auto utf8_it = utf8_begin + MID(0, m_scroll, textlen); - int c = m_scroll; - for (; utf8_it != utf8_end; ++c, ++utf8_it) { - int w = font()->charWidth(*utf8_it); - if (x+w >= bounds.x2()-border().right()) - break; - if ((mx >= x) && (mx < x+w)) { - caret = c; - break; + if (mouseX > bounds().x2() - border().right()) { + if (x >= bounds().x2() - border().right()) { + // Scroll to the right + break; + } } - x += w; - } - - if (utf8_it == utf8_end) { - if ((mx >= x) && (mx < bounds.x2())) { - caret = c; + else { + if (x > mouseX) { + // Previous char is the selected one + if (i > m_scroll) + --i; + break; + } } } - return MID(0, caret, textlen); + return MID(0, i, textlen); } void Entry::executeCmd(EntryCmd cmd, int unicodeChar, bool shift_pressed) @@ -560,6 +540,16 @@ void Entry::executeCmd(EntryCmd cmd, int unicodeChar, bool shift_pressed) text.erase(selbeg, selend-selbeg+1); m_caret = selbeg; + + // We set the caret to the beginning of the erased selection, + // needed to show the first inserted character in case + // m_scroll > m_caret. E.g. we select all text and insert a + // new character to replace the whole text, the new inserted + // character makes m_caret=1, so m_scroll will be 1 too, but + // we need to make m_scroll=0 to show the new inserted char.) + // In this way, we first ensure a m_scroll value enough to + // show the new inserted character. + setCaretPos(m_caret); } // put the character @@ -782,11 +772,6 @@ void Entry::backwardWord() m_caret = 0; } -int Entry::getAvailableTextLength() -{ - return clientChildrenBounds().w / font()->charWidth('w'); -} - bool Entry::isPosInSelection(int pos) { return (pos >= MIN(m_caret, m_select) && pos <= MAX(m_caret, m_select)); diff --git a/src/ui/entry.h b/src/ui/entry.h index 4d77dbf46..268fecbde 100644 --- a/src/ui/entry.h +++ b/src/ui/entry.h @@ -1,5 +1,5 @@ // Aseprite UI Library -// Copyright (C) 2001-2016 David Capello +// Copyright (C) 2001-2017 David Capello // // This file is released under the terms of the MIT license. // Read LICENSE.txt for more information. @@ -84,7 +84,6 @@ namespace ui { void executeCmd(EntryCmd cmd, int ascii, bool shift_pressed); void forwardWord(); void backwardWord(); - int getAvailableTextLength(); bool isPosInSelection(int pos); void showEditPopupMenu(const gfx::Point& pt); diff --git a/src/ui/graphics.cpp b/src/ui/graphics.cpp index 57dd67620..1c2716b80 100644 --- a/src/ui/graphics.cpp +++ b/src/ui/graphics.cpp @@ -1,5 +1,5 @@ // Aseprite UI Library -// Copyright (C) 2001-2016 David Capello +// Copyright (C) 2001-2017 David Capello // // This file is released under the terms of the MIT license. // Read LICENSE.txt for more information. @@ -17,6 +17,7 @@ #include "gfx/region.h" #include "gfx/size.h" #include "she/display.h" +#include "she/draw_text.h" #include "she/font.h" #include "she/surface.h" #include "she/system.h" @@ -200,95 +201,142 @@ void Graphics::setFont(she::Font* font) m_font = font; } -void Graphics::drawChar(int chr, gfx::Color fg, gfx::Color bg, int x, int y) +void Graphics::drawText(base::utf8_const_iterator it, + const base::utf8_const_iterator& end, + gfx::Color fg, gfx::Color bg, + const gfx::Point& origPt, + she::DrawTextDelegate* delegate) { - dirty(gfx::Rect(gfx::Point(m_dx+x, m_dy+y), measureChar(chr))); + gfx::Point pt(m_dx+origPt.x, m_dy+origPt.y); she::SurfaceLock lock(m_surface); - m_surface->drawChar(m_font, fg, bg, m_dx+x, m_dy+y, chr); + gfx::Rect textBounds = + she::draw_text(m_surface, m_font, it, end, fg, bg, pt.x, pt.y, delegate); + + dirty(gfx::Rect(pt.x, pt.y, textBounds.w, textBounds.h)); } -void Graphics::drawString(const std::string& str, gfx::Color fg, gfx::Color bg, const gfx::Point& ptOrig) +void Graphics::drawText(const std::string& str, gfx::Color fg, gfx::Color bg, const gfx::Point& pt) { - gfx::Point pt(m_dx+ptOrig.x, m_dy+ptOrig.y); - dirty(gfx::Rect(pt.x, pt.y, m_font->textLength(str), m_font->height())); - - she::SurfaceLock lock(m_surface); - m_surface->drawString(m_font, fg, bg, pt.x, pt.y, str); + drawText(base::utf8_const_iterator(str.begin()), + base::utf8_const_iterator(str.end()), + fg, bg, pt, nullptr); } -void Graphics::drawUIString(const std::string& str, gfx::Color fg, gfx::Color bg, const gfx::Point& pt, - bool drawUnderscore) -{ - she::SurfaceLock lock(m_surface); - base::utf8_const_iterator it(str.begin()), end(str.end()); - int x = m_dx+pt.x; - int y = m_dy+pt.y; - int underscored_x = 0; - int underscored_w = -1; +namespace { - while (it != end) { - if (*it == '&') { - ++it; - if (it != end && *it != '&') { - underscored_x = x; - underscored_w = m_font->charWidth(*it); +class DrawUITextDelegate : public she::DrawTextDelegate { +public: + DrawUITextDelegate(she::Surface* surface, + she::Font* font, const bool drawUnderscore) + : m_surface(surface) + , m_font(font) + , m_drawUnderscore(drawUnderscore) + , m_underscoreNext(false) { + } + + gfx::Rect bounds() const { return m_bounds; } + + void preProcessChar(const base::utf8_const_iterator& it, + const base::utf8_const_iterator& end, + int& chr, + gfx::Color& fg, + gfx::Color& bg, + bool& drawChar, + bool& moveCaret) override { + if (m_underscoreNext) + m_underscoreColor = fg; + + if (!m_surface) + drawChar = false; + + moveCaret = true; + if (chr == '&') { // TODO change this with other character, maybe '_' or configurable + auto it2 = it; + ++it2; + if (it2 != end && *it2 != '&') { + m_underscoreNext = true; + moveCaret = false; } } - m_surface->drawChar(m_font, fg, bg, x, y, *it); - x += m_font->charWidth(*it); - ++it; } - y += m_font->height(); - if (drawUnderscore && underscored_w > 0) { - m_surface->fillRect(fg, - gfx::Rect(underscored_x, y, underscored_w, guiscale())); - y += guiscale(); + bool preDrawChar(const gfx::Rect& charBounds) override { + m_bounds |= charBounds; + return true; } - dirty(gfx::Rect(pt, gfx::Point(x, y))); + void postDrawChar(const gfx::Rect& charBounds) override { + if (m_underscoreNext) { + m_underscoreNext = false; + if (m_drawUnderscore) { + // TODO underscore height = guiscale() should be configurable from ui::Theme + int dy = 0; + if (m_font->type() == she::FontType::kTrueType) // TODO use other method to locate the underline + dy += guiscale(); + gfx::Rect underscoreBounds(charBounds.x, charBounds.y+charBounds.h+dy, + charBounds.w, guiscale()); + m_surface->fillRect(m_underscoreColor, underscoreBounds); + m_bounds |= underscoreBounds; + } + } + } + +private: + she::Surface* m_surface; + she::Font* m_font; + bool m_drawUnderscore; + bool m_underscoreNext; + gfx::Color m_underscoreColor; + gfx::Rect m_bounds; +}; + } -void Graphics::drawAlignedUIString(const std::string& str, gfx::Color fg, gfx::Color bg, const gfx::Rect& rc, int align) +void Graphics::drawUIText(const std::string& str, gfx::Color fg, gfx::Color bg, const gfx::Point& pt, + bool drawUnderscore) +{ + she::SurfaceLock lock(m_surface); + int x = m_dx+pt.x; + int y = m_dy+pt.y; + + DrawUITextDelegate delegate(m_surface, m_font, drawUnderscore); + she::draw_text(m_surface, m_font, + base::utf8_const_iterator(str.begin()), + base::utf8_const_iterator(str.end()), + fg, bg, x, y, &delegate); + + dirty(delegate.bounds()); +} + +void Graphics::drawAlignedUIText(const std::string& str, gfx::Color fg, gfx::Color bg, const gfx::Rect& rc, int align) { doUIStringAlgorithm(str, fg, bg, rc, align, true); } -gfx::Size Graphics::measureChar(int chr) +gfx::Size Graphics::measureUIText(const std::string& str) { return gfx::Size( - m_font->charWidth(chr), - m_font->height()); -} - -gfx::Size Graphics::measureUIString(const std::string& str) -{ - return gfx::Size( - Graphics::measureUIStringLength(str, m_font), + Graphics::measureUITextLength(str, m_font), m_font->height()); } // static -int Graphics::measureUIStringLength(const std::string& str, she::Font* font) +int Graphics::measureUITextLength(const std::string& str, she::Font* font) { - base::utf8_const_iterator it(str.begin()), end(str.end()); - int length = 0; - - while (it != end) { - if (*it == '&') - ++it; - - length += font->charWidth(*it); - ++it; - } - - return length; + DrawUITextDelegate delegate(nullptr, font, false); + she::draw_text(nullptr, font, + base::utf8_const_iterator(str.begin()), + base::utf8_const_iterator(str.end()), + gfx::ColorNone, gfx::ColorNone, 0, 0, + &delegate); + return delegate.bounds().w; } gfx::Size Graphics::fitString(const std::string& str, int maxWidth, int align) { - return doUIStringAlgorithm(str, gfx::ColorNone, gfx::ColorNone, gfx::Rect(0, 0, maxWidth, 0), align, false); + return doUIStringAlgorithm(str, gfx::ColorNone, gfx::ColorNone, + gfx::Rect(0, 0, maxWidth, 0), align, false); } gfx::Size Graphics::doUIStringAlgorithm(const std::string& str, gfx::Color fg, gfx::Color bg, const gfx::Rect& rc, int align, bool draw) @@ -366,7 +414,7 @@ gfx::Size Graphics::doUIStringAlgorithm(const std::string& str, gfx::Color fg, g else xout = pt.x; - drawString(line, fg, bg, gfx::Point(xout, pt.y)); + drawText(line, fg, bg, gfx::Point(xout, pt.y)); if (!gfx::is_transparent(bg)) fillAreaBetweenRects(bg, diff --git a/src/ui/graphics.h b/src/ui/graphics.h index 6be11d542..946c3cfc4 100644 --- a/src/ui/graphics.h +++ b/src/ui/graphics.h @@ -1,5 +1,5 @@ // Aseprite UI Library -// Copyright (C) 2001-2015 David Capello +// Copyright (C) 2001-2017 David Capello // // This file is released under the terms of the MIT license. // Read LICENSE.txt for more information. @@ -10,6 +10,7 @@ #include "base/disable_copying.h" #include "base/shared_ptr.h" +#include "base/string.h" #include "gfx/color.h" #include "gfx/point.h" #include "gfx/rect.h" @@ -22,6 +23,7 @@ namespace gfx { } namespace she { + class DrawTextDelegate; class Font; class Surface; } @@ -79,14 +81,20 @@ namespace ui { she::Font* font() { return m_font; } void setFont(she::Font* font); - void drawChar(int chr, gfx::Color fg, gfx::Color bg, int x, int y); - void drawString(const std::string& str, gfx::Color fg, gfx::Color bg, const gfx::Point& pt); - void drawUIString(const std::string& str, gfx::Color fg, gfx::Color bg, const gfx::Point& pt, bool drawUnderscore = true); - void drawAlignedUIString(const std::string& str, gfx::Color fg, gfx::Color bg, const gfx::Rect& rc, int align); + void drawText(base::utf8_const_iterator it, + const base::utf8_const_iterator& end, + gfx::Color fg, gfx::Color bg, const gfx::Point& pt, + she::DrawTextDelegate* delegate); - gfx::Size measureChar(int chr); - gfx::Size measureUIString(const std::string& str); - static int measureUIStringLength(const std::string& str, she::Font* font); + void drawText(const std::string& str, gfx::Color fg, gfx::Color bg, + const gfx::Point& pt); + void drawUIText(const std::string& str, gfx::Color fg, gfx::Color bg, + const gfx::Point& pt, bool drawUnderscore = true); + void drawAlignedUIText(const std::string& str, gfx::Color fg, gfx::Color bg, + const gfx::Rect& rc, int align); + + gfx::Size measureUIText(const std::string& str); + static int measureUITextLength(const std::string& str, she::Font* font); gfx::Size fitString(const std::string& str, int maxWidth, int align); private: diff --git a/src/ui/int_entry.cpp b/src/ui/int_entry.cpp index 9d3114d73..36297a04a 100644 --- a/src/ui/int_entry.cpp +++ b/src/ui/int_entry.cpp @@ -1,5 +1,5 @@ // Aseprite UI Library -// Copyright (C) 2001-2016 David Capello +// Copyright (C) 2001-2017 David Capello // // This file is released under the terms of the MIT license. // Read LICENSE.txt for more information. @@ -133,10 +133,10 @@ bool IntEntry::onProcessMessage(Message* msg) void IntEntry::onSizeHint(SizeHintEvent& ev) { - int min_w = font()->textLength(m_slider.convertValueToText(m_min)); + int min_w = font()->textLength(m_slider.convertValueToText(m_min) + "%"); int max_w = font()->textLength(m_slider.convertValueToText(m_max)); - int w = MAX(min_w, max_w) + font()->charWidth('%'); + int w = MAX(min_w, max_w); int h = textHeight(); w += border().width(); diff --git a/src/ui/theme.cpp b/src/ui/theme.cpp index c59f4ca38..8f6edb0ec 100644 --- a/src/ui/theme.cpp +++ b/src/ui/theme.cpp @@ -1,5 +1,5 @@ // Aseprite UI Library -// Copyright (C) 2001-2016 David Capello +// Copyright (C) 2001-2017 David Capello // // This file is released under the terms of the MIT license. // Read LICENSE.txt for more information. @@ -203,7 +203,7 @@ void drawTextBox(Graphics* g, Widget* widget, else // Left align xout = x; - g->drawString(beg, fg, gfx::ColorNone, gfx::Point(xout, y)); + g->drawText(beg, fg, gfx::ColorNone, gfx::Point(xout, y)); } if (w) diff --git a/src/ui/theme.h b/src/ui/theme.h index 1c661a3ce..a69451247 100644 --- a/src/ui/theme.h +++ b/src/ui/theme.h @@ -1,5 +1,5 @@ // Aseprite UI Library -// Copyright (C) 2001-2013, 2015 David Capello +// Copyright (C) 2001-2017 David Capello // // This file is released under the terms of the MIT license. // Read LICENSE.txt for more information. @@ -8,6 +8,7 @@ #define UI_THEME_H_INCLUDED #pragma once +#include "gfx/size.h" #include "ui/base.h" #include "ui/cursor_type.h" @@ -43,6 +44,7 @@ namespace ui { virtual void getWindowMask(Widget* widget, gfx::Region& region) = 0; virtual void setDecorativeWidgetBounds(Widget* widget) = 0; virtual int getScrollbarSize() = 0; + virtual gfx::Size getEntryCaretSize(Widget* widget) = 0; virtual void paintDesktop(PaintEvent& ev) = 0; virtual void paintBox(PaintEvent& ev) = 0; diff --git a/src/ui/widget.cpp b/src/ui/widget.cpp index c51fb8c16..ab1bc7404 100644 --- a/src/ui/widget.cpp +++ b/src/ui/widget.cpp @@ -1,5 +1,5 @@ // Aseprite UI Library -// Copyright (C) 2001-2016 David Capello +// Copyright (C) 2001-2017 David Capello // // This file is released under the terms of the MIT license. // Read LICENSE.txt for more information. @@ -754,7 +754,7 @@ void Widget::getDrawableRegion(gfx::Region& region, DrawableRegionFlags flags) int Widget::textWidth() const { - return Graphics::measureUIStringLength(text().c_str(), font()); + return Graphics::measureUITextLength(text().c_str(), font()); } int Widget::textHeight() const