mirror of
https://github.com/aseprite/aseprite.git
synced 2024-10-06 06:50:07 +00:00
Render text/highlight selected text on the canvas
Now we don't render the default ui::Entry edges, but we paint just a border of the text bounds + the rendered text highlighting selected text on the canvas itself. With this change click mouse positions are translated with a scale factor that changes depending on the app::Editor zoom.
This commit is contained in:
parent
a487bcec7d
commit
5591a59001
@ -35,6 +35,12 @@
|
|||||||
#include "ui/message.h"
|
#include "ui/message.h"
|
||||||
#include "ui/paint_event.h"
|
#include "ui/paint_event.h"
|
||||||
|
|
||||||
|
#ifdef LAF_SKIA
|
||||||
|
#include "app/util/shader_helpers.h"
|
||||||
|
#include "os/skia/skia_helpers.h"
|
||||||
|
#include "os/skia/skia_surface.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace app {
|
namespace app {
|
||||||
|
|
||||||
using namespace ui;
|
using namespace ui;
|
||||||
@ -82,7 +88,13 @@ public:
|
|||||||
m_doc->generateMaskBoundaries();
|
m_doc->generateMaskBoundaries();
|
||||||
}
|
}
|
||||||
|
|
||||||
ExtraCelRef extraCel() const { return m_extraCel; }
|
// Returns the extra cel with the text rendered (but without the
|
||||||
|
// selected text highlighted).
|
||||||
|
ExtraCelRef extraCel() {
|
||||||
|
renderExtraCelBase();
|
||||||
|
renderExtraCelText(false);
|
||||||
|
return m_extraCel;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool onProcessMessage(Message* msg) override {
|
bool onProcessMessage(Message* msg) override {
|
||||||
@ -112,36 +124,48 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void onPaint(PaintEvent& ev) override {
|
void onPaint(PaintEvent& ev) override {
|
||||||
Entry::onPaint(ev);
|
Graphics* g = ev.graphics();
|
||||||
|
|
||||||
|
// Don't paint the base Entry borders
|
||||||
|
//Entry::onPaint(ev);
|
||||||
|
|
||||||
if (!hasText())
|
if (!hasText())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// Paint border
|
||||||
|
{
|
||||||
|
ui::Paint paint;
|
||||||
|
paint.style(ui::Paint::Stroke);
|
||||||
|
set_checkered_paint_mode(paint, 0,
|
||||||
|
gfx::rgba(0, 0, 0, 255),
|
||||||
|
gfx::rgba(255, 255, 255, 255));
|
||||||
|
g->drawRect(clientBounds(), paint);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
text::TextBlobRef blob = textBlob();
|
if (!textBlob()) {
|
||||||
if (!blob) {
|
|
||||||
m_doc->setExtraCel(nullptr);
|
m_doc->setExtraCel(nullptr);
|
||||||
m_editor->invalidate();
|
m_editor->invalidate();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto textColor =
|
// Render extra cel with text + selected text
|
||||||
color_utils::color_for_image(
|
renderExtraCelBase();
|
||||||
Preferences::instance().colorBar.fgColor(),
|
renderExtraCelText(true);
|
||||||
IMAGE_RGB);
|
m_doc->setExtraCel(m_extraCel);
|
||||||
|
|
||||||
doc::ImageRef image = render_text_blob(blob, textColor);
|
// Paint caret
|
||||||
if (image) {
|
if (isCaretVisible()) {
|
||||||
renderExtraCelBase();
|
int scroll, caret;
|
||||||
|
getEntryThemeInfo(&scroll, &caret, nullptr, nullptr);
|
||||||
|
|
||||||
doc::blend_image(
|
gfx::RectF caretBounds = getCharBoxBounds(caret);
|
||||||
m_extraCel->image(), image.get(),
|
caretBounds *= gfx::SizeF(scale());
|
||||||
gfx::Clip(image->bounds().size()),
|
caretBounds.w = 1;
|
||||||
m_doc->sprite()->palette(m_editor->frame()),
|
g->fillRect(gfx::rgba(0, 0, 0), caretBounds);
|
||||||
255, doc::BlendMode::NORMAL);
|
|
||||||
|
|
||||||
m_doc->setExtraCel(m_extraCel);
|
|
||||||
m_editor->invalidate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_editor->invalidate();
|
||||||
}
|
}
|
||||||
catch (const std::exception& e) {
|
catch (const std::exception& e) {
|
||||||
StatusBar::instance()->showTip(500, std::string(e.what()));
|
StatusBar::instance()->showTip(500, std::string(e.what()));
|
||||||
@ -159,6 +183,50 @@ private:
|
|||||||
doc::BlendMode::SRC);
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
doc::blend_image(
|
||||||
|
m_extraCel->image(), image.get(),
|
||||||
|
gfx::Clip(image->bounds().size()),
|
||||||
|
m_doc->sprite()->palette(m_editor->frame()),
|
||||||
|
255, doc::BlendMode::NORMAL);
|
||||||
|
}
|
||||||
|
|
||||||
Editor* m_editor;
|
Editor* m_editor;
|
||||||
Doc* m_doc;
|
Doc* m_doc;
|
||||||
ExtraCelRef m_extraCel;
|
ExtraCelRef m_extraCel;
|
||||||
@ -178,7 +246,7 @@ WritingTextState::WritingTextState(Editor* editor,
|
|||||||
App::instance()->contextBar()->FontChange.connect(
|
App::instance()->contextBar()->FontChange.connect(
|
||||||
&WritingTextState::onFontChange, this);
|
&WritingTextState::onFontChange, this);
|
||||||
|
|
||||||
m_entry->setBounds(calcEntryBounds());
|
onEditorResize(editor);
|
||||||
}
|
}
|
||||||
|
|
||||||
WritingTextState::~WritingTextState()
|
WritingTextState::~WritingTextState()
|
||||||
@ -232,8 +300,11 @@ bool WritingTextState::onKeyUp(Editor*, KeyMessage* msg)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WritingTextState::onEditorResize(Editor*)
|
void WritingTextState::onEditorResize(Editor* editor)
|
||||||
{
|
{
|
||||||
|
const gfx::PointF scale(editor->projection().scaleX(),
|
||||||
|
editor->projection().scaleY());
|
||||||
|
m_entry->setScale(scale);
|
||||||
m_entry->setBounds(calcEntryBounds());
|
m_entry->setBounds(calcEntryBounds());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
#ifdef LAF_SKIA
|
#ifdef LAF_SKIA
|
||||||
#include "app/util/shader_helpers.h"
|
#include "app/util/shader_helpers.h"
|
||||||
|
#include "os/skia/skia_helpers.h"
|
||||||
#include "os/skia/skia_surface.h"
|
#include "os/skia/skia_surface.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -52,6 +52,7 @@ Entry::Entry(const int maxsize, const char* format, ...)
|
|||||||
, m_recent_focused(false)
|
, m_recent_focused(false)
|
||||||
, m_lock_selection(false)
|
, m_lock_selection(false)
|
||||||
, m_translate_dead_keys(true)
|
, m_translate_dead_keys(true)
|
||||||
|
, m_scale(1.0f, 1.0f)
|
||||||
{
|
{
|
||||||
enableFlags(CTRL_RIGHT_CLICK);
|
enableFlags(CTRL_RIGHT_CLICK);
|
||||||
|
|
||||||
@ -235,6 +236,16 @@ gfx::Rect Entry::getEntryTextBounds() const
|
|||||||
return onGetEntryTextBounds();
|
return onGetEntryTextBounds();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gfx::Rect Entry::getCharBoxBounds(const int charBoxIndex)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
int x = 0;
|
||||||
|
for (; i<charBoxIndex; ++i) {
|
||||||
|
x += m_boxes[i].width;
|
||||||
|
}
|
||||||
|
return gfx::Rect(x, 0, m_boxes[i].width, textHeight());
|
||||||
|
}
|
||||||
|
|
||||||
bool Entry::onProcessMessage(Message* msg)
|
bool Entry::onProcessMessage(Message* msg)
|
||||||
{
|
{
|
||||||
switch (msg->type()) {
|
switch (msg->type()) {
|
||||||
@ -551,9 +562,9 @@ gfx::Rect Entry::onGetEntryTextBounds() const
|
|||||||
return bounds;
|
return bounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Entry::getCaretFromMouse(MouseMessage* mousemsg)
|
int Entry::getCaretFromMouse(MouseMessage* mouseMsg)
|
||||||
{
|
{
|
||||||
int mouseX = mousemsg->position().x;
|
const int mouseX = mouseMsg->position().x;
|
||||||
if (mouseX < bounds().x+border().left()) {
|
if (mouseX < bounds().x+border().left()) {
|
||||||
// Scroll to the left
|
// Scroll to the left
|
||||||
return std::max(0, m_scroll-1);
|
return std::max(0, m_scroll-1);
|
||||||
@ -569,7 +580,8 @@ int Entry::getCaretFromMouse(MouseMessage* mousemsg)
|
|||||||
indexBox = j+1;
|
indexBox = j+1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int x = bounds().x + border().left() + segmentWidth + m_boxes[indexBox].width / 2;
|
const int x = bounds().x + border().left()
|
||||||
|
+ (segmentWidth + m_boxes[indexBox].width / 2) * m_scale.x;
|
||||||
|
|
||||||
if (mouseX > bounds().x2() - border().right()) {
|
if (mouseX > bounds().x2() - border().right()) {
|
||||||
if (x >= bounds().x2() - border().right()) {
|
if (x >= bounds().x2() - border().right()) {
|
||||||
|
@ -63,6 +63,9 @@ namespace ui {
|
|||||||
void getEntryThemeInfo(int* scroll, int* caret, int* state, Range* range) const;
|
void getEntryThemeInfo(int* scroll, int* caret, int* state, Range* range) const;
|
||||||
gfx::Rect getEntryTextBounds() const;
|
gfx::Rect getEntryTextBounds() const;
|
||||||
|
|
||||||
|
gfx::PointF scale() const { return m_scale; }
|
||||||
|
void setScale(const gfx::PointF& scale) { m_scale = scale; }
|
||||||
|
|
||||||
static gfx::Size sizeHintWithText(Entry* entry,
|
static gfx::Size sizeHintWithText(Entry* entry,
|
||||||
const std::string& text);
|
const std::string& text);
|
||||||
|
|
||||||
@ -70,6 +73,8 @@ namespace ui {
|
|||||||
obs::signal<void()> Change;
|
obs::signal<void()> Change;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
gfx::Rect getCharBoxBounds(int charBoxIndex);
|
||||||
|
|
||||||
// Events
|
// Events
|
||||||
bool onProcessMessage(Message* msg) override;
|
bool onProcessMessage(Message* msg) override;
|
||||||
void onSizeHint(SizeHintEvent& ev) override;
|
void onSizeHint(SizeHintEvent& ev) override;
|
||||||
@ -137,6 +142,11 @@ namespace ui {
|
|||||||
bool m_translate_dead_keys : 1;
|
bool m_translate_dead_keys : 1;
|
||||||
Range m_selecting_words;
|
Range m_selecting_words;
|
||||||
std::unique_ptr<std::string> m_suffix;
|
std::unique_ptr<std::string> m_suffix;
|
||||||
|
|
||||||
|
// Scale (1.0 by default) applied to each axis. Can be used in
|
||||||
|
// case you are going to display/paint the text scaled and want to
|
||||||
|
// convert the mouse position correctly.
|
||||||
|
gfx::PointF m_scale;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ui
|
} // namespace ui
|
||||||
|
Loading…
Reference in New Issue
Block a user