| 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 |  | 
|---|