1 | // Aseprite |
2 | // Copyright (C) 2018-2021 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 | #ifndef APP_UI_PALETTE_VIEW_H_INCLUDED |
9 | #define APP_UI_PALETTE_VIEW_H_INCLUDED |
10 | #pragma once |
11 | |
12 | #include "app/color.h" |
13 | #include "app/context_observer.h" |
14 | #include "app/ui/color_source.h" |
15 | #include "app/ui/marching_ants.h" |
16 | #include "app/ui/tile_source.h" |
17 | #include "doc/palette_picks.h" |
18 | #include "doc/tile.h" |
19 | #include "obs/connection.h" |
20 | #include "obs/signal.h" |
21 | #include "ui/event.h" |
22 | #include "ui/mouse_button.h" |
23 | #include "ui/widget.h" |
24 | |
25 | #include <memory> |
26 | #include <vector> |
27 | |
28 | namespace doc { |
29 | class Palette; |
30 | class Tileset; |
31 | } |
32 | |
33 | namespace app { |
34 | |
35 | enum class PaletteViewModification { |
36 | CLEAR, |
37 | DRAGANDDROP, |
38 | RESIZE, |
39 | }; |
40 | |
41 | class PaletteView; |
42 | |
43 | class PaletteViewDelegate { |
44 | public: |
45 | virtual ~PaletteViewDelegate() { } |
46 | virtual bool onIsPaletteViewActive(PaletteView* paletteView) const { return false; } |
47 | virtual void onPaletteViewIndexChange(int index, ui::MouseButton button) { } |
48 | virtual void onPaletteViewModification(const doc::Palette* newPalette, PaletteViewModification mod) { } |
49 | virtual void onPaletteViewChangeSize(PaletteView* paletteView, int boxsize) { } |
50 | virtual void onPaletteViewPasteColors( |
51 | const doc::Palette* fromPal, const doc::PalettePicks& from, const doc::PalettePicks& to) { } |
52 | virtual app::Color onPaletteViewGetForegroundIndex() { return app::Color::fromMask(); } |
53 | virtual app::Color onPaletteViewGetBackgroundIndex() { return app::Color::fromMask(); } |
54 | virtual doc::tile_index onPaletteViewGetForegroundTile() { return -1; } |
55 | virtual doc::tile_index onPaletteViewGetBackgroundTile() { return -1; } |
56 | virtual void onTilesViewClearTiles(const doc::PalettePicks& tiles) { } |
57 | virtual void onTilesViewResize(const int newSize) { } |
58 | virtual void onTilesViewDragAndDrop(doc::Tileset* tileset, |
59 | doc::PalettePicks& picks, |
60 | int& currentEntry, |
61 | const int beforeIndex, |
62 | const bool isCopy) { } |
63 | virtual void onTilesViewIndexChange(int index, ui::MouseButton button) { } |
64 | }; |
65 | |
66 | class AbstractPaletteViewAdapter; |
67 | class PaletteViewAdapter; |
68 | class TilesetViewAdapter; |
69 | |
70 | class PaletteView : public ui::Widget |
71 | , public MarchingAnts |
72 | , public IColorSource |
73 | , public ITileSource |
74 | , public ContextObserver { |
75 | friend class PaletteViewAdapter; |
76 | friend class TilesetViewAdapter; |
77 | public: |
78 | enum PaletteViewStyle { |
79 | SelectOneColor, |
80 | FgBgColors, |
81 | FgBgTiles, |
82 | }; |
83 | |
84 | PaletteView(bool editable, PaletteViewStyle style, PaletteViewDelegate* delegate, int boxsize); |
85 | ~PaletteView(); |
86 | |
87 | PaletteViewDelegate* delegate() { return m_delegate; } |
88 | |
89 | bool isEditable() const { return m_editable; } |
90 | bool isPalette() const { return m_style != FgBgTiles; } |
91 | bool isTiles() const { return m_style == FgBgTiles; } |
92 | |
93 | int getColumns() const { return m_columns; } |
94 | void setColumns(int columns); |
95 | |
96 | void deselect(); |
97 | void selectColor(int index); |
98 | void selectExactMatchColor(const app::Color& color); |
99 | void selectRange(int index1, int index2); |
100 | |
101 | int getSelectedEntry() const; |
102 | bool getSelectedRange(int& index1, int& index2) const; |
103 | void getSelectedEntries(doc::PalettePicks& entries) const; |
104 | int getSelectedEntriesCount() const; |
105 | void setSelectedEntries(const doc::PalettePicks& entries); |
106 | |
107 | doc::Tileset* tileset() const; |
108 | |
109 | // IColorSource |
110 | app::Color getColorByPosition(const gfx::Point& pos) override; |
111 | |
112 | // ITileSource |
113 | doc::tile_t getTileByPosition(const gfx::Point& pos) override; |
114 | |
115 | // ContextObserver impl |
116 | void onActiveSiteChange(const Site& site) override; |
117 | |
118 | int getBoxSize() const; |
119 | void setBoxSize(double boxsize); |
120 | |
121 | void clearSelection(); |
122 | void cutToClipboard(); |
123 | void copyToClipboard(); |
124 | void pasteFromClipboard(); |
125 | void discardClipboardSelection(); |
126 | |
127 | obs::signal<void(ui::Message*)> FocusOrClick; |
128 | |
129 | protected: |
130 | bool onProcessMessage(ui::Message* msg) override; |
131 | void onPaint(ui::PaintEvent& ev) override; |
132 | void onResize(ui::ResizeEvent& ev) override; |
133 | void onSizeHint(ui::SizeHintEvent& ev) override; |
134 | void onDrawMarchingAnts() override; |
135 | |
136 | private: |
137 | |
138 | enum class State { |
139 | WAITING, |
140 | SELECTING_COLOR, |
141 | DRAGGING_OUTLINE, |
142 | RESIZING_PALETTE, |
143 | }; |
144 | |
145 | struct Hit { |
146 | enum Part { |
147 | NONE, |
148 | COLOR, |
149 | OUTLINE, |
150 | RESIZE_HANDLE, |
151 | POSSIBLE_COLOR, |
152 | }; |
153 | Part part; |
154 | int color; |
155 | |
156 | Hit(Part part, int color = -1) : part(part), color(color) { |
157 | } |
158 | |
159 | bool operator==(const Hit& hit) const { |
160 | return ( |
161 | part == hit.part && |
162 | color == hit.color); |
163 | } |
164 | bool operator!=(const Hit& hit) const { |
165 | return !operator==(hit); |
166 | } |
167 | }; |
168 | |
169 | void update_scroll(int color); |
170 | void onAppPaletteChange(); |
171 | gfx::Rect getPaletteEntryBounds(int index) const; |
172 | Hit hitTest(const gfx::Point& pos); |
173 | void dropColors(int beforeIndex); |
174 | void getEntryBoundsAndClip(int i, |
175 | const doc::PalettePicks& entries, |
176 | const int outlineWidth, |
177 | gfx::Rect& box, gfx::Rect& clip) const; |
178 | bool pickedXY(const doc::PalettePicks& entries, int i, int dx, int dy) const; |
179 | void updateCopyFlag(ui::Message* msg); |
180 | void setCursor(); |
181 | void setStatusBar(); |
182 | doc::Palette* currentPalette() const; |
183 | int findExactIndex(const app::Color& color) const; |
184 | void setNewPalette(doc::Palette* oldPalette, doc::Palette* newPalette, |
185 | PaletteViewModification mod); |
186 | int boxSizePx() const; |
187 | void updateBorderAndChildSpacing(); |
188 | |
189 | State m_state; |
190 | bool m_editable; |
191 | PaletteViewStyle m_style; |
192 | PaletteViewDelegate* m_delegate; |
193 | std::unique_ptr<AbstractPaletteViewAdapter> m_adapter; |
194 | int m_columns; |
195 | double m_boxsize; |
196 | int m_currentEntry; |
197 | int m_rangeAnchor; |
198 | doc::PalettePicks m_selectedEntries; |
199 | bool m_isUpdatingColumns; |
200 | obs::scoped_connection m_palConn; |
201 | obs::scoped_connection m_csConn; |
202 | obs::scoped_connection m_sepConn; |
203 | Hit m_hot; |
204 | bool m_copy; |
205 | bool m_withSeparator; |
206 | }; |
207 | |
208 | } // namespace app |
209 | |
210 | #endif |
211 | |