1 | // Aseprite |
2 | // Copyright (C) 2020-2022 Igara Studio S.A. |
3 | // Copyright (C) 2001-2017 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/ui/palettes_listbox.h" |
13 | |
14 | #include "app/app.h" |
15 | #include "app/doc.h" |
16 | #include "app/extensions.h" |
17 | #include "app/modules/palettes.h" |
18 | #include "app/res/palette_resource.h" |
19 | #include "app/res/palettes_loader_delegate.h" |
20 | #include "app/ui/doc_view.h" |
21 | #include "app/ui/editor/editor.h" |
22 | #include "app/ui/icon_button.h" |
23 | #include "app/ui/skin/skin_theme.h" |
24 | #include "app/ui_context.h" |
25 | #include "base/launcher.h" |
26 | #include "doc/palette.h" |
27 | #include "doc/sprite.h" |
28 | #include "os/surface.h" |
29 | #include "ui/graphics.h" |
30 | #include "ui/listitem.h" |
31 | #include "ui/message.h" |
32 | #include "ui/paint_event.h" |
33 | #include "ui/resize_event.h" |
34 | #include "ui/size_hint_event.h" |
35 | #include "ui/tooltips.h" |
36 | #include "ui/view.h" |
37 | |
38 | namespace app { |
39 | |
40 | using namespace ui; |
41 | using namespace app::skin; |
42 | |
43 | static bool is_url_char(int chr) |
44 | { |
45 | return ((chr >= 'a' && chr <= 'z') || |
46 | (chr >= 'A' && chr <= 'Z') || |
47 | (chr >= '0' && chr <= '9') || |
48 | (chr == ':' || chr == '/' || chr == '@' || |
49 | chr == '?' || chr == '!' || chr == '#' || |
50 | chr == '-' || chr == '_' || chr == '~' || |
51 | chr == '.' || chr == ',' || chr == ';' || |
52 | chr == '*' || chr == '+' || chr == '=' || |
53 | chr == '[' || chr == ']' || |
54 | chr == '(' || chr == ')' || |
55 | chr == '$' || chr == '\'')); |
56 | } |
57 | |
58 | class PalettesListItem : public ResourceListItem { |
59 | |
60 | class : public IconButton { |
61 | public: |
62 | (const std::string& ) |
63 | : IconButton(SkinTheme::instance()->parts.iconUserData()) |
64 | , m_comment(comment) { |
65 | setFocusStop(false); |
66 | initTheme(); |
67 | } |
68 | |
69 | private: |
70 | void (InitThemeEvent& ev) override { |
71 | IconButton::onInitTheme(ev); |
72 | |
73 | auto theme = SkinTheme::get(this); |
74 | setBgColor(theme->colors.listitemNormalFace()); |
75 | } |
76 | |
77 | void (Event& ev) override { |
78 | IconButton::onClick(ev); |
79 | |
80 | std::string::size_type j, i = m_comment.find("http" ); |
81 | if (i != std::string::npos) { |
82 | for (j=i+4; j != m_comment.size() && is_url_char(m_comment[j]); ++j) |
83 | ; |
84 | base::launcher::open_url(m_comment.substr(i, j-i)); |
85 | } |
86 | } |
87 | |
88 | std::string ; |
89 | }; |
90 | |
91 | public: |
92 | PalettesListItem(Resource* resource, TooltipManager* tooltips) |
93 | : ResourceListItem(resource) |
94 | , m_comment(nullptr) |
95 | { |
96 | std::string = static_cast<PaletteResource*>(resource)->palette()->comment(); |
97 | if (!comment.empty()) { |
98 | addChild(m_comment = new CommentButton(comment)); |
99 | |
100 | tooltips->addTooltipFor(m_comment, comment, LEFT); |
101 | } |
102 | } |
103 | |
104 | private: |
105 | void onResize(ResizeEvent& ev) override { |
106 | ResourceListItem::onResize(ev); |
107 | |
108 | if (m_comment) { |
109 | auto reqSz = m_comment->sizeHint(); |
110 | m_comment->setBounds( |
111 | gfx::Rect(ev.bounds().x+ev.bounds().w-reqSz.w, |
112 | ev.bounds().y+ev.bounds().h/2-reqSz.h/2, |
113 | reqSz.w, reqSz.h)); |
114 | } |
115 | } |
116 | |
117 | CommentButton* ; |
118 | }; |
119 | |
120 | PalettesListBox::PalettesListBox() |
121 | : ResourcesListBox( |
122 | new ResourcesLoader( |
123 | std::make_unique<PalettesLoaderDelegate>())) |
124 | { |
125 | addChild(&m_tooltips); |
126 | |
127 | m_extPaletteChanges = |
128 | App::instance()->extensions().PalettesChange.connect( |
129 | [this]{ reload(); }); |
130 | m_extPresetsChanges = |
131 | App::instance()->PalettePresetsChange.connect( |
132 | [this]{ reload(); }); |
133 | } |
134 | |
135 | const doc::Palette* PalettesListBox::selectedPalette() |
136 | { |
137 | Resource* resource = selectedResource(); |
138 | if (!resource) |
139 | return NULL; |
140 | |
141 | return static_cast<PaletteResource*>(resource)->palette(); |
142 | } |
143 | |
144 | ResourceListItem* PalettesListBox::onCreateResourceItem(Resource* resource) |
145 | { |
146 | return new PalettesListItem(resource, &m_tooltips); |
147 | } |
148 | |
149 | void PalettesListBox::onResourceChange(Resource* resource) |
150 | { |
151 | const doc::Palette* palette = static_cast<PaletteResource*>(resource)->palette(); |
152 | PalChange(palette); |
153 | } |
154 | |
155 | void PalettesListBox::onPaintResource(Graphics* g, gfx::Rect& bounds, Resource* resource) |
156 | { |
157 | auto theme = SkinTheme::get(this); |
158 | const doc::Palette* palette = static_cast<PaletteResource*>(resource)->palette(); |
159 | os::Surface* tick = theme->parts.checkSelected()->bitmap(0); |
160 | |
161 | // Draw tick (to say "this palette matches the active sprite |
162 | // palette"). |
163 | auto view = UIContext::instance()->activeView(); |
164 | if (view && view->document()) { |
165 | auto docPal = view->document()->sprite()->palette(view->editor()->frame()); |
166 | if (docPal && *docPal == *palette) |
167 | g->drawRgbaSurface(tick, bounds.x, bounds.y+bounds.h/2-tick->height()/2); |
168 | } |
169 | |
170 | bounds.x += tick->width(); |
171 | bounds.w -= tick->width(); |
172 | |
173 | gfx::Rect box( |
174 | bounds.x, bounds.y+bounds.h-6*guiscale(), |
175 | 4*guiscale(), 4*guiscale()); |
176 | |
177 | for (int i=0; i<palette->size(); ++i) { |
178 | doc::color_t c = palette->getEntry(i); |
179 | |
180 | g->fillRect(gfx::rgba( |
181 | doc::rgba_getr(c), |
182 | doc::rgba_getg(c), |
183 | doc::rgba_getb(c)), box); |
184 | |
185 | box.x += box.w; |
186 | } |
187 | } |
188 | |
189 | void PalettesListBox::onResourceSizeHint(Resource* resource, gfx::Size& size) |
190 | { |
191 | size = gfx::Size(0, (2+16+2)*guiscale()); |
192 | } |
193 | |
194 | } // namespace app |
195 | |