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