1// Aseprite
2// Copyright (C) 2020-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/ui/palette_popup.h"
13
14#include "app/commands/cmd_set_palette.h"
15#include "app/commands/commands.h"
16#include "app/launcher.h"
17#include "app/match_words.h"
18#include "app/res/palettes_loader_delegate.h"
19#include "app/res/resource.h"
20#include "app/ui/palettes_listbox.h"
21#include "app/ui/search_entry.h"
22#include "app/ui_context.h"
23#include "ui/box.h"
24#include "ui/button.h"
25#include "ui/fit_bounds.h"
26#include "ui/scale.h"
27#include "ui/theme.h"
28#include "ui/view.h"
29
30#include "palette_popup.xml.h"
31
32namespace app {
33
34using namespace ui;
35
36PalettePopup::PalettePopup()
37 : PopupWindow("Palettes", ClickBehavior::CloseOnClickInOtherWindow)
38 , m_popup(new gen::PalettePopup())
39{
40 setAutoRemap(false);
41 setEnterBehavior(EnterBehavior::DoNothingOnEnter);
42
43 addChild(m_popup);
44
45 m_paletteListBox.DoubleClickItem.connect([this]{ onLoadPal(); });
46 m_paletteListBox.FinishLoading.connect([this]{ onSearchChange(); });
47 m_popup->search()->Change.connect([this]{ onSearchChange(); });
48 m_popup->loadPal()->Click.connect([this]{ onLoadPal(); });
49 m_popup->openFolder()->Click.connect([this]{ onOpenFolder(); });
50
51 m_popup->view()->attachToView(&m_paletteListBox);
52
53 m_paletteListBox.PalChange.connect(&PalettePopup::onPalChange, this);
54
55 InitTheme.connect(
56 [this]{
57 setBorder(gfx::Border(4*guiscale()));
58 });
59 initTheme();
60}
61
62void PalettePopup::showPopup(ui::Display* display,
63 const gfx::Rect& buttonPos)
64{
65 m_popup->loadPal()->setEnabled(false);
66 m_popup->openFolder()->setEnabled(false);
67 m_paletteListBox.selectChild(NULL);
68
69 fit_bounds(display, this,
70 gfx::Rect(buttonPos.x, buttonPos.y2(), 32, 32),
71 [](const gfx::Rect& workarea,
72 gfx::Rect& bounds,
73 std::function<gfx::Rect(Widget*)> getWidgetBounds) {
74 bounds.w = workarea.w/2;
75 bounds.h = workarea.h*3/4;
76 });
77
78 openWindowInForeground();
79}
80
81void PalettePopup::onPalChange(const doc::Palette* palette)
82{
83 const bool state =
84 (UIContext::instance()->activeDocument() &&
85 palette != nullptr);
86
87 m_popup->loadPal()->setEnabled(state);
88 m_popup->openFolder()->setEnabled(state);
89}
90
91void PalettePopup::onSearchChange()
92{
93 MatchWords match(m_popup->search()->text());
94 bool selected = false;
95
96 for (auto child : m_paletteListBox.children()) {
97 if (dynamic_cast<ResourceListItem*>(child)) {
98 const bool vis = match(child->text());
99 child->setVisible(vis);
100 if (!selected && vis) {
101 selected = true;
102 m_paletteListBox.selectChild(child);
103 }
104 }
105 else
106 child->setVisible(true);
107 }
108
109 if (!selected)
110 m_paletteListBox.selectChild(nullptr);
111
112 m_popup->view()->layout();
113}
114
115void PalettePopup::onLoadPal()
116{
117 const doc::Palette* palette = m_paletteListBox.selectedPalette();
118 if (!palette)
119 return;
120
121 SetPaletteCommand* cmd = static_cast<SetPaletteCommand*>(
122 Commands::instance()->byId(CommandId::SetPalette()));
123 cmd->setPalette(palette);
124 UIContext::instance()->executeCommandFromMenuOrShortcut(cmd);
125
126 m_paletteListBox.requestFocus();
127 m_paletteListBox.invalidate();
128}
129
130void PalettePopup::onOpenFolder()
131{
132 Resource* res = m_paletteListBox.selectedResource();
133 if (!res)
134 return;
135
136 launcher::open_folder(res->path());
137}
138
139} // namespace app
140