1 | // Aseprite |
2 | // Copyright (C) 2019-2022 Igara Studio S.A. |
3 | // Copyright (C) 2001-2018 David Capello |
4 | // |
5 | // This program is distributed under the terms of |
6 | // the End-User License Agreement for Aseprite. |
7 | |
8 | #ifdef HAVE_CONFIG_H |
9 | #include "config.h" |
10 | #endif |
11 | |
12 | #include "app/app.h" |
13 | #include "app/commands/command.h" |
14 | #include "app/console.h" |
15 | #include "app/context.h" |
16 | #include "app/file_selector.h" |
17 | #include "app/modules/editors.h" |
18 | #include "app/pref/preferences.h" |
19 | #include "app/ui/drop_down_button.h" |
20 | #include "app/ui/editor/editor.h" |
21 | #include "app/ui/font_popup.h" |
22 | #include "app/ui/timeline/timeline.h" |
23 | #include "app/util/freetype_utils.h" |
24 | #include "base/fs.h" |
25 | #include "base/string.h" |
26 | #include "doc/image.h" |
27 | #include "doc/image_ref.h" |
28 | #include "render/dithering.h" |
29 | #include "render/ordered_dither.h" |
30 | #include "render/quantization.h" |
31 | #include "ui/manager.h" |
32 | |
33 | #include "paste_text.xml.h" |
34 | |
35 | namespace app { |
36 | |
37 | static std::string last_text_used; |
38 | |
39 | class PasteTextCommand : public Command { |
40 | public: |
41 | PasteTextCommand(); |
42 | |
43 | protected: |
44 | bool onEnabled(Context* ctx) override; |
45 | void onExecute(Context* ctx) override; |
46 | }; |
47 | |
48 | PasteTextCommand::PasteTextCommand() |
49 | : Command(CommandId::PasteText(), CmdUIOnlyFlag) |
50 | { |
51 | } |
52 | |
53 | bool PasteTextCommand::onEnabled(Context* ctx) |
54 | { |
55 | return ctx->checkFlags(ContextFlags::ActiveDocumentIsWritable | |
56 | ContextFlags::ActiveLayerIsEditable); |
57 | } |
58 | |
59 | class PasteTextWindow : public app::gen::PasteText { |
60 | public: |
61 | PasteTextWindow(const std::string& face, int size, |
62 | bool antialias, |
63 | const app::Color& color) |
64 | : m_face(face) { |
65 | ok()->setEnabled(!m_face.empty()); |
66 | if (!m_face.empty()) |
67 | updateFontFaceButton(); |
68 | |
69 | fontSize()->setTextf("%d" , size); |
70 | fontFace()->Click.connect([this]{ onSelectFontFile(); }); |
71 | fontFace()->DropDownClick.connect([this]{ onSelectSystemFont(); }); |
72 | fontColor()->setColor(color); |
73 | this->antialias()->setSelected(antialias); |
74 | } |
75 | |
76 | std::string faceValue() const { |
77 | return m_face; |
78 | } |
79 | |
80 | int sizeValue() const { |
81 | int size = fontSize()->textInt(); |
82 | size = std::clamp(size, 1, 5000); |
83 | return size; |
84 | } |
85 | |
86 | private: |
87 | void updateFontFaceButton() { |
88 | fontFace()->mainButton() |
89 | ->setTextf("Select Font: %s" , |
90 | base::get_file_title(m_face).c_str()); |
91 | } |
92 | |
93 | void onSelectFontFile() { |
94 | base::paths exts = { "ttf" , "ttc" , "otf" , "dfont" }; |
95 | base::paths face; |
96 | if (!show_file_selector( |
97 | "Select a TrueType Font" , |
98 | m_face, exts, |
99 | FileSelectorType::Open, face)) |
100 | return; |
101 | |
102 | ASSERT(!face.empty()); |
103 | setFontFace(face.front()); |
104 | } |
105 | |
106 | void setFontFace(const std::string& face) { |
107 | m_face = face; |
108 | ok()->setEnabled(true); |
109 | updateFontFaceButton(); |
110 | } |
111 | |
112 | void onSelectSystemFont() { |
113 | if (!m_fontPopup) { |
114 | try { |
115 | m_fontPopup.reset(new FontPopup()); |
116 | m_fontPopup->Load.connect(&PasteTextWindow::setFontFace, this); |
117 | m_fontPopup->Close.connect([this]{ onCloseFontPopup(); }); |
118 | } |
119 | catch (const std::exception& ex) { |
120 | Console::showException(ex); |
121 | return; |
122 | } |
123 | } |
124 | |
125 | if (!m_fontPopup->isVisible()) { |
126 | m_fontPopup->showPopup(display(), fontFace()->bounds()); |
127 | } |
128 | else { |
129 | m_fontPopup->closeWindow(NULL); |
130 | } |
131 | } |
132 | |
133 | void () { |
134 | fontFace()->dropDown()->requestFocus(); |
135 | } |
136 | |
137 | std::string m_face; |
138 | std::unique_ptr<FontPopup> ; |
139 | }; |
140 | |
141 | void PasteTextCommand::onExecute(Context* ctx) |
142 | { |
143 | Editor* editor = current_editor; |
144 | if (editor == NULL) |
145 | return; |
146 | |
147 | Preferences& pref = Preferences::instance(); |
148 | PasteTextWindow window(pref.textTool.fontFace(), |
149 | pref.textTool.fontSize(), |
150 | pref.textTool.antialias(), |
151 | pref.colorBar.fgColor()); |
152 | |
153 | window.userText()->setText(last_text_used); |
154 | |
155 | window.openWindowInForeground(); |
156 | if (window.closer() != window.ok()) |
157 | return; |
158 | |
159 | last_text_used = window.userText()->text(); |
160 | |
161 | bool antialias = window.antialias()->isSelected(); |
162 | std::string faceName = window.faceValue(); |
163 | int size = window.sizeValue(); |
164 | size = std::clamp(size, 1, 999); |
165 | pref.textTool.fontFace(faceName); |
166 | pref.textTool.fontSize(size); |
167 | pref.textTool.antialias(antialias); |
168 | |
169 | try { |
170 | std::string text = window.userText()->text(); |
171 | app::Color appColor = window.fontColor()->getColor(); |
172 | doc::color_t color = doc::rgba(appColor.getRed(), |
173 | appColor.getGreen(), |
174 | appColor.getBlue(), |
175 | appColor.getAlpha()); |
176 | |
177 | doc::ImageRef image(render_text(faceName, size, text, color, antialias)); |
178 | if (image) { |
179 | Sprite* sprite = editor->sprite(); |
180 | if (image->pixelFormat() != sprite->pixelFormat()) { |
181 | RgbMap* rgbmap = sprite->rgbMap(editor->frame()); |
182 | image.reset( |
183 | render::convert_pixel_format( |
184 | image.get(), NULL, sprite->pixelFormat(), |
185 | render::Dithering(), |
186 | rgbmap, sprite->palette(editor->frame()), |
187 | false, |
188 | sprite->transparentColor())); |
189 | } |
190 | |
191 | // TODO we don't support pasting text in multiple cels at the |
192 | // moment, so we clear the range here (same as in |
193 | // clipboard::paste()) |
194 | if (auto timeline = App::instance()->timeline()) |
195 | timeline->clearAndInvalidateRange(); |
196 | |
197 | editor->pasteImage(image.get()); |
198 | } |
199 | } |
200 | catch (const std::exception& ex) { |
201 | Console::showException(ex); |
202 | } |
203 | } |
204 | |
205 | Command* CommandFactory::createPasteTextCommand() |
206 | { |
207 | return new PasteTextCommand; |
208 | } |
209 | |
210 | } // namespace app |
211 | |