diff --git a/emulator/emulator.hpp b/emulator/emulator.hpp index ad1e105b..7e8466ee 100644 --- a/emulator/emulator.hpp +++ b/emulator/emulator.hpp @@ -8,7 +8,7 @@ using namespace nall; namespace Emulator { static const string Name = "higan"; - static const string Version = "094.42"; + static const string Version = "094.43"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "http://byuu.org/"; diff --git a/hiro/components.hpp b/hiro/components.hpp index be188659..87131829 100644 --- a/hiro/components.hpp +++ b/hiro/components.hpp @@ -9,15 +9,17 @@ * As such, this file is really only meant for disabling individual widgets or menu items. */ -#define Hiro_Application - #define Hiro_Color +#define Hiro_Gradient #define Hiro_Alignment +#define Hiro_Cursor #define Hiro_Position #define Hiro_Size #define Hiro_Geometry - #define Hiro_Font +#define Hiro_Image + +#define Hiro_Application #define Hiro_Desktop #define Hiro_Monitor #define Hiro_Keyboard @@ -63,7 +65,7 @@ #define Hiro_ProgressBar #define Hiro_RadioButton #define Hiro_RadioLabel -#define Hiro_SourceView +#define Hiro_SourceEdit #define Hiro_TabFrame #define Hiro_TextEdit #define Hiro_TreeView @@ -86,6 +88,6 @@ #if defined(HIRO_WINDOWS) || defined(HIRO_QT) #undef Hiro_Console #undef Hiro_IconView - #undef Hiro_SourceView + #undef Hiro_SourceEdit #undef Hiro_TreeView #endif diff --git a/hiro/core/action/menu-item.cpp b/hiro/core/action/menu-item.cpp index 8f5ce5fe..3b09a8c4 100644 --- a/hiro/core/action/menu-item.cpp +++ b/hiro/core/action/menu-item.cpp @@ -10,8 +10,8 @@ auto mMenuItem::doActivate() const -> void { if(state.onActivate) return state.onActivate(); } -auto mMenuItem::icon() const -> image { - return state.icon; +auto mMenuItem::image() const -> Image { + return state.image; } auto mMenuItem::onActivate(const function& callback) -> type& { @@ -19,9 +19,9 @@ auto mMenuItem::onActivate(const function& callback) -> type& { return *this; } -auto mMenuItem::setIcon(const image& icon) -> type& { - state.icon = icon; - signal(setIcon, icon); +auto mMenuItem::setImage(const Image& image) -> type& { + state.image = image; + signal(setImage, image); return *this; } diff --git a/hiro/core/action/menu-radio-item.cpp b/hiro/core/action/menu-radio-item.cpp index 36d4eada..a8c74e50 100644 --- a/hiro/core/action/menu-radio-item.cpp +++ b/hiro/core/action/menu-radio-item.cpp @@ -39,9 +39,8 @@ auto mMenuRadioItem::setChecked() -> type& { } auto mMenuRadioItem::setGroup(sGroup group) -> type& { - state.group = group; + state.group = group ? group : Group{&instance}; signal(setGroup, group); - if(group && group->objectCount() == 1) setChecked(); return *this; } diff --git a/hiro/core/action/menu.cpp b/hiro/core/action/menu.cpp index 97f3f797..0549185a 100644 --- a/hiro/core/action/menu.cpp +++ b/hiro/core/action/menu.cpp @@ -33,8 +33,8 @@ auto mMenu::append(sAction action) -> type& { return *this; } -auto mMenu::icon() const -> image { - return state.icon; +auto mMenu::image() const -> Image { + return state.image; } auto mMenu::remove(sAction action) -> type& { @@ -51,9 +51,16 @@ auto mMenu::reset() -> type& { return *this; } -auto mMenu::setIcon(const image& icon) -> type& { - state.icon = icon; - signal(setIcon, icon); +auto mMenu::setImage(const Image& image) -> type& { + state.image = image; + signal(setImage, image); + return *this; +} + +auto mMenu::setParent(mObject* parent, signed offset) -> type& { + for(auto n : rrange(state.actions)) state.actions[n]->destruct(); + mObject::setParent(parent, offset); + for(auto& action : state.actions) action->setParent(this, action->offset()); return *this; } diff --git a/hiro/core/alignment.cpp b/hiro/core/alignment.cpp index c682d000..210a0aa6 100644 --- a/hiro/core/alignment.cpp +++ b/hiro/core/alignment.cpp @@ -17,6 +17,10 @@ auto Alignment::horizontal() const -> double { return state.horizontal; } +auto Alignment::reset() -> type& { + return setAlignment(-1.0, -1.0); +} + auto Alignment::setAlignment(double horizontal, double vertical) -> type& { state.horizontal = horizontal; state.vertical = vertical; diff --git a/hiro/core/application.cpp b/hiro/core/application.cpp index 1e8f84d1..949f5d3f 100644 --- a/hiro/core/application.cpp +++ b/hiro/core/application.cpp @@ -6,7 +6,7 @@ auto Application::doMain() -> void { if(state.onMain) return state.onMain(); } -auto Application::font() -> string { +auto Application::font() -> Font { return state.font; } @@ -35,7 +35,7 @@ auto Application::quit() -> void { return pApplication::quit(); } -auto Application::setFont(const string& font) -> void { +auto Application::setFont(const Font& font) -> void { state.font = font; } diff --git a/hiro/core/color.cpp b/hiro/core/color.cpp index 012545c9..63d8f005 100644 --- a/hiro/core/color.cpp +++ b/hiro/core/color.cpp @@ -9,7 +9,7 @@ Color::Color(signed red, signed green, signed blue, signed alpha) { } Color::operator bool() const { - return !empty(); + return state.red || state.green || state.blue || state.alpha; } auto Color::operator==(const Color& source) const -> bool { @@ -28,10 +28,6 @@ auto Color::blue() const -> uint8_t { return state.blue; } -auto Color::empty() const -> bool { - return state.red == 0 && state.green == 0 && state.blue == 0 && state.alpha == 0; -} - auto Color::green() const -> uint8_t { return state.green; } @@ -40,6 +36,10 @@ auto Color::red() const -> uint8_t { return state.red; } +auto Color::reset() -> type& { + return setColor(0, 0, 0, 0); +} + auto Color::setAlpha(signed alpha) -> type& { state.alpha = max(0, min(255, alpha)); return *this; @@ -73,7 +73,7 @@ auto Color::setRed(signed red) -> type& { } auto Color::value() const -> uint32_t { - return (state.alpha << 24) + (state.red << 16) + (state.green << 8) + (state.blue << 0); + return state.alpha << 24 | state.red << 16 | state.green << 8 | state.blue << 0; } #endif diff --git a/hiro/core/core.cpp b/hiro/core/core.cpp index d25f6c6a..c9bdb6b1 100644 --- a/hiro/core/core.cpp +++ b/hiro/core/core.cpp @@ -31,13 +31,17 @@ using namespace nall; (delegate ? self()->function(__VA_ARGS__) : decltype(self()->function(__VA_ARGS__))()) namespace hiro { - #include "application.cpp" #include "color.cpp" + #include "gradient.cpp" #include "alignment.cpp" + #include "cursor.cpp" #include "position.cpp" #include "size.cpp" #include "geometry.cpp" #include "font.cpp" + #include "image.cpp" + + #include "application.cpp" #include "desktop.cpp" #include "monitor.cpp" #include "keyboard.cpp" diff --git a/hiro/core/core.hpp b/hiro/core/core.hpp index ebee01fd..4f83ec6d 100644 --- a/hiro/core/core.hpp +++ b/hiro/core/core.hpp @@ -13,7 +13,6 @@ #include using nall::function; -using nall::image; using nall::lstring; using nall::maybe; using nall::shared_pointer; @@ -23,6 +22,9 @@ using nall::vector; namespace hiro { +struct Font; +struct Keyboard; + #define Declare(Name) \ struct Name; \ struct m##Name; \ @@ -30,11 +32,9 @@ namespace hiro { using s##Name = shared_pointer; \ using w##Name = shared_pointer_weak; \ -Declare(Keyboard) Declare(Object) Declare(Group) Declare(Timer) -Declare(Hotkey) Declare(Window) Declare(StatusBar) Declare(MenuBar) @@ -83,64 +83,8 @@ Declare(Viewport) #undef Declare -enum class Edge : unsigned { Top, Bottom, Left, Right }; - enum class Orientation : unsigned { Horizontal, Vertical }; -enum class Placement : unsigned { Top, Bottom, Left, Right }; - -#if defined(Hiro_Application) -struct Application { - Application() = delete; - - static auto doMain() -> void; - static auto font() -> string; - static auto name() -> string; - static auto onMain(const function& callback = {}) -> void; - static auto run() -> void; - static auto pendingEvents() -> bool; - static auto processEvents() -> void; - static auto quit() -> void; - static auto setFont(const string& font = "") -> void; - static auto setName(const string& name = "") -> void; - - struct Windows { - static auto doModalChange(bool modal) -> void; - static auto onModalChange(const function& callback = {}) -> void; - }; - - struct Cocoa { - static auto doAbout() -> void; - static auto doActivate() -> void; - static auto doPreferences() -> void; - static auto doQuit() -> void; - static auto onAbout(const function& callback = {}) -> void; - static auto onActivate(const function& callback = {}) -> void; - static auto onPreferences(const function& callback = {}) -> void; - static auto onQuit(const function& callback = {}) -> void; - }; - -//private: - struct State { - string font; - string name; - function onMain; - bool quit = false; - - struct Windows { - function onModalChange; - } windows; - - struct Cocoa { - function onAbout; - function onActivate; - function onPreferences; - function onQuit; - } cocoa; - }; - static State state; - static auto initialize() -> void; -}; -#endif +enum class Navigation : unsigned { Top, Bottom, Left, Right }; #if defined(Hiro_Color) struct Color { @@ -155,9 +99,9 @@ struct Color { auto alpha() const -> uint8_t; auto blue() const -> uint8_t; - auto empty() const -> bool; auto green() const -> uint8_t; auto red() const -> uint8_t; + auto reset() -> type&; auto setAlpha(signed alpha) -> type&; auto setBlue(signed blue) -> type&; auto setColor(Color color = {}) -> type&; @@ -168,10 +112,31 @@ struct Color { //private: struct State { - signed red; - signed green; - signed blue; - signed alpha; + uint8_t red; + uint8_t green; + uint8_t blue; + uint8_t alpha; + } state; +}; +#endif + +#if defined(Hiro_Gradient) +struct Gradient { + using type = Gradient; + + Gradient(); + + explicit operator bool() const; + auto operator==(const Gradient& source) const -> bool; + auto operator!=(const Gradient& source) const -> bool; + + auto setBilinear(Color topLeft, Color topRight, Color bottomLeft, Color bottomRight) -> type&; + auto setHorizontal(Color left, Color right) -> type&; + auto setVertical(Color top, Color bottom) -> type&; + +//private: + struct State { + vector colors; } state; }; #endif @@ -188,6 +153,7 @@ struct Alignment { auto operator!=(const Alignment& source) const -> bool; auto horizontal() const -> double; + auto reset() -> type&; auto setAlignment(double horizontal = -1.0, double vertical = 0.5) -> type&; auto setHorizontal(double horizontal) -> type&; auto setVertical(double vertical) -> type&; @@ -195,8 +161,32 @@ struct Alignment { //private: struct State { - double horizontal; - double vertical; + float horizontal; + float vertical; + } state; +}; +#endif + +#if defined(Hiro_Cursor) +struct Cursor { + using type = Cursor; + + Cursor(signed offset = 0, signed length = 0); + + explicit operator bool() const; + auto operator==(const Cursor& source) const -> bool; + auto operator!=(const Cursor& source) const -> bool; + + auto length() const -> signed; + auto offset() const -> signed; + auto setCursor(signed offset = 0, signed length = 0) -> type&; + auto setLength(signed length = 0) -> type&; + auto setOffset(signed offset = 0) -> type&; + +//private: + struct State { + signed offset; + signed length; } state; }; #endif @@ -208,9 +198,11 @@ struct Position { Position(); Position(signed x, signed y); + explicit operator bool() const; auto operator==(const Position& source) const -> bool; auto operator!=(const Position& source) const -> bool; + auto reset() -> type&; auto setPosition(Position position = {}) -> type&; auto setPosition(signed x, signed y) -> type&; auto setX(signed x) -> type&; @@ -233,10 +225,12 @@ struct Size { Size(); Size(signed width, signed height); + explicit operator bool() const; auto operator==(const Size& source) const -> bool; auto operator!=(const Size& source) const -> bool; auto height() const -> signed; + auto reset() -> type&; auto setHeight(signed height) -> type&; auto setSize(Size source = {}) -> type&; auto setSize(signed width, signed height) -> type&; @@ -263,11 +257,13 @@ struct Geometry { Geometry(signed x, signed y, signed width, signed height); Geometry(const string& text); + explicit operator bool() const; auto operator==(const Geometry& source) const -> bool; auto operator!=(const Geometry& source) const -> bool; auto height() const -> signed; auto position() const -> Position; + auto reset() -> type&; auto setGeometry(Geometry geometry = {}) -> type&; auto setGeometry(Position position, Size size) -> type&; auto setGeometry(signed x, signed y, signed width, signed height) -> type&; @@ -299,8 +295,7 @@ struct Geometry { struct Font { using type = Font; - Font(); - Font(const string& family, unsigned size = 0); + Font(const string& family = "", unsigned size = 0); explicit operator bool() const; auto operator==(const Font& source) const -> bool; @@ -309,27 +304,143 @@ struct Font { auto bold() const -> bool; auto family() const -> string; auto italic() const -> bool; + auto reset() -> type&; auto setBold(bool bold = true) -> type&; auto setFamily(const string& family = "") -> type&; auto setItalic(bool italic = true) -> type&; auto setSize(unsigned size = 0) -> type&; auto size() const -> unsigned; + auto size(const string& text) const -> Size; - static auto serif(unsigned size = 0, const string& style = "") -> string; - static auto sans(unsigned size = 0, const string& style = "") -> string; - static auto monospace(unsigned size = 0, const string& style = "") -> string; - static auto size(const string& font, const string& text = " ") -> Size; + static const string Sans; + static const string Serif; + static const string Mono; //private: struct State { string family; - unsigned size = 0; - bool bold = false; - bool italic = false; + unsigned size; + bool bold; + bool italic; } state; }; #endif +#if defined(Hiro_Image) +struct Image { + using type = Image; + + Image(); + Image(const Image& source); + Image(const string& source); + Image(const vector& source); + Image(const uint32_t* data, Size size); + ~Image(); + + explicit operator bool() const; + auto operator=(const Image& source) -> type&; + + auto data() const -> uint32_t*; + auto height() const -> signed; + auto reset() -> type&; + auto setImage(const uint32_t* data, Size size) -> type&; + auto setSize(Size size = {}) -> type&; + auto size() const -> Size; + auto width() const -> signed; + +//private: + struct State { + uint32_t* data = nullptr; + Size size; + } state; +}; +#endif + +#if defined(Hiro_Hotkey) +struct Hotkey { + using type = Hotkey; + + Hotkey(); + Hotkey(const string& sequence); + + explicit operator bool() const; + auto operator==(const Hotkey& source) const -> bool; + auto operator!=(const Hotkey& source) const -> bool; + + auto doPress() const -> void; + auto doRelease() const -> void; + auto onPress(const function& callback = {}) -> type&; + auto onRelease(const function& callback = {}) -> type&; + auto reset() -> type&; + auto sequence() const -> string; + auto setSequence(const string& sequence = "") -> type&; + +//private: + struct State { + bool active = false; + vector keys; + function onPress; + function onRelease; + string sequence; + }; + shared_pointer state; +}; +#endif + +#if defined(Hiro_Application) +struct Application { + Application() = delete; + + static auto doMain() -> void; + static auto font() -> Font; + static auto name() -> string; + static auto onMain(const function& callback = {}) -> void; + static auto run() -> void; + static auto pendingEvents() -> bool; + static auto processEvents() -> void; + static auto quit() -> void; + static auto setFont(const Font& font = {}) -> void; + static auto setName(const string& name = "") -> void; + + struct Windows { + static auto doModalChange(bool modal) -> void; + static auto onModalChange(const function& callback = {}) -> void; + }; + + struct Cocoa { + static auto doAbout() -> void; + static auto doActivate() -> void; + static auto doPreferences() -> void; + static auto doQuit() -> void; + static auto onAbout(const function& callback = {}) -> void; + static auto onActivate(const function& callback = {}) -> void; + static auto onPreferences(const function& callback = {}) -> void; + static auto onQuit(const function& callback = {}) -> void; + }; + +//private: + struct State { + Font font; + string name; + function onMain; + bool quit = false; + + struct Windows { + function onModalChange; + } windows; + + struct Cocoa { + function onAbout; + function onActivate; + function onPreferences; + function onQuit; + } cocoa; + }; + static State state; + static auto initialize() -> void; +}; +#endif + #if defined(Hiro_Desktop) struct Desktop { Desktop() = delete; @@ -353,19 +464,20 @@ struct Monitor { struct Keyboard { Keyboard() = delete; - static auto append(sHotkey hotkey) -> void; + static auto append(Hotkey hotkey) -> void; static auto hotkey(unsigned position) -> Hotkey; - static auto hotkeys() -> unsigned; + static auto hotkeyCount() -> unsigned; + static auto hotkeys() -> vector; static auto poll() -> vector; static auto pressed(const string& key) -> bool; static auto released(const string& key) -> bool; - static auto remove(sHotkey hotkey) -> void; + static auto remove(Hotkey hotkey) -> void; static const vector keys; //private: struct State { - vector hotkeys; + vector hotkeys; }; static State state; }; @@ -439,6 +551,7 @@ struct MessageWindow { auto self() const -> const p##Name* { return (const p##Name*)delegate; } \ auto bind(const s##Name& instance) -> void { \ this->instance = instance; \ + setGroup(); \ if(!abstract()) construct(); \ } \ auto unbind() -> void { \ @@ -463,7 +576,7 @@ struct mObject { auto adjustOffset(signed displacement) -> type&; auto enabled(bool recursive = false) const -> bool; virtual auto focused() const -> bool; - auto font(bool recursive = false) const -> string; + auto font(bool recursive = false) const -> Font; virtual auto group() const -> Group; auto offset() const -> signed; auto parent() const -> mObject*; @@ -488,7 +601,7 @@ struct mObject { virtual auto reset() -> type&; virtual auto setEnabled(bool enabled = true) -> type&; virtual auto setFocused() -> type&; - virtual auto setFont(const string& font = "") -> type&; + virtual auto setFont(const Font& font = {}) -> type&; virtual auto setGroup(sGroup group = {}) -> type&; virtual auto setParent(mObject* parent = nullptr, signed offset = -1) -> type&; virtual auto setVisible(bool visible = true) -> type&; @@ -497,7 +610,7 @@ struct mObject { //private: struct State { bool enabled = true; - string font; + Font font; signed offset = -1; mObject* parent = nullptr; bool visible = true; @@ -529,32 +642,6 @@ struct mGroup : mObject { }; #endif -#if defined(Hiro_Hotkey) -struct mHotkey : mObject { - Declare(Hotkey) - - auto doPress() const -> void; - auto doRelease() const -> void; - auto onPress(const function& callback = {}) -> type&; - auto onRelease(const function& callback = {}) -> type&; - auto owner() const -> wObject; - auto remove() -> type& override; - auto sequence() const -> string; - auto setOwner(sObject owner) -> type&; - auto setSequence(const string& sequence = "") -> type&; - -//private: - struct State { - bool active = false; - vector keys; - function onPress; - function onRelease; - wObject owner; - string sequence; - } state; -}; -#endif - #if defined(Hiro_Timer) struct mTimer : mObject { Declare(Timer) @@ -672,7 +759,7 @@ struct mMenuBar : mObject { auto remove() -> type& override; auto remove(sMenu menu) -> type&; auto reset() -> type&; -//TODO setParent + auto setParent(mObject* parent = nullptr, signed offset = -1) -> type& override; //private: struct State { @@ -727,18 +814,18 @@ struct mMenu : mAction { auto actionCount() const -> unsigned; auto actions() const -> vector; auto append(sAction action) -> type&; - auto icon() const -> image; + auto image() const -> Image; auto remove(sAction action) -> type&; auto reset() -> type&; - auto setIcon(const image& icon = {}) -> type&; -//TODO setParent + auto setImage(const Image& image = {}) -> type&; + auto setParent(mObject* parent = nullptr, signed offset = -1) -> type& override; auto setText(const string& text = "") -> type&; auto text() const -> string; //private: struct State { vector actions; - image icon; + Image image; string text; } state; @@ -761,15 +848,15 @@ struct mMenuItem : mAction { Declare(MenuItem) auto doActivate() const -> void; - auto icon() const -> image; + auto image() const -> Image; auto onActivate(const function& callback = {}) -> type&; - auto setIcon(const image& icon = {}) -> type&; + auto setImage(const Image& image = {}) -> type&; auto setText(const string& text = "") -> type&; auto text() const -> string; //private: struct State { - image icon; + Image image; function onActivate; string text; } state; @@ -877,11 +964,11 @@ struct mButton : mWidget { auto bordered() const -> bool; auto doActivate() const -> void; - auto icon() const -> image; + auto image() const -> Image; auto onActivate(const function& callback = {}) -> type&; auto orientation() const -> Orientation; auto setBordered(bool bordered = true) -> type&; - auto setIcon(const image& icon = {}) -> type&; + auto setImage(const Image& image = {}) -> type&; auto setOrientation(Orientation orientation = Orientation::Horizontal) -> type&; auto setText(const string& text = "") -> type&; auto text() const -> string; @@ -889,7 +976,7 @@ struct mButton : mWidget { //private: struct State { bool bordered = true; - image icon; + Image image; function onActivate; Orientation orientation = Orientation::Horizontal; string text; @@ -909,36 +996,32 @@ struct mCanvas : mWidget { auto doMouseMove(Position position) const -> void; auto doMousePress(Mouse::Button button) const -> void; auto doMouseRelease(Mouse::Button button) const -> void; - auto gradient() const -> vector; - auto icon() const -> image; + auto gradient() const -> Gradient; + auto image() const -> Image; auto onDrop(const function& callback = {}) -> type&; auto onMouseLeave(const function& callback = {}) -> type&; auto onMouseMove(const function& callback = {}) -> type&; auto onMousePress(const function& callback = {}) -> type&; auto onMouseRelease(const function& callback = {}) -> type&; - auto setColor(Color color) -> type&; - auto setData(Size size) -> type&; + auto setColor(Color color = {}) -> type&; auto setDroppable(bool droppable = true) -> type&; - auto setGradient(Color topLeft, Color topRight, Color bottomLeft, Color bottomRight) -> type&; - auto setHorizontalGradient(Color left, Color right) -> type&; - auto setIcon(const image& icon = {}) -> type&; - auto setVerticalGradient(Color top, Color bottom) -> type&; + auto setGradient(Gradient gradient = {}) -> type&; + auto setImage(const Image& image = {}) -> type&; + auto setSize(Size size = {}) -> type&; auto size() const -> Size; auto update() -> type&; //private: struct State { Color color; - vector data; bool droppable = false; - vector gradient = {{}, {}, {}, {}}; - image icon; + Gradient gradient; + Image image; function onDrop; function onMouseLeave; function onMouseMove; function onMousePress; function onMouseRelease; - Size size; } state; }; #endif @@ -950,12 +1033,12 @@ struct mCheckButton : mWidget { auto bordered() const -> bool; auto checked() const -> bool; auto doToggle() const -> void; - auto icon() const -> image; + auto image() const -> Image; auto onToggle(const function& callback = {}) -> type&; auto orientation() const -> Orientation; auto setBordered(bool bordered = true) -> type&; auto setChecked(bool checked = true) -> type&; - auto setIcon(const image& icon = {}) -> type&; + auto setImage(const Image& image = {}) -> type&; auto setOrientation(Orientation orientation = Orientation::Horizontal) -> type&; auto setText(const string& text = "") -> type&; auto text() const -> string; @@ -964,7 +1047,7 @@ struct mCheckButton : mWidget { struct State { bool bordered = true; bool checked = false; - image icon; + Image image; function onToggle; Orientation orientation = Orientation::Horizontal; string text; @@ -1022,17 +1105,17 @@ struct mComboButton : mWidget { struct mComboButtonItem : mObject { Declare(ComboButtonItem) - auto icon() const -> image; + auto image() const -> Image; auto remove() -> type& override; auto selected() const -> bool; - auto setIcon(const image& icon = {}) -> type&; + auto setImage(const Image& image = {}) -> type&; auto setSelected() -> type&; auto setText(const string& text = "") -> type&; auto text() const -> string; //private: struct State { - image icon; + Image image; bool selected = false; string text; } state; @@ -1073,7 +1156,7 @@ struct mFrame : mWidget { auto layout() const -> Layout; auto remove(sLayout layout) -> type&; auto reset() -> type&; -//TODO setParent() + auto setParent(mObject* parent = nullptr, signed offset = -1) -> type& override; auto setText(const string& text = "") -> type&; auto text() const -> string; @@ -1170,6 +1253,8 @@ struct mIconView : mWidget { auto append(sIconViewItem item) -> type&; auto backgroundColor() const -> Color; + auto batchable() const -> bool; + auto batched() const -> vector; auto doActivate() const -> void; auto doChange() const -> void; auto doContext() const -> void; @@ -1178,30 +1263,28 @@ struct mIconView : mWidget { auto item(unsigned position) const -> IconViewItem; auto itemCount() const -> unsigned; auto items() const -> vector; - auto multiSelect() const -> bool; auto onActivate(const function& callback = {}) -> type&; auto onChange(const function& callback = {}) -> type&; auto onContext(const function& callback = {}) -> type&; auto orientation() const -> Orientation; auto remove(sIconViewItem item) -> type&; auto reset() -> type&; - auto selected() const -> maybe; - auto selectedItems() const -> vector; + auto selected() const -> IconViewItem; auto setBackgroundColor(Color color = {}) -> type&; + auto setBatchable(bool batchable = true) -> type&; auto setFlow(Orientation flow = Orientation::Vertical) -> type&; auto setForegroundColor(Color color = {}) -> type&; - auto setMultiSelect(bool multipleSelections = true) -> type&; auto setOrientation(Orientation orientation = Orientation::Horizontal) -> type&; -//TODO setParent() + auto setParent(mObject* object = nullptr, signed offset = -1) -> type& override; auto setSelected(const vector& selections) -> type&; //private: struct State { Color backgroundColor; + bool batchable = false; Color foregroundColor; Orientation flow = Orientation::Vertical; vector items; - bool multiSelect = false; function onActivate; function onChange; function onContext; @@ -1216,17 +1299,17 @@ struct mIconView : mWidget { struct mIconViewItem : mObject { Declare(IconViewItem) - auto icon() const -> image; + auto image() const -> Image; auto remove() -> type& override; auto selected() const -> bool; - auto setIcon(const image& icon = {}) -> type&; + auto setImage(const Image& image = {}) -> type&; auto setSelected(bool selected = true) -> type&; auto setText(const string& text = "") -> type&; auto text() const -> string; //private: struct State { - image icon; + Image image; bool selected = false; string text; } state; @@ -1318,7 +1401,6 @@ struct mListView : mWidget { auto setBatchable(bool batchable = true) -> type&; auto setBordered(bool bordered = true) -> type&; auto setForegroundColor(Color color = {}) -> type&; - auto setGridVisible(bool visible = true) -> type&; auto setParent(mObject* parent = nullptr, signed offset = -1) -> type& override; //private: @@ -1373,7 +1455,7 @@ struct mListViewColumn : mObject { auto expandable() const -> bool; auto foregroundColor() const -> Color; auto horizontalAlignment() const -> double; - auto icon() const -> image; + auto image() const -> Image; auto remove() -> type& override; auto resizable() const -> bool; auto setActive() -> type&; @@ -1383,7 +1465,7 @@ struct mListViewColumn : mObject { auto setExpandable(bool expandable = true) -> type&; auto setForegroundColor(Color color = {}) -> type&; auto setHorizontalAlignment(double alignment = 0.0) -> type&; - auto setIcon(const image& icon = {}) -> type&; + auto setImage(const Image& image = {}) -> type&; auto setResizable(bool resizable = true) -> type&; auto setSortable(bool sortable = true) -> type&; auto setText(const string& text = "") -> type&; @@ -1403,7 +1485,7 @@ struct mListViewColumn : mObject { bool expandable = false; Color foregroundColor; double horizontalAlignment = 0.0; - image icon; + Image image; bool resizable = true; bool sortable = false; string text; @@ -1454,15 +1536,15 @@ struct mListViewCell : mObject { auto backgroundColor(bool recursive = false) const -> Color; auto checkable() const -> bool; auto checked() const -> bool; - auto font(bool recursive = false) const -> string; + auto font(bool recursive = false) const -> Font; auto foregroundColor(bool recursive = false) const -> Color; - auto icon() const -> image; + auto image() const -> Image; auto setAlignment(Alignment alignment = {}) -> type&; auto setBackgroundColor(Color color = {}) -> type&; auto setCheckable(bool checkable = true) -> type&; auto setChecked(bool checked = true) -> type&; auto setForegroundColor(Color color = {}) -> type&; - auto setIcon(const image& icon = {}) -> type&; + auto setImage(const Image& image = {}) -> type&; auto setText(const string& text = "") -> type&; auto text() const -> string; @@ -1473,7 +1555,7 @@ struct mListViewCell : mObject { bool checkable = false; bool checked = false; Color foregroundColor; - image icon; + Image image; string text; } state; }; @@ -1501,13 +1583,13 @@ struct mRadioButton : mWidget { auto checked() const -> bool; auto doActivate() const -> void; auto group() const -> Group override; - auto icon() const -> image; + auto image() const -> Image; auto onActivate(const function& callback = {}) -> type&; auto orientation() const -> Orientation; auto setBordered(bool bordered = true) -> type&; auto setChecked() -> type&; auto setGroup(sGroup group = {}) -> type& override; - auto setIcon(const image& icon = {}) -> type&; + auto setImage(const Image& image = {}) -> type&; auto setOrientation(Orientation orientation = Orientation::Horizontal) -> type&; auto setText(const string& text = "") -> type&; auto text() const -> string; @@ -1517,7 +1599,7 @@ struct mRadioButton : mWidget { bool bordered = true; bool checked = false; sGroup group; - image icon; + Image image; function onActivate; Orientation orientation = Orientation::Horizontal; string text; @@ -1552,22 +1634,20 @@ struct mRadioLabel : mWidget { struct mSourceEdit : mWidget { Declare(SourceEdit) + auto cursor() const -> Cursor; auto doChange() const -> void; auto doMove() const -> void; auto onChange(const function& callback = {}) -> type&; auto onMove(const function& callback = {}) -> type&; - auto position() const -> unsigned; - auto setPosition(signed position) -> type&; - auto setSelected(Position selected) -> type&; + auto setCursor(Cursor cursor = {}) -> type&; auto setText(const string& text = "") -> type&; auto text() const -> string; //private: struct State { + Cursor cursor; function onChange; function onMove; - unsigned position = 0; - Position selected; string text; } state; }; @@ -1583,23 +1663,23 @@ struct mTabFrame : mWidget { auto doChange() const -> void; auto doClose(sTabFrameItem item) const -> void; auto doMove(sTabFrameItem from, sTabFrameItem to) const -> void; - auto edge() const -> Edge; auto item(unsigned position) const -> TabFrameItem; auto itemCount() const -> unsigned; auto items() const -> vector; + auto navigation() const -> Navigation; auto onChange(const function& callback = {}) -> type&; auto onClose(const function& callback = {}) -> type&; auto onMove(const function& callback = {}) -> type&; auto remove(sTabFrameItem item) -> type&; auto reset() -> type&; auto selected() const -> TabFrameItem; - auto setEdge(Edge edge = Edge::Top) -> type&; + auto setNavigation(Navigation navigation = Navigation::Top) -> type&; auto setParent(mObject* object = nullptr, signed offset = -1) -> type& override; //private: struct State { - Edge edge = Edge::Top; vector items; + Navigation navigation = Navigation::Top; function onChange; function onClose; function onMove; @@ -1615,7 +1695,7 @@ struct mTabFrameItem : mObject { auto append(sLayout layout) -> type&; auto closable() const -> bool; - auto icon() const -> image; + auto image() const -> Image; auto layout() const -> Layout; auto movable() const -> bool; auto remove() -> type& override; @@ -1623,7 +1703,7 @@ struct mTabFrameItem : mObject { auto reset() -> type&; auto selected() const -> bool; auto setClosable(bool closable = true) -> type&; - auto setIcon(const image& icon = {}) -> type&; + auto setImage(const Image& image = {}) -> type&; auto setMovable(bool movable = true) -> type&; auto setParent(mObject* object = nullptr, signed offset = -1) -> type& override; auto setSelected() -> type&; @@ -1633,7 +1713,7 @@ struct mTabFrameItem : mObject { //private: struct State { bool closable = false; - image icon; + Image image; sLayout layout; bool movable = false; bool selected = false; @@ -1649,7 +1729,7 @@ struct mTextEdit : mWidget { Declare(TextEdit) auto backgroundColor() const -> Color; - auto cursorPosition() const -> unsigned; + auto cursor() const -> Cursor; auto doChange() const -> void; auto doMove() const -> void; auto editable() const -> bool; @@ -1657,7 +1737,7 @@ struct mTextEdit : mWidget { auto onChange(const function& callback = {}) -> type&; auto onMove(const function& callback = {}) -> type&; auto setBackgroundColor(Color color = {}) -> type&; - auto setCursorPosition(unsigned position) -> type&; + auto setCursor(Cursor cursor = {}) -> type&; auto setEditable(bool editable = true) -> type&; auto setForegroundColor(Color color = {}) -> type&; auto setText(const string& text = "") -> type&; @@ -1668,7 +1748,7 @@ struct mTextEdit : mWidget { //private: struct State { Color backgroundColor; - unsigned cursorPosition = 0; + Cursor cursor; bool editable = true; Color foregroundColor; function onChange; @@ -1686,13 +1766,10 @@ struct mTreeView : mWidget { auto append(sTreeViewItem item) -> type&; auto backgroundColor() const -> Color; - auto checkable() const -> bool; - auto collapse() -> type&; auto doActivate() const -> void; auto doChange() const -> void; auto doContext() const -> void; auto doToggle(sTreeViewItem item) const -> void; - auto expand() -> type&; auto foregroundColor() const -> Color; auto item(const string& path) const -> TreeViewItem; auto itemCount() const -> unsigned; @@ -1705,14 +1782,12 @@ struct mTreeView : mWidget { auto reset() -> type&; auto selected() const -> TreeViewItem; auto setBackgroundColor(Color color = {}) -> type&; - auto setCheckable(bool checkable = true) -> type&; auto setForegroundColor(Color color = {}) -> type&; -//TODO setParent + auto setParent(mObject* parent = nullptr, signed offset = -1) -> type&; //private: struct State { Color backgroundColor; - bool checkable = false; Color foregroundColor; vector items; function onActivate; @@ -1731,8 +1806,11 @@ struct mTreeViewItem : mObject { Declare(TreeViewItem) auto append(sTreeViewItem item) -> type&; + auto backgroundColor(bool recursive = false) const -> Color; + auto checkable() const -> bool; auto checked() const -> bool; - auto icon() const -> image; + auto foregroundColor(bool recursive = false) const -> Color; + auto image() const -> Image; auto item(const string& path) const -> TreeViewItem; auto itemCount() const -> unsigned; auto items() const -> vector; @@ -1740,18 +1818,25 @@ struct mTreeViewItem : mObject { auto remove() -> type& override; auto remove(sTreeViewItem item) -> type&; auto selected() const -> bool; + auto setBackgroundColor(Color color = {}) -> type&; + auto setCheckable(bool checkable = true) -> type&; auto setChecked(bool checked = true) -> type&; + auto setExpanded(bool expanded = true) -> type&; auto setFocused() -> type& override; - auto setIcon(const image& icon = {}) -> type&; -//TODO setParent + auto setForegroundColor(Color color = {}) -> type&; + auto setImage(const Image& image = {}) -> type&; + auto setParent(mObject* parent = nullptr, signed offset = -1) -> type&; auto setSelected() -> type&; auto setText(const string& text = "") -> type&; auto text() const -> string; //private: struct State { + Color backgroundColor; + bool checkable = false; bool checked = false; - image icon; + Color foregroundColor; + Image image; vector items; string text; } state; diff --git a/hiro/core/cursor.cpp b/hiro/core/cursor.cpp new file mode 100644 index 00000000..4db6e46f --- /dev/null +++ b/hiro/core/cursor.cpp @@ -0,0 +1,43 @@ +#if defined(Hiro_Cursor) + +Cursor::Cursor(signed offset, signed length) { + setCursor(offset, length); +} + +Cursor::operator bool() const { + return offset() && length(); +} + +auto Cursor::operator==(const Cursor& source) const -> bool { + return offset() == source.offset() && length() == source.length(); +} + +auto Cursor::operator!=(const Cursor& source) const -> bool { + return !operator==(source); +} + +auto Cursor::length() const -> signed { + return state.length; +} + +auto Cursor::offset() const -> signed { + return state.offset; +} + +auto Cursor::setCursor(signed offset, signed length) -> type& { + state.offset = offset; + state.length = length; + return *this; +} + +auto Cursor::setOffset(signed offset) -> type& { + state.offset = offset; + return *this; +} + +auto Cursor::setLength(signed length) -> type& { + state.length = length; + return *this; +} + +#endif diff --git a/hiro/core/font.cpp b/hiro/core/font.cpp index ec32f614..d082c9c1 100644 --- a/hiro/core/font.cpp +++ b/hiro/core/font.cpp @@ -1,11 +1,14 @@ #if defined(Hiro_Font) -Font::Font() { -} +const string Font::Sans = "{sans}"; +const string Font::Serif = "{serif}"; +const string Font::Mono = "{mono}"; Font::Font(const string& family, unsigned size) { - state.family = family; - state.size = size; + setFamily(family); + setSize(size); + state.bold = false; + state.italic = false; } Font::operator bool() const { @@ -13,8 +16,8 @@ Font::operator bool() const { } auto Font::operator==(const Font& source) const -> bool { - return family() == source.family() || size() == source.size() && bold() == source.bold() && italic() == source.italic(); -}; + return family() == source.family() && size() == source.size() && bold() == source.bold() && italic() == source.italic(); +} auto Font::operator!=(const Font& source) const -> bool { return !operator==(source); @@ -32,6 +35,14 @@ auto Font::italic() const -> bool { return state.italic; } +auto Font::reset() -> type& { + state.family = ""; + state.size = 0; + state.bold = false; + state.italic = false; + return *this; +} + auto Font::setBold(bool bold) -> type& { state.bold = bold; return *this; @@ -56,22 +67,8 @@ auto Font::size() const -> unsigned { return state.size; } -// - -auto Font::serif(unsigned size, const string& style) -> string { - return pFont::serif(size, style); -} - -auto Font::sans(unsigned size, const string& style) -> string { - return pFont::sans(size, style); -} - -auto Font::monospace(unsigned size, const string& style) -> string { - return pFont::monospace(size, style); -} - -auto Font::size(const string& font, const string& text) -> Size { - return pFont::size(font, text); +auto Font::size(const string& text) const -> Size { + return pFont::size(*this, text); } #endif diff --git a/hiro/core/geometry.cpp b/hiro/core/geometry.cpp index 6a2d320d..4cd15443 100644 --- a/hiro/core/geometry.cpp +++ b/hiro/core/geometry.cpp @@ -20,6 +20,10 @@ Geometry::Geometry(const string& text) { state.height = integer(part(3)); } +Geometry::operator bool() const { + return state.x || state.y || state.width || state.height; +} + auto Geometry::operator==(const Geometry& source) const -> bool { return x() == source.x() && y() == source.y() && width() == source.width() && height() == source.height(); } @@ -36,6 +40,10 @@ auto Geometry::position() const -> Position { return {state.x, state.y}; } +auto Geometry::reset() -> type& { + return setGeometry(0, 0, 0, 0); +} + auto Geometry::setHeight(signed height) -> type& { state.height = height; return *this; diff --git a/hiro/core/gradient.cpp b/hiro/core/gradient.cpp new file mode 100644 index 00000000..946ad0f5 --- /dev/null +++ b/hiro/core/gradient.cpp @@ -0,0 +1,37 @@ +#if defined(Hiro_Gradient) + +Gradient::Gradient() { +} + +Gradient::operator bool() const { + return state.colors.size() == 4; +} + +auto Gradient::operator==(const Gradient& source) const -> bool { + if(state.colors.size() != source.state.colors.size()) return false; + for(auto n : range(state.colors)) { + if(state.colors[n] != source.state.colors[n]) return false; + } + return true; +} + +auto Gradient::operator!=(const Gradient& source) const -> bool { + return !operator==(source); +} + +auto Gradient::setBilinear(Color topLeft, Color topRight, Color bottomLeft, Color bottomRight) -> type& { + state.colors = {topLeft, topRight, bottomLeft, bottomRight}; + return *this; +} + +auto Gradient::setHorizontal(Color left, Color right) -> type& { + state.colors = {left, right, left, right}; + return *this; +} + +auto Gradient::setVertical(Color top, Color bottom) -> type& { + state.colors = {top, top, bottom, bottom}; + return *this; +} + +#endif diff --git a/hiro/core/hotkey.cpp b/hiro/core/hotkey.cpp index 8a4d71cd..da5516e9 100644 --- a/hiro/core/hotkey.cpp +++ b/hiro/core/hotkey.cpp @@ -1,54 +1,59 @@ #if defined(Hiro_Hotkey) -auto mHotkey::allocate() -> pObject* { - return new pHotkey(*this); +Hotkey::Hotkey() : state(new Hotkey::State) { + setSequence(); } -// - -auto mHotkey::doPress() const -> void { - if(state.onPress) return state.onPress(); +Hotkey::Hotkey(const string& sequence) : state(new Hotkey::State) { + setSequence(sequence); } -auto mHotkey::doRelease() const -> void { - if(state.onRelease) return state.onRelease(); +Hotkey::operator bool() const { + return state->sequence; } -auto mHotkey::onPress(const function& callback) -> type& { - state.onPress = callback; +auto Hotkey::operator==(const Hotkey& source) const -> bool { + return state == source.state; +} + +auto Hotkey::operator!=(const Hotkey& source) const -> bool { + return !operator==(source); +} + +auto Hotkey::doPress() const -> void { + if(state->onPress) state->onPress(); +} + +auto Hotkey::doRelease() const -> void { + if(state->onRelease) state->onRelease(); +} + +auto Hotkey::onPress(const function& callback) -> type& { + state->onPress = callback; return *this; } -auto mHotkey::onRelease(const function& callback) -> type& { - state.onRelease = callback; +auto Hotkey::onRelease(const function& callback) -> type& { + state->onRelease = callback; return *this; } -auto mHotkey::owner() const -> wObject { - return state.owner; -} - -auto mHotkey::remove() -> type& { -//todo: remove from Keyboard::hotkeys +auto Hotkey::reset() -> type& { + setSequence(); return *this; } -auto mHotkey::sequence() const -> string { - return state.sequence; +auto Hotkey::sequence() const -> string { + return state->sequence; } -auto mHotkey::setOwner(sObject owner) -> type& { - state.owner = owner; - return *this; -} - -auto mHotkey::setSequence(const string& sequence) -> type& { - state.active = false; - state.sequence = sequence; - state.keys.reset(); +auto Hotkey::setSequence(const string& sequence) -> type& { + state->active = false; + state->sequence = sequence; + state->keys.reset(); for(auto& key : sequence.split("+")) { if(auto position = Keyboard::keys.find(key)) { - state.keys.append(*position); + state->keys.append(*position); } } return *this; diff --git a/hiro/core/image.cpp b/hiro/core/image.cpp new file mode 100644 index 00000000..503530e1 --- /dev/null +++ b/hiro/core/image.cpp @@ -0,0 +1,83 @@ +#if defined(Hiro_Image) + +Image::Image() { +} + +Image::Image(const Image& source) { + operator=(source); +} + +Image::Image(const string& source) { + nall::image image{source}; + image.transform(); + setImage((const uint32_t*)image.data(), Size{(signed)image.width(), (signed)image.height()}); +} + +Image::Image(const vector& source) { + nall::image image{source}; + image.transform(); + setImage((const uint32_t*)image.data(), Size{(signed)image.width(), (signed)image.height()}); +} + +Image::Image(const uint32_t* data, Size size) { + setImage(data, size); +} + +Image::~Image() { + reset(); +} + +Image::operator bool() const { + return state.data; +} + +auto Image::operator=(const Image& source) -> type& { + reset(); + if(state.size = source.state.size) { + state.data = new uint32_t[state.size.width() * state.size.height()]; + memory::copy(state.data, source.state.data, state.size.width() * state.size.height() * sizeof(uint32_t)); + } + return *this; +} + +auto Image::data() const -> uint32_t* { + return state.data; +} + +auto Image::height() const -> signed { + return state.size.height(); +} + +auto Image::reset() -> type& { + if(state.data) delete[] state.data; + state.data = nullptr; + state.size = {}; + return *this; +} + +auto Image::setImage(const uint32_t* data, Size size) -> type& { + reset(); + if(data && size) { + state.size = size; + state.data = new uint32_t[size.width() * size.height()]; + memory::copy(state.data, data, size.width() * size.height() * sizeof(uint32_t)); + } + return *this; +} + +auto Image::setSize(Size size) -> type& { + state.size = size; + if(state.data) delete[] state.data; + if(state.size) state.data = new uint32_t[size.width() * size.height()]; + return *this; +} + +auto Image::size() const -> Size { + return state.size; +} + +auto Image::width() const -> signed { + return state.size.width(); +} + +#endif diff --git a/hiro/core/keyboard.cpp b/hiro/core/keyboard.cpp index 82f3bbc9..8abbbf3d 100644 --- a/hiro/core/keyboard.cpp +++ b/hiro/core/keyboard.cpp @@ -18,44 +18,43 @@ const vector Keyboard::keys = { "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Zero", //group aliases - "Shift", //"LeftShift" | "RightShift" + "Shift", //"LeftShift" | "RightShift" "Control", //"LeftControl" | "RightControl" - "Alt", //"LeftAlt" | "RightAlt" - "Super", //"LeftSuper" | "RightSuper" - "Enter", //"LeftEnter" | "RightEnter" + "Alt", //"LeftAlt" | "RightAlt" + "Super", //"LeftSuper" | "RightSuper" + "Enter", //"LeftEnter" | "RightEnter" }; -auto Keyboard::append(sHotkey hotkey) -> void { +auto Keyboard::append(Hotkey hotkey) -> void { state.hotkeys.append(hotkey); } auto Keyboard::hotkey(unsigned position) -> Hotkey { - if(position < hotkeys()) return state.hotkeys[position]; + if(position < hotkeyCount()) return state.hotkeys[position]; return {}; } -auto Keyboard::hotkeys() -> unsigned { +auto Keyboard::hotkeyCount() -> unsigned { return state.hotkeys.size(); } +auto Keyboard::hotkeys() -> vector { + return state.hotkeys; +} + auto Keyboard::poll() -> vector { auto pressed = pKeyboard::poll(); for(auto& hotkey : state.hotkeys) { - bool active = hotkey->state.sequence.size() > 0; - for(auto& key : hotkey->state.keys) { + bool active = hotkey.state->sequence.size() > 0; + for(auto& key : hotkey.state->keys) { if(pressed[key]) continue; active = false; break; } - if(auto owner = hotkey->state.owner.acquire()) { - //todo: set active = false if owner no longer exists - active &= owner->focused(); - } - if(hotkey->state.active != active) { - hotkey->state.active = active; - if( active) hotkey->doPress(); - if(!active) hotkey->doRelease(); + if(hotkey.state->active != active) { + hotkey.state->active = active; + active ? hotkey.doPress() : hotkey.doRelease(); } } @@ -71,7 +70,7 @@ auto Keyboard::released(const string& key) -> bool { return !pressed(key); } -auto Keyboard::remove(sHotkey hotkey) -> void { +auto Keyboard::remove(Hotkey hotkey) -> void { if(auto offset = state.hotkeys.find(hotkey)) { state.hotkeys.remove(*offset); } diff --git a/hiro/core/menu-bar.cpp b/hiro/core/menu-bar.cpp index c977c7f0..329f0826 100644 --- a/hiro/core/menu-bar.cpp +++ b/hiro/core/menu-bar.cpp @@ -54,4 +54,11 @@ auto mMenuBar::reset() -> type& { return *this; } +auto mMenuBar::setParent(mObject* parent, signed offset) -> type& { + for(auto n : rrange(state.menus)) state.menus[n]->destruct(); + mObject::setParent(parent, offset); + for(auto& menu : state.menus) menu->setParent(this, menu->offset()); + return *this; +} + #endif diff --git a/hiro/core/object.cpp b/hiro/core/object.cpp index c378a1c3..c420da14 100644 --- a/hiro/core/object.cpp +++ b/hiro/core/object.cpp @@ -68,7 +68,7 @@ auto mObject::focused() const -> bool { return false; } -auto mObject::font(bool recursive) const -> string { +auto mObject::font(bool recursive) const -> Font { if(!recursive || state.font) return state.font; if(auto object = parent()) return object->font(true); return Application::font(); @@ -277,7 +277,7 @@ auto mObject::setFocused() -> type& { return *this; } -auto mObject::setFont(const string& font) -> type& { +auto mObject::setFont(const Font& font) -> type& { state.font = font; signal(setFont, this->font(true)); return *this; diff --git a/hiro/core/popup-menu.cpp b/hiro/core/popup-menu.cpp index 17a08388..f064df7c 100644 --- a/hiro/core/popup-menu.cpp +++ b/hiro/core/popup-menu.cpp @@ -50,7 +50,7 @@ auto mPopupMenu::reset() -> type& { } auto mPopupMenu::setParent(mObject* parent, signed offset) -> type& { - for(auto& action : state.actions) action->destruct(); + for(auto n : rrange(state.actions)) state.actions[n]->destruct(); mObject::setParent(parent, offset); for(auto& action : state.actions) action->construct(); return *this; diff --git a/hiro/core/position.cpp b/hiro/core/position.cpp index 8d5dc744..0bab72ed 100644 --- a/hiro/core/position.cpp +++ b/hiro/core/position.cpp @@ -8,6 +8,10 @@ Position::Position(signed x, signed y) { setPosition(x, y); } +Position::operator bool() const { + return state.x || state.y; +} + auto Position::operator==(const Position& source) const -> bool { return x() == source.x() && y() == source.y(); } @@ -16,6 +20,10 @@ auto Position::operator!=(const Position& source) const -> bool { return !operator==(source); } +auto Position::reset() -> type& { + return setPosition(0, 0); +} + auto Position::setPosition(Position position) -> type& { return setPosition(position.x(), position.y()); } diff --git a/hiro/core/shared.hpp b/hiro/core/shared.hpp index efbf7c65..3b79ab56 100644 --- a/hiro/core/shared.hpp +++ b/hiro/core/shared.hpp @@ -28,7 +28,7 @@ auto remove() { return self().remove(), *this; } \ auto setEnabled(bool enabled = true) { return self().setEnabled(enabled), *this; } \ auto setFocused() { return self().setFocused(), *this; } \ - auto setFont(const string& font = "") { return self().setFont(font), *this; } \ + auto setFont(const Font& font = {}) { return self().setFont(font), *this; } \ auto setVisible(bool visible = true) { return self().setVisible(visible), *this; } \ auto visible(bool recursive = false) const { return self().visible(recursive); } \ @@ -81,21 +81,6 @@ private: }; #endif -#if defined(Hiro_Hotkey) -struct Hotkey : sHotkey { - DeclareSharedObject(Hotkey) - - auto doPress() const { return self().doPress(); } - auto doRelease() const { return self().doRelease(); } - auto onPress(const function& callback = {}) { return self().onPress(callback), *this; } - auto onRelease(const function& callback = {}) { return self().onRelease(callback), *this; } - auto owner() const { return self().owner(); } - auto sequence() const { return self().sequence(); } - auto setOwner(sObject owner) { return self().setOwner(owner), *this; } - auto setSequence(const string& sequence = "") { return self().setSequence(sequence), *this; } -}; -#endif - #if defined(Hiro_Timer) struct Timer : sTimer { DeclareSharedObject(Timer) @@ -121,10 +106,10 @@ struct Menu : sMenu { auto actionCount() const { return self().actionCount(); } auto actions() const { return self().actions(); } auto append(sAction action) { return self().append(action), *this; } - auto icon() const { return self().icon(); } + auto image() const { return self().image(); } auto remove(sAction action) { return self().remove(action), *this; } auto reset() { return self().reset(), *this; } - auto setIcon(const image& icon = {}) { return self().setIcon(icon), *this; } + auto setImage(const Image& image = {}) { return self().setImage(image), *this; } auto setText(const string& text = "") { return self().setText(text), *this; } auto text() const { return self().text(); } }; @@ -141,9 +126,9 @@ struct MenuItem : sMenuItem { DeclareSharedAction(MenuItem) auto doActivate() const { return self().doActivate(); } - auto icon() const { return self().icon(); } + auto image() const { return self().image(); } auto onActivate(const function& callback = {}) { return self().onActivate(callback), *this; } - auto setIcon(const image& icon = {}) { return self().setIcon(icon), *this; } + auto setImage(const Image& image = {}) { return self().setImage(image), *this; } auto setText(const string& text = "") { return self().setText(text), *this; } auto text() const { return self().text(); } }; @@ -200,11 +185,11 @@ struct Button : sButton { auto bordered() const { return self().bordered(); } auto doActivate() const { return self().doActivate(); } - auto icon() const { return self().icon(); } + auto image() const { return self().image(); } auto onActivate(const function& callback = {}) { return self().onActivate(callback), *this; } auto orientation() const { return self().orientation(); } auto setBordered(bool bordered = true) { return self().setBordered(bordered), *this; } - auto setIcon(const image& icon = {}) { return self().setIcon(icon), *this; } + auto setImage(const Image& image = {}) { return self().setImage(image), *this; } auto setOrientation(Orientation orientation = Orientation::Horizontal) { return self().setOrientation(orientation), *this; } auto setText(const string& text = "") { return self().setText(text), *this; } auto text() const { return self().text(); } @@ -224,20 +209,17 @@ struct Canvas : sCanvas { auto doMousePress(Mouse::Button button) const { return self().doMousePress(button); } auto doMouseRelease(Mouse::Button button) const { return self().doMouseRelease(button); } auto gradient() const { return self().gradient(); } - auto icon() const { return self().icon(); } + auto image() const { return self().image(); } auto onDrop(const function& callback = {}) { return self().onDrop(callback), *this; } auto onMouseLeave(const function& callback = {}) { return self().onMouseLeave(callback), *this; } auto onMouseMove(const function& callback = {}) { return self().onMouseMove(callback), *this; } auto onMousePress(const function& callback = {}) { return self().onMousePress(callback), *this; } auto onMouseRelease(const function& callback = {}) { return self().onMouseRelease(callback), *this; } auto setColor(Color color) { return self().setColor(color), *this; } - auto setData(Size size) { return self().setData(size), *this; } auto setDroppable(bool droppable = true) { return self().setDroppable(droppable), *this; } - auto setGradient(Color topLeft, Color topRight, Color bottomLeft, Color bottomRight) { return self().setGradient(topLeft, topRight, bottomLeft, bottomRight), *this; } - auto setHorizontalGradient(Color left, Color right) { return self().setGradient(left, right, left, right), *this; } - auto setIcon(const image& icon = {}) { return self().setIcon(icon), *this; } - auto setVerticalGradient(Color top, Color bottom) { return self().setGradient(top, top, bottom, bottom), *this; } - auto size() const { return self().size(); } + auto setGradient(Gradient gradient = {}) { return self().setGradient(gradient), *this; } + auto setImage(const Image& image = {}) { return self().setImage(image), *this; } + auto setSize(Size size = {}) { return self().setSize(size), *this; } auto update() { return self().update(), *this; } }; #endif @@ -249,12 +231,12 @@ struct CheckButton : sCheckButton { auto bordered() const { return self().bordered(); } auto checked() const { return self().checked(); } auto doToggle() const { return self().doToggle(); } - auto icon() const { return self().icon(); } + auto image() const { return self().image(); } auto onToggle(const function& callback = {}) { return self().onToggle(callback), *this; } auto orientation() const { return self().orientation(); } auto setBordered(bool bordered = true) { return self().setBordered(bordered), *this; } auto setChecked(bool checked = true) { return self().setChecked(checked), *this; } - auto setIcon(const image& icon = {}) { return self().setIcon(icon), *this; } + auto setImage(const Image& image = {}) { return self().setImage(image), *this; } auto setOrientation(Orientation orientation = Orientation::Horizontal) { return self().setOrientation(orientation), *this; } auto setText(const string& text = "") { return self().setText(text), *this; } auto text() const { return self().text(); } @@ -278,9 +260,9 @@ struct CheckLabel : sCheckLabel { struct ComboButtonItem : sComboButtonItem { DeclareSharedObject(ComboButtonItem) - auto icon() const { return self().icon(); } + auto image() const { return self().image(); } auto selected() const { return self().selected(); } - auto setIcon(const image& icon = {}) { return self().setIcon(icon), *this; } + auto setImage(const Image& image = {}) { return self().setImage(image), *this; } auto setSelected() { return self().setSelected(), *this; } auto setText(const string& text = "") { return self().setText(text), *this; } auto text() const { return self().text(); } @@ -388,9 +370,9 @@ struct HorizontalSlider : sHorizontalSlider { struct IconViewItem : sIconViewItem { DeclareSharedObject(IconViewItem) - auto icon() const { return self().icon(); } + auto image() const { return self().image(); } auto selected() const { return self().selected(); } - auto setIcon(const image& icon = {}) { return self().setIcon(icon), *this; } + auto setImage(const Image& image = {}) { return self().setImage(image), *this; } auto setSelected(bool selected = true) { return self().setSelected(selected), *this; } auto setText(const string& text = "") { return self().setText(text), *this; } auto text() const { return self().text(); } @@ -403,6 +385,8 @@ struct IconView : sIconView { auto append(sIconViewItem item) { return self().append(item), *this; } auto backgroundColor() const { return self().backgroundColor(); } + auto batchable() const { return self().batchable(); } + auto batched() const { return self().batched(); } auto doActivate() const { return self().doActivate(); } auto doChange() const { return self().doChange(); } auto doContext() const { return self().doContext(); } @@ -411,7 +395,6 @@ struct IconView : sIconView { auto item(unsigned position) const { return self().item(position); } auto itemCount() const { return self().itemCount(); } auto items() const { return self().items(); } - auto multiSelect() const { return self().multiSelect(); } auto onActivate(const function& callback = {}) { return self().onActivate(callback), *this; } auto onChange(const function& callback = {}) { return self().onChange(callback), *this; } auto onContext(const function& callback = {}) { return self().onContext(callback), *this; } @@ -419,11 +402,10 @@ struct IconView : sIconView { auto remove(sIconViewItem item) { return self().remove(item), *this; } auto reset() { return self().reset(), *this; } auto selected() const { return self().selected(); } - auto selectedItems() const { return self().selectedItems(); } auto setBackgroundColor(Color color = {}) { return self().setBackgroundColor(color), *this; } + auto setBatchable(bool batchable = true) { return self().setBatchable(batchable), *this; } auto setFlow(Orientation orientation = Orientation::Vertical) { return self().setFlow(orientation), *this; } auto setForegroundColor(Color color = {}) { return self().setForegroundColor(color), *this; } - auto setMultiSelect(bool multiSelect = true) { return self().setMultiSelect(multiSelect), *this; } auto setOrientation(Orientation orientation = Orientation::Horizontal) { return self().setOrientation(orientation), *this; } auto setSelected(const vector& selections) { return self().setSelected(selections), *this; } }; @@ -470,7 +452,7 @@ struct ListViewColumn : sListViewColumn { auto expandable() const { return self().expandable(); } auto foregroundColor() const { return self().foregroundColor(); } auto horizontalAlignment() const { return self().horizontalAlignment(); } - auto icon() const { return self().icon(); } + auto image() const { return self().image(); } auto resizable() const { return self().resizable(); } auto setActive() { return self().setActive(), *this; } auto setAlignment(Alignment alignment = {}) { return self().setAlignment(alignment), *this; } @@ -478,12 +460,10 @@ struct ListViewColumn : sListViewColumn { auto setEditable(bool editable = true) { return self().setEditable(editable), *this; } auto setExpandable(bool expandable = true) { return self().setExpandable(expandable), *this; } auto setForegroundColor(Color color = {}) { return self().setForegroundColor(color), *this; } - auto setHorizontalAlignment(double alignment = 0.0) { return self().setHorizontalAlignment(alignment), *this; } - auto setIcon(const image& icon = {}) { return self().setIcon(icon), *this; } + auto setImage(const Image& image = {}) { return self().setImage(image), *this; } auto setResizable(bool resizable = true) { return self().setResizable(resizable), *this; } auto setSortable(bool sortable = true) { return self().setSortable(sortable), *this; } auto setText(const string& text = "") { return self().setText(text), *this; } - auto setVerticalAlignment(double alignment = 0.5) { return self().setVerticalAlignment(alignment), *this; } auto setWidth(signed width = 0) { return self().setWidth(width), *this; } auto sortable() const { return self().sortable(); } auto text() const { return self().text(); } @@ -513,13 +493,13 @@ struct ListViewCell : sListViewCell { auto checkable() const { return self().checkable(); } auto checked() const { return self().checked(); } auto foregroundColor() const { return self().foregroundColor(); } - auto icon() const { return self().icon(); } + auto image() const { return self().image(); } auto setAlignment(Alignment alignment = {}) { return self().setAlignment(alignment), *this; } auto setBackgroundColor(Color color = {}) { return self().setBackgroundColor(color), *this; } auto setCheckable(bool checkable = true) const { return self().setCheckable(checkable), *this; } auto setChecked(bool checked = true) const { return self().setChecked(checked), *this; } auto setForegroundColor(Color color = {}) { return self().setForegroundColor(color), *this; } - auto setIcon(const image& icon = {}) { return self().setIcon(icon), *this; } + auto setImage(const Image& image = {}) { return self().setImage(image), *this; } auto setText(const string& text = "") { return self().setText(text), *this; } auto text() const { return self().text(); } }; @@ -603,12 +583,12 @@ struct RadioButton : sRadioButton { auto checked() const { return self().checked(); } auto doActivate() const { return self().doActivate(); } auto group() const { return self().group(); } - auto icon() const { return self().icon(); } + auto image() const { return self().image(); } auto onActivate(const function& callback = {}) { return self().onActivate(callback), *this; } auto orientation() const { return self().orientation(); } auto setBordered(bool bordered = true) { return self().setBordered(bordered), *this; } auto setChecked() { return self().setChecked(), *this; } - auto setIcon(const image& icon = {}) { return self().setIcon(icon), *this; } + auto setImage(const Image& image = {}) { return self().setImage(image), *this; } auto setOrientation(Orientation orientation = Orientation::Horizontal) { return self().setOrientation(orientation), *this; } auto setText(const string& text = "") { return self().setText(text), *this; } auto text() const { return self().text(); } @@ -633,13 +613,12 @@ struct RadioLabel : sRadioLabel { struct SourceEdit : sSourceEdit { DeclareSharedWidget(SourceEdit) + auto cursor() const { return self().cursor(); } auto doChange() const { return self().doChange(); } auto doMove() const { return self().doMove(); } auto onChange(const function& callback = {}) { return self().onChange(callback), *this; } auto onMove(const function& callback = {}) { return self().onMove(callback), *this; } - auto position() const { return self().position(); } - auto setPosition(signed position) { return self().setPosition(position), *this; } - auto setSelected(Position selected) { return self().setSelected(selected), *this; } + auto setCursor(Cursor cursor = {}) { return self().setCursor(cursor), *this; } auto setText(const string& text = "") { return self().setText(text), *this; } auto text() const { return self().text(); } }; @@ -651,14 +630,14 @@ struct TabFrameItem : sTabFrameItem { auto append(sLayout layout) { return self().append(layout), *this; } auto closable() const { return self().closable(); } - auto icon() const { return self().icon(); } + auto image() const { return self().image(); } auto layout() const { return self().layout(); } auto movable() const { return self().movable(); } auto remove(sLayout layout) { return self().remove(layout), *this; } auto reset() { return self().reset(), *this; } auto selected() const { return self().selected(); } auto setClosable(bool closable = true) { return self().setClosable(closable), *this; } - auto setIcon(const image& icon = {}) { return self().setIcon(icon), *this; } + auto setImage(const Image& image = {}) { return self().setImage(image), *this; } auto setMovable(bool movable = true) { return self().setMovable(movable), *this; } auto setSelected() { return self().setSelected(), *this; } auto setText(const string& text = "") { return self().setText(text), *this; } @@ -674,17 +653,17 @@ struct TabFrame : sTabFrame { auto doChange() const { return self().doChange(); } auto doClose(sTabFrameItem item) const { return self().doClose(item); } auto doMove(sTabFrameItem from, sTabFrameItem to) const { return self().doMove(from, to); } - auto edge() const { return self().edge(); } auto item(unsigned position) const { return self().item(position); } auto itemCount() const { return self().itemCount(); } auto items() const { return self().items(); } + auto navigation() const { return self().navigation(); } auto onChange(const function& callback = {}) { return self().onChange(callback), *this; } auto onClose(const function& callback = {}) { return self().onClose(callback), *this; } auto onMove(const function& callback = {}) { return self().onMove(callback), *this; } auto remove(sTabFrameItem item) { return self().remove(item), *this; } auto reset() { return self().reset(), *this; } auto selected() const { return self().selected(); } - auto setEdge(Edge edge = Edge::Top) { return self().setEdge(edge), *this; } + auto setNavigation(Navigation navigation = Navigation::Top) { return self().setNavigation(navigation), *this; } }; #endif @@ -693,7 +672,7 @@ struct TextEdit : sTextEdit { DeclareSharedWidget(TextEdit) auto backgroundColor() const { return self().backgroundColor(); } - auto cursorPosition() const { return self().cursorPosition(); } + auto cursor() const { return self().cursor(); } auto doChange() const { return self().doChange(); } auto doMove() const { return self().doMove(); } auto editable() const { return self().editable(); } @@ -701,7 +680,7 @@ struct TextEdit : sTextEdit { auto onChange(const function& callback = {}) { return self().onChange(callback), *this; } auto onMove(const function& callback = {}) { return self().onMove(callback), *this; } auto setBackgroundColor(Color color = {}) { return self().setBackgroundColor(color), *this; } - auto setCursorPosition(unsigned position) { return self().setCursorPosition(position), *this; } + auto setCursor(Cursor cursor = {}) { return self().setCursor(cursor), *this; } auto setEditable(bool editable = true) { return self().setEditable(editable), *this; } auto setForegroundColor(Color color = {}) { return self().setForegroundColor(color), *this; } auto setText(const string& text = "") { return self().setText(text), *this; } @@ -716,16 +695,23 @@ struct TreeViewItem : sTreeViewItem { DeclareSharedObject(TreeViewItem) auto append(sTreeViewItem item) { return self().append(item), *this; } + auto backgroundColor() const { return self().backgroundColor(); } + auto checkable() const { return self().checkable(); } auto checked() const { return self().checked(); } - auto icon() const { return self().icon(); } + auto foregroundColor() const { return self().foregroundColor(); } + auto image() const { return self().image(); } auto item(const string& path) const { return self().item(path); } auto itemCount() const { return self().itemCount(); } auto items() const { return self().items(); } auto path() const { return self().path(); } auto remove(sTreeViewItem item) { return self().remove(item), *this; } auto selected() const { return self().selected(); } + auto setBackgroundColor(Color color = {}) { return self().setBackgroundColor(color), *this; } + auto setCheckable(bool checkable = true) { return self().setCheckable(checkable), *this; } auto setChecked(bool checked = true) { return self().setChecked(checked), *this; } - auto setIcon(const image& icon = {}) { return self().setIcon(icon), *this; } + auto setExpanded(bool expanded = true) { return self().setExpanded(expanded), *this; } + auto setForegroundColor(Color color = {}) { return self().setForegroundColor(color), *this; } + auto setImage(const Image& image = {}) { return self().setImage(image), *this; } auto setSelected() { return self().setSelected(), *this; } auto setText(const string& text = "") { return self().setText(text), *this; } auto text() const { return self().text(); } @@ -738,13 +724,10 @@ struct TreeView : sTreeView { auto append(sTreeViewItem item) { return self().append(item), *this; } auto backgroundColor() const { return self().backgroundColor(); } - auto checkable() const { return self().checkable(); } - auto collapse() { return self().collapse(), *this; } auto doActivate() const { return self().doActivate(); } auto doChange() const { return self().doChange(); } auto doContext() const { return self().doContext(); } auto doToggle(sTreeViewItem item) const { return self().doToggle(item); } - auto expand() { return self().expand(), *this; } auto foregroundColor() const { return self().foregroundColor(); } auto item(const string& path) const { return self().item(path); } auto itemCount() const { return self().itemCount(); } @@ -757,7 +740,6 @@ struct TreeView : sTreeView { auto reset() { return self().reset(), *this; } auto selected() const { return self().selected(); } auto setBackgroundColor(Color color = {}) { return self().setBackgroundColor(color), *this; } - auto setCheckable(bool checkable = true) { return self().setCheckable(checkable), *this; } auto setForegroundColor(Color color = {}) { return self().setForegroundColor(color), *this; } }; #endif diff --git a/hiro/core/size.cpp b/hiro/core/size.cpp index bbf79efc..26cd3168 100644 --- a/hiro/core/size.cpp +++ b/hiro/core/size.cpp @@ -8,6 +8,10 @@ Size::Size(signed width, signed height) { setSize(width, height); } +Size::operator bool() const { + return state.width || state.height; +} + auto Size::operator==(const Size& source) const -> bool { return width() == source.width() && height() == source.height(); } @@ -20,6 +24,10 @@ auto Size::height() const -> signed { return state.height; } +auto Size::reset() -> type& { + return setSize(0, 0); +} + auto Size::setHeight(signed height) -> type& { state.height = height; return *this; diff --git a/hiro/core/widget/button.cpp b/hiro/core/widget/button.cpp index d754ba03..07f7f344 100644 --- a/hiro/core/widget/button.cpp +++ b/hiro/core/widget/button.cpp @@ -14,8 +14,8 @@ auto mButton::doActivate() const -> void { if(state.onActivate) return state.onActivate(); } -auto mButton::icon() const -> image { - return state.icon; +auto mButton::image() const -> Image { + return state.image; } auto mButton::onActivate(const function& callback) -> type& { @@ -33,9 +33,9 @@ auto mButton::setBordered(bool bordered) -> type& { return *this; } -auto mButton::setIcon(const image& icon) -> type& { - state.icon = icon; - signal(setIcon, icon); +auto mButton::setImage(const Image& image) -> type& { + state.image = image; + signal(setImage, image); return *this; } diff --git a/hiro/core/widget/canvas.cpp b/hiro/core/widget/canvas.cpp index f7ab59ce..0dfd405b 100644 --- a/hiro/core/widget/canvas.cpp +++ b/hiro/core/widget/canvas.cpp @@ -11,7 +11,7 @@ auto mCanvas::color() const -> Color { } auto mCanvas::data() -> uint32_t* { - return state.data.data(); + return state.image.data(); } auto mCanvas::droppable() const -> bool { @@ -38,12 +38,12 @@ auto mCanvas::doMouseRelease(Mouse::Button button) const -> void { if(state.onMouseRelease) return state.onMouseRelease(button); } -auto mCanvas::gradient() const -> vector { +auto mCanvas::gradient() const -> Gradient { return state.gradient; } -auto mCanvas::icon() const -> image { - return state.icon; +auto mCanvas::image() const -> Image { + return state.image; } auto mCanvas::onDrop(const function& callback) -> type& { @@ -72,53 +72,38 @@ auto mCanvas::onMouseRelease(const function& callback) -> } auto mCanvas::setColor(Color color) -> type& { - state.size = {}; state.color = color; signal(setColor, color); return *this; } -auto mCanvas::setData(Size size) -> type& { - state.size = size; - state.data.resize(size.width() * size.height()); - memory::fill(state.data.data(), size.width() * size.height() * sizeof(uint32_t)); - signal(setData, size); - return *this; -} - auto mCanvas::setDroppable(bool droppable) -> type& { state.droppable = droppable; signal(setDroppable, droppable); return *this; } -auto mCanvas::setGradient(Color topLeft, Color topRight, Color bottomLeft, Color bottomRight) -> type& { - state.size = {}; - state.gradient[0] = topLeft; - state.gradient[1] = topRight; - state.gradient[2] = bottomLeft; - state.gradient[3] = bottomRight; - signal(setGradient, topLeft, topRight, bottomLeft, bottomRight); +auto mCanvas::setGradient(Gradient gradient) -> type& { + state.gradient = gradient; + signal(setGradient, gradient); return *this; } -auto mCanvas::setHorizontalGradient(Color left, Color right) -> type& { - return setGradient(left, right, left, right); -} - -auto mCanvas::setIcon(const image& icon) -> type& { - state.size = {(signed)icon.width(), (signed)icon.height()}; - state.icon = icon; - signal(setIcon, icon); +auto mCanvas::setImage(const Image& image) -> type& { + state.image = image; + signal(setImage, image); return *this; } -auto mCanvas::setVerticalGradient(Color top, Color bottom) -> type& { - return setGradient(top, top, bottom, bottom); +auto mCanvas::setSize(Size size) -> type& { + Image image; + image.setSize(size); + memory::fill(image.data(), size.width() * size.height() * sizeof(uint32_t), 0x00); + return setImage(image); } auto mCanvas::size() const -> Size { - return state.size; + return state.image.size(); } auto mCanvas::update() -> type& { diff --git a/hiro/core/widget/check-button.cpp b/hiro/core/widget/check-button.cpp index e67bf17d..a72db387 100644 --- a/hiro/core/widget/check-button.cpp +++ b/hiro/core/widget/check-button.cpp @@ -18,8 +18,8 @@ auto mCheckButton::doToggle() const -> void { if(state.onToggle) return state.onToggle(); } -auto mCheckButton::icon() const -> image { - return state.icon; +auto mCheckButton::image() const -> Image { + return state.image; } auto mCheckButton::onToggle(const function& callback) -> type& { @@ -43,9 +43,9 @@ auto mCheckButton::setChecked(bool checked) -> type& { return *this; } -auto mCheckButton::setIcon(const image& icon) -> type& { - state.icon = icon; - signal(setIcon, icon); +auto mCheckButton::setImage(const Image& image) -> type& { + state.image = image; + signal(setImage, image); return *this; } diff --git a/hiro/core/widget/combo-button-item.cpp b/hiro/core/widget/combo-button-item.cpp index ff836d36..9d006aea 100644 --- a/hiro/core/widget/combo-button-item.cpp +++ b/hiro/core/widget/combo-button-item.cpp @@ -6,8 +6,8 @@ auto mComboButtonItem::allocate() -> pObject* { // -auto mComboButtonItem::icon() const -> image { - return state.icon; +auto mComboButtonItem::image() const -> Image { + return state.image; } auto mComboButtonItem::remove() -> type& { @@ -19,9 +19,9 @@ auto mComboButtonItem::selected() const -> bool { return state.selected; } -auto mComboButtonItem::setIcon(const image& icon) -> type& { - state.icon = icon; - signal(setIcon, icon); +auto mComboButtonItem::setImage(const Image& image) -> type& { + state.image = image; + signal(setImage, image); return *this; } diff --git a/hiro/core/widget/frame.cpp b/hiro/core/widget/frame.cpp index e21b2b30..9a172a36 100644 --- a/hiro/core/widget/frame.cpp +++ b/hiro/core/widget/frame.cpp @@ -33,6 +33,13 @@ auto mFrame::reset() -> type& { return *this; } +auto mFrame::setParent(mObject* object, signed offset) -> type& { + if(auto& layout = state.layout) layout->destruct(); + mObject::setParent(object, offset); + if(auto& layout = state.layout) layout->setParent(this, 0); + return *this; +} + auto mFrame::setText(const string& text) -> type& { state.text = text; signal(setText, text); diff --git a/hiro/core/widget/icon-view-item.cpp b/hiro/core/widget/icon-view-item.cpp index bff5694e..61fc4031 100644 --- a/hiro/core/widget/icon-view-item.cpp +++ b/hiro/core/widget/icon-view-item.cpp @@ -6,8 +6,8 @@ auto mIconViewItem::allocate() -> pObject* { // -auto mIconViewItem::icon() const -> image { - return state.icon; +auto mIconViewItem::image() const -> Image { + return state.image; } auto mIconViewItem::remove() -> type& { @@ -19,9 +19,9 @@ auto mIconViewItem::selected() const -> bool { return state.selected; } -auto mIconViewItem::setIcon(const image& icon) -> type& { - state.icon = icon; - signal(setIcon, icon); +auto mIconViewItem::setImage(const Image& image) -> type& { + state.image = image; + signal(setImage, image); return *this; } diff --git a/hiro/core/widget/icon-view.cpp b/hiro/core/widget/icon-view.cpp index 184601b3..9026c95d 100644 --- a/hiro/core/widget/icon-view.cpp +++ b/hiro/core/widget/icon-view.cpp @@ -22,6 +22,18 @@ auto mIconView::backgroundColor() const -> Color { return state.backgroundColor; } +auto mIconView::batchable() const -> bool { + return state.batchable; +} + +auto mIconView::batched() const -> vector { + vector items; + for(auto& item : state.items) { + if(item->selected()) items.append(item); + } + return items; +} + auto mIconView::doActivate() const -> void { if(state.onActivate) return state.onActivate(); } @@ -57,10 +69,6 @@ auto mIconView::items() const -> vector { return items; } -auto mIconView::multiSelect() const -> bool { - return state.multiSelect; -} - auto mIconView::onActivate(const function& callback) -> type& { state.onActivate = callback; return *this; @@ -97,19 +105,11 @@ auto mIconView::reset() -> type& { return *this; } -auto mIconView::selected() const -> maybe { +auto mIconView::selected() const -> IconViewItem { for(auto& item : state.items) { - if(item->selected()) return (unsigned)item->offset(); + if(item->selected()) return item; } - return nothing; -} - -auto mIconView::selectedItems() const -> vector { - vector result; - for(auto& item : state.items) { - if(item->selected()) result.append(item->offset()); - } - return result; + return {}; } auto mIconView::setBackgroundColor(Color color) -> type& { @@ -118,6 +118,12 @@ auto mIconView::setBackgroundColor(Color color) -> type& { return *this; } +auto mIconView::setBatchable(bool batchable) -> type& { + state.batchable = batchable; + signal(setBatchable, batchable); + return *this; +} + auto mIconView::setFlow(Orientation flow) -> type& { state.flow = flow; signal(setFlow, flow); @@ -130,18 +136,19 @@ auto mIconView::setForegroundColor(Color color) -> type& { return *this; } -auto mIconView::setMultiSelect(bool multiSelect) -> type& { - state.multiSelect = multiSelect; - signal(setMultiSelect, multiSelect); - return *this; -} - auto mIconView::setOrientation(Orientation orientation) -> type& { state.orientation = orientation; signal(setOrientation, orientation); return *this; } +auto mIconView::setParent(mObject* parent, signed offset) -> type& { + for(auto n : rrange(state.items)) state.items[n]->destruct(); + mObject::setParent(parent, offset); + for(auto& item : state.items) item->setParent(this, item->offset()); + return *this; +} + auto mIconView::setSelected(const vector& selections) -> type& { bool selectAll = selections(0, 0) == ~0; for(auto& item : state.items) item->state.selected = selectAll; diff --git a/hiro/core/widget/list-view-cell.cpp b/hiro/core/widget/list-view-cell.cpp index bcfc6483..bd4b699f 100644 --- a/hiro/core/widget/list-view-cell.cpp +++ b/hiro/core/widget/list-view-cell.cpp @@ -54,7 +54,7 @@ auto mListViewCell::checked() const -> bool { return state.checkable && state.checked; } -auto mListViewCell::font(bool recursive) const -> string { +auto mListViewCell::font(bool recursive) const -> Font { if(auto font = mObject::font()) return font; if(recursive) { if(auto parent = parentListViewItem()) { @@ -94,8 +94,8 @@ auto mListViewCell::foregroundColor(bool recursive) const -> Color { return state.foregroundColor; } -auto mListViewCell::icon() const -> image { - return state.icon; +auto mListViewCell::image() const -> Image { + return state.image; } auto mListViewCell::setAlignment(Alignment alignment) -> type& { @@ -129,9 +129,9 @@ auto mListViewCell::setForegroundColor(Color color) -> type& { return *this; } -auto mListViewCell::setIcon(const image& icon) -> type& { - state.icon = icon; - signal(setIcon, icon); +auto mListViewCell::setImage(const Image& image) -> type& { + state.image = image; + signal(setImage, image); return *this; } diff --git a/hiro/core/widget/list-view-column.cpp b/hiro/core/widget/list-view-column.cpp index e23e67df..a9223da9 100644 --- a/hiro/core/widget/list-view-column.cpp +++ b/hiro/core/widget/list-view-column.cpp @@ -35,8 +35,8 @@ auto mListViewColumn::horizontalAlignment() const -> double { return state.horizontalAlignment; } -auto mListViewColumn::icon() const -> image { - return state.icon; +auto mListViewColumn::image() const -> Image { + return state.image; } auto mListViewColumn::remove() -> type& { @@ -91,9 +91,9 @@ auto mListViewColumn::setHorizontalAlignment(double alignment) -> type& { return *this; } -auto mListViewColumn::setIcon(const image& icon) -> type& { - state.icon = icon; - signal(setIcon, icon); +auto mListViewColumn::setImage(const Image& image) -> type& { + state.image = image; + signal(setImage, image); return *this; } diff --git a/hiro/core/widget/list-view.cpp b/hiro/core/widget/list-view.cpp index 2307449c..14a8ccb3 100644 --- a/hiro/core/widget/list-view.cpp +++ b/hiro/core/widget/list-view.cpp @@ -194,7 +194,7 @@ auto mListView::setForegroundColor(Color color) -> type& { } auto mListView::setParent(mObject* parent, signed offset) -> type& { - for(auto& item : state.items) item->destruct(); + for(auto n : rrange(state.items)) state.items[n]->destruct(); if(auto& header = state.header) header->destruct(); mObject::setParent(parent, offset); if(auto& header = state.header) header->setParent(this, 0); diff --git a/hiro/core/widget/radio-button.cpp b/hiro/core/widget/radio-button.cpp index e504972a..b95e7066 100644 --- a/hiro/core/widget/radio-button.cpp +++ b/hiro/core/widget/radio-button.cpp @@ -22,8 +22,8 @@ auto mRadioButton::group() const -> Group { return state.group; } -auto mRadioButton::icon() const -> image { - return state.icon; +auto mRadioButton::image() const -> Image { + return state.image; } auto mRadioButton::onActivate(const function& callback) -> type& { @@ -57,15 +57,14 @@ auto mRadioButton::setChecked() -> type& { } auto mRadioButton::setGroup(sGroup group) -> type& { - state.group = group; + state.group = group ? group : Group{&instance}; signal(setGroup, group); - if(group && group->objectCount() == 1) setChecked(); return *this; } -auto mRadioButton::setIcon(const image& icon) -> type& { - state.icon = icon; - signal(setIcon, icon); +auto mRadioButton::setImage(const Image& image) -> type& { + state.image = image; + signal(setImage, image); return *this; } diff --git a/hiro/core/widget/radio-label.cpp b/hiro/core/widget/radio-label.cpp index a4f45ac2..6961ba72 100644 --- a/hiro/core/widget/radio-label.cpp +++ b/hiro/core/widget/radio-label.cpp @@ -39,9 +39,8 @@ auto mRadioLabel::setChecked() -> type& { } auto mRadioLabel::setGroup(sGroup group) -> type& { - state.group = group; + state.group = group ? group : Group{&instance}; signal(setGroup, group); - if(group && group->objectCount() == 1) setChecked(); return *this; } diff --git a/hiro/core/widget/source-edit.cpp b/hiro/core/widget/source-edit.cpp index ec100116..5ef494a5 100644 --- a/hiro/core/widget/source-edit.cpp +++ b/hiro/core/widget/source-edit.cpp @@ -6,6 +6,10 @@ auto mSourceEdit::allocate() -> pObject* { // +auto mSourceEdit::cursor() const -> Cursor { + return state.cursor; +} + auto mSourceEdit::doChange() const -> void { if(state.onChange) return state.onChange(); } @@ -24,22 +28,9 @@ auto mSourceEdit::onMove(const function& callback) -> type& { return *this; } -auto mSourceEdit::position() const -> unsigned { - return state.position; -} - -auto mSourceEdit::setPosition(signed position) -> type& { - state.position = position; - signal(setPosition, position); - return *this; -} - -auto mSourceEdit::setSelected(Position selected) -> type& { - if(selected.x() < 0) return *this; - if(selected.y() < 0) selected.setY(-1); - else if(selected.x() > selected.y()) return *this; - state.selected = selected; - signal(setSelected, selected); +auto mSourceEdit::setCursor(Cursor cursor) -> type& { + state.cursor = cursor; + signal(setCursor, cursor); return *this; } diff --git a/hiro/core/widget/tab-frame-item.cpp b/hiro/core/widget/tab-frame-item.cpp index 2c6998f4..3a7d1cff 100644 --- a/hiro/core/widget/tab-frame-item.cpp +++ b/hiro/core/widget/tab-frame-item.cpp @@ -23,8 +23,8 @@ auto mTabFrameItem::closable() const -> bool { return state.closable; } -auto mTabFrameItem::icon() const -> image { - return state.icon; +auto mTabFrameItem::image() const -> Image { + return state.image; } auto mTabFrameItem::layout() const -> Layout { @@ -62,9 +62,9 @@ auto mTabFrameItem::setClosable(bool closable) -> type& { return *this; } -auto mTabFrameItem::setIcon(const image& icon) -> type& { - state.icon = icon; - signal(setIcon, icon); +auto mTabFrameItem::setImage(const Image& image) -> type& { + state.image = image; + signal(setImage, image); return *this; } diff --git a/hiro/core/widget/tab-frame.cpp b/hiro/core/widget/tab-frame.cpp index d344a629..1ac78bf5 100644 --- a/hiro/core/widget/tab-frame.cpp +++ b/hiro/core/widget/tab-frame.cpp @@ -12,6 +12,7 @@ auto mTabFrame::destruct() -> void { // auto mTabFrame::append(sTabFrameItem item) -> type& { + if(!state.items) item->state.selected = true; state.items.append(item); item->setParent(this, itemCount() - 1); signal(append, item); @@ -30,10 +31,6 @@ auto mTabFrame::doMove(sTabFrameItem from, sTabFrameItem to) const -> void { if(state.onMove) return state.onMove(from, to); } -auto mTabFrame::edge() const -> Edge { - return state.edge; -} - auto mTabFrame::item(unsigned position) const -> TabFrameItem { if(position < itemCount()) return state.items[position]; return {}; @@ -49,6 +46,10 @@ auto mTabFrame::items() const -> vector { return items; } +auto mTabFrame::navigation() const -> Navigation { + return state.navigation; +} + auto mTabFrame::onChange(const function& callback) -> type& { state.onChange = callback; return *this; @@ -87,14 +88,14 @@ auto mTabFrame::selected() const -> TabFrameItem { return {}; } -auto mTabFrame::setEdge(Edge edge) -> type& { - state.edge = edge; - signal(setEdge, edge); +auto mTabFrame::setNavigation(Navigation navigation) -> type& { + state.navigation = navigation; + signal(setNavigation, navigation); return *this; } auto mTabFrame::setParent(mObject* parent, signed offset) -> type& { - for(auto& item : state.items) item->destruct(); + for(auto n : rrange(state.items)) state.items[n]->destruct(); mObject::setParent(parent, offset); for(auto& item : state.items) item->setParent(this, item->offset()); return *this; diff --git a/hiro/core/widget/text-edit.cpp b/hiro/core/widget/text-edit.cpp index 909d7440..14211900 100644 --- a/hiro/core/widget/text-edit.cpp +++ b/hiro/core/widget/text-edit.cpp @@ -10,8 +10,8 @@ auto mTextEdit::backgroundColor() const -> Color { return state.backgroundColor; } -auto mTextEdit::cursorPosition() const -> unsigned { - return state.cursorPosition; +auto mTextEdit::cursor() const -> Cursor { + return state.cursor; } auto mTextEdit::doChange() const -> void { @@ -46,9 +46,9 @@ auto mTextEdit::setBackgroundColor(Color color) -> type& { return *this; } -auto mTextEdit::setCursorPosition(unsigned position) -> type& { - state.cursorPosition = position; - signal(setCursorPosition, position); +auto mTextEdit::setCursor(Cursor cursor) -> type& { + state.cursor = cursor; + signal(setCursor, cursor); return *this; } diff --git a/hiro/core/widget/tree-view-item.cpp b/hiro/core/widget/tree-view-item.cpp index e3f720a1..798282d1 100644 --- a/hiro/core/widget/tree-view-item.cpp +++ b/hiro/core/widget/tree-view-item.cpp @@ -18,12 +18,42 @@ auto mTreeViewItem::append(sTreeViewItem item) -> type& { return *this; } +auto mTreeViewItem::backgroundColor(bool recursive) const -> Color { + if(auto color = state.backgroundColor) return color; + if(recursive) { + if(auto parent = parentTreeViewItem()) { + if(auto color = parent->backgroundColor(true)) return color; + } + if(auto parent = parentTreeView()) { + if(auto color = parent->backgroundColor()) return color; + } + } + return {}; +} + +auto mTreeViewItem::checkable() const -> bool { + return state.checkable; +} + auto mTreeViewItem::checked() const -> bool { return state.checked; } -auto mTreeViewItem::icon() const -> image { - return state.icon; +auto mTreeViewItem::foregroundColor(bool recursive) const -> Color { + if(auto color = state.foregroundColor) return color; + if(recursive) { + if(auto parent = parentTreeViewItem()) { + if(auto color = parent->foregroundColor(true)) return color; + } + if(auto parent = parentTreeView()) { + if(auto color = parent->foregroundColor()) return color; + } + } + return {}; +} + +auto mTreeViewItem::image() const -> Image { + return state.image; } auto mTreeViewItem::item(const string& path) const -> TreeViewItem { @@ -73,23 +103,52 @@ auto mTreeViewItem::selected() const -> bool { return false; } +auto mTreeViewItem::setBackgroundColor(Color color) -> type& { + state.backgroundColor = color; + signal(setBackgroundColor, color); + return *this; +} + +auto mTreeViewItem::setCheckable(bool checkable) -> type& { + state.checkable = checkable; + signal(setCheckable, checkable); + return *this; +} + auto mTreeViewItem::setChecked(bool checked) -> type& { state.checked = checked; signal(setChecked, checked); return *this; } +auto mTreeViewItem::setExpanded(bool expanded) -> type& { + signal(setExpanded, expanded); + return *this; +} + auto mTreeViewItem::setFocused() -> type& { signal(setFocused); return *this; } -auto mTreeViewItem::setIcon(const image& icon) -> type& { - state.icon = icon; - signal(setIcon, icon); +auto mTreeViewItem::setForegroundColor(Color color) -> type& { + state.foregroundColor = color; + signal(setForegroundColor, color); return *this; } +auto mTreeViewItem::setImage(const Image& image) -> type& { + state.image = image; + signal(setImage, image); + return *this; +} + +auto mTreeViewItem::setParent(mObject* parent, signed offset) -> type& { + for(auto n : rrange(state.items)) state.items[n]->destruct(); + mObject::setParent(parent, offset); + for(auto& item : state.items) item->setParent(this, item->offset()); +} + auto mTreeViewItem::setSelected() -> type& { signal(setSelected); return *this; diff --git a/hiro/core/widget/tree-view.cpp b/hiro/core/widget/tree-view.cpp index d93351e0..a3cc3cd6 100644 --- a/hiro/core/widget/tree-view.cpp +++ b/hiro/core/widget/tree-view.cpp @@ -22,15 +22,6 @@ auto mTreeView::backgroundColor() const -> Color { return state.backgroundColor; } -auto mTreeView::checkable() const -> bool { - return state.checkable; -} - -auto mTreeView::collapse() -> type& { - signal(collapse); - return *this; -} - auto mTreeView::doActivate() const -> void { if(state.onActivate) return state.onActivate(); } @@ -47,11 +38,6 @@ auto mTreeView::doToggle(sTreeViewItem item) const -> void { if(state.onToggle) return state.onToggle(item); } -auto mTreeView::expand() -> type& { - signal(expand); - return *this; -} - auto mTreeView::foregroundColor() const -> Color { return state.foregroundColor; } @@ -107,9 +93,7 @@ auto mTreeView::remove(sTreeViewItem item) -> type& { auto mTreeView::reset() -> type& { state.selectedPath.reset(); - signal(reset); - for(auto& item : state.items) item->setParent(); - state.items.reset(); + for(auto n : rrange(state.items)) remove(state.items[n]); return *this; } @@ -123,16 +107,17 @@ auto mTreeView::setBackgroundColor(Color color) -> type& { return *this; } -auto mTreeView::setCheckable(bool checkable) -> type& { - state.checkable = checkable; - signal(setCheckable, checkable); - return *this; -} - auto mTreeView::setForegroundColor(Color color) -> type& { state.foregroundColor = color; signal(setForegroundColor, color); return *this; } +auto mTreeView::setParent(mObject* object, signed offset) -> type& { + for(auto n : rrange(state.items)) state.items[n]->destruct(); + mObject::setParent(object, offset); + for(auto& item : state.items) item->setParent(this, item->offset()); + return *this; +} + #endif diff --git a/hiro/extension/browser-dialog.cpp b/hiro/extension/browser-dialog.cpp index 2de7b70a..89551ad8 100644 --- a/hiro/extension/browser-dialog.cpp +++ b/hiro/extension/browser-dialog.cpp @@ -15,8 +15,8 @@ private: VerticalLayout layout{&window}; HorizontalLayout pathLayout{&layout, Size{~0, 0}, 5}; LineEdit pathName{&pathLayout, Size{~0, 0}, 0}; - Button pathHome{&pathLayout, Size{0, 0}, 0}; Button pathRefresh{&pathLayout, Size{0, 0}, 0}; + Button pathHome{&pathLayout, Size{0, 0}, 0}; Button pathUp{&pathLayout, Size{0, 0}, 0}; ListView view{&layout, Size{~0, ~0}, 5}; HorizontalLayout controlLayout{&layout, Size{~0, 0}}; @@ -118,9 +118,9 @@ auto BrowserDialogWindow::run() -> lstring { layout.setMargin(5); pathName.onActivate([&] { setPath(pathName.text()); }); - pathHome.setBordered(false).setIcon(Icon::Go::Home).onActivate([&] { setPath(userpath()); }); - pathRefresh.setBordered(false).setIcon(Icon::Action::Refresh).onActivate([&] { setPath(state.path); }); - pathUp.setBordered(false).setIcon(Icon::Go::Up).onActivate([&] { setPath(state.path.dirname()); }); + pathRefresh.setBordered(false).setImage(Icon::Action::Refresh).onActivate([&] { setPath(state.path); }); + pathHome.setBordered(false).setImage(Icon::Go::Home).onActivate([&] { setPath(userpath()); }); + pathUp.setBordered(false).setImage(Icon::Go::Up).onActivate([&] { setPath(state.path.dirname()); }); view.setBatchable(state.action == "openFiles").onActivate([&] { activate(); }).onChange([&] { change(); }); filterList.setVisible(state.action != "selectFolder").onChange([&] { setPath(state.path); }); for(auto& filter : state.filters) { @@ -175,7 +175,7 @@ auto BrowserDialogWindow::setPath(string path) -> void { if(folderMode && isMatch(content)) continue; view.append(ListViewItem() - .append(ListViewCell().setText(content).setIcon(Icon::Emblem::Folder)) + .append(ListViewCell().setText(content).setImage(Icon::Emblem::Folder)) ); } @@ -185,7 +185,7 @@ auto BrowserDialogWindow::setPath(string path) -> void { if(!isMatch(content)) continue; view.append(ListViewItem() - .append(ListViewCell().setText(content).setIcon(folderMode ? Icon::Action::Open : Icon::Emblem::File)) + .append(ListViewCell().setText(content).setImage(folderMode ? Icon::Action::Open : Icon::Emblem::File)) ); } diff --git a/hiro/extension/fixed-layout.cpp b/hiro/extension/fixed-layout.cpp index cbabcbd0..8fe302f9 100644 --- a/hiro/extension/fixed-layout.cpp +++ b/hiro/extension/fixed-layout.cpp @@ -7,6 +7,14 @@ auto mFixedLayout::append(sSizable sizable, Geometry geometry) -> type& { return *this; } +auto mFixedLayout::modify(sSizable sizable, Geometry geometry) -> type& { + if(sizable && this->sizable(sizable->offset()) == sizable) { + auto& properties = this->properties[sizable->offset()]; + properties.geometry = geometry; + } + return *this; +} + auto mFixedLayout::minimumSize() const -> Size { signed width = Size::Minimum, height = Size::Minimum; for(auto n : range(sizableCount())) { @@ -36,7 +44,7 @@ auto mFixedLayout::setEnabled(bool enabled) -> type& { return *this; } -auto mFixedLayout::setFont(const string& font) -> type& { +auto mFixedLayout::setFont(const Font& font) -> type& { mLayout::setFont(font); for(auto n : range(sizableCount())) { sizable(n)->setFont(sizable(n)->font()); diff --git a/hiro/extension/fixed-layout.hpp b/hiro/extension/fixed-layout.hpp index 3d4c94f8..bcfb2e6c 100644 --- a/hiro/extension/fixed-layout.hpp +++ b/hiro/extension/fixed-layout.hpp @@ -6,11 +6,12 @@ struct mFixedLayout : mLayout { using mLayout::remove; auto append(sSizable sizable, Geometry geometry) -> type&; + auto modify(sSizable sizable, Geometry geometry) -> type&; auto minimumSize() const -> Size override; auto remove(sSizable sizable) -> type& override; auto reset() -> type& override; auto setEnabled(bool enabled = true) -> type& override; - auto setFont(const string& font = "") -> type& override; + auto setFont(const Font& font = {}) -> type& override; auto setVisible(bool visible = true) ->type& override; struct Properties { diff --git a/hiro/extension/horizontal-layout.cpp b/hiro/extension/horizontal-layout.cpp index a3fe9344..35f11ca6 100644 --- a/hiro/extension/horizontal-layout.cpp +++ b/hiro/extension/horizontal-layout.cpp @@ -6,6 +6,16 @@ auto mHorizontalLayout::append(sSizable sizable, Size size, signed spacing) -> t return *this; } +auto mHorizontalLayout::modify(sSizable sizable, Size size, signed spacing) -> type& { + if(sizable && this->sizable(sizable->offset()) == sizable) { + auto& properties = this->properties[sizable->offset()]; + properties.width = size.width(); + properties.height = size.height(); + properties.spacing = spacing; + } + return *this; +} + auto mHorizontalLayout::minimumSize() const -> Size { signed width = 0, height = 0; @@ -56,7 +66,7 @@ auto mHorizontalLayout::setEnabled(bool enabled) -> type& { return *this; } -auto mHorizontalLayout::setFont(const string& font) -> type& { +auto mHorizontalLayout::setFont(const Font& font) -> type& { mLayout::setFont(font); for(auto n : range(sizableCount())) { sizable(n)->setFont(sizable(n)->font()); diff --git a/hiro/extension/horizontal-layout.hpp b/hiro/extension/horizontal-layout.hpp index 783a2768..13a4d95a 100644 --- a/hiro/extension/horizontal-layout.hpp +++ b/hiro/extension/horizontal-layout.hpp @@ -7,11 +7,12 @@ struct mHorizontalLayout : mLayout { auto append(sSizable sizable, Size size, signed spacing = 5) -> type&; auto minimumSize() const -> Size override; + auto modify(sSizable sizable, Size size, signed spacing = 5) -> type&; auto remove(sSizable sizable) -> type& override; auto reset() -> type& override; auto setAlignment(double alignment = 0.5) -> type&; auto setEnabled(bool enabled = true) -> type& override; - auto setFont(const string& font = "") -> type& override; + auto setFont(const Font& font = {}) -> type& override; auto setGeometry(Geometry geometry) -> type& override; auto setMargin(signed margin = 0) -> type&; auto setSpacing(signed spacing = 5) -> type&; diff --git a/hiro/extension/message-dialog.cpp b/hiro/extension/message-dialog.cpp index 2f1b5bde..31a4c894 100644 --- a/hiro/extension/message-dialog.cpp +++ b/hiro/extension/message-dialog.cpp @@ -6,19 +6,19 @@ MessageDialog::MessageDialog(const string& text) { auto MessageDialog::error(const lstring& buttons) -> string { state.buttons = buttons; - state.icon = Icon::Prompt::Error; + state.image = Icon::Prompt::Error; return _run(); } auto MessageDialog::information(const lstring& buttons) -> string { state.buttons = buttons; - state.icon = Icon::Prompt::Information; + state.image = Icon::Prompt::Information; return _run(); } auto MessageDialog::question(const lstring& buttons) -> string { state.buttons = buttons; - state.icon = Icon::Prompt::Question; + state.image = Icon::Prompt::Question; return _run(); } @@ -39,7 +39,7 @@ auto MessageDialog::setTitle(const string& title) -> type& { auto MessageDialog::warning(const lstring& buttons) -> string { state.buttons = buttons; - state.icon = Icon::Prompt::Warning; + state.image = Icon::Prompt::Warning; return _run(); } @@ -53,7 +53,7 @@ auto MessageDialog::_run() -> string { Widget controlSpacer{&controlLayout, Size{~0, 0}}; layout.setMargin(5); - messageIcon.setIcon(state.icon); + messageIcon.setImage(state.image); messageText.setText(state.text); for(auto n : range(state.buttons)) { Button button{&controlLayout, Size{80, 0}, 5}; @@ -62,7 +62,7 @@ auto MessageDialog::_run() -> string { button.setFocused(); //the last button will have effective focus } - signed widthMessage = 5 + 16 + 5 + Font::size(Font::sans(), state.text).width() + 5; + signed widthMessage = 5 + 16 + 5 + Font().size(state.text).width() + 5; signed widthButtons = 5 + state.buttons.size() * 85; signed width = max(320, widthMessage, widthButtons); diff --git a/hiro/extension/message-dialog.hpp b/hiro/extension/message-dialog.hpp index 64f845a1..480beedb 100644 --- a/hiro/extension/message-dialog.hpp +++ b/hiro/extension/message-dialog.hpp @@ -15,7 +15,7 @@ struct MessageDialog { private: struct State { lstring buttons; - vector icon; + vector image; sWindow parent; string response; string text; diff --git a/hiro/extension/shared.hpp b/hiro/extension/shared.hpp index b432b451..de05c1c9 100644 --- a/hiro/extension/shared.hpp +++ b/hiro/extension/shared.hpp @@ -4,6 +4,7 @@ struct FixedLayout : sFixedLayout { DeclareSharedLayout(FixedLayout) auto append(sSizable sizable, Geometry geometry) { return self().append(sizable, geometry), *this; } + auto modify(sSizable sizable, Geometry geometry) { return self().modify(sizable, geometry), *this; } }; #endif @@ -13,6 +14,7 @@ struct HorizontalLayout : sHorizontalLayout { DeclareSharedLayout(HorizontalLayout) auto append(sSizable sizable, Size size, signed spacing = 5) { return self().append(sizable, size, spacing), *this; } + auto modify(sSizable sizable, Size size, signed spacing = 5) { return self().modify(sizable, size, spacing), *this; } auto setAlignment(double alignment = 0.5) { return self().setAlignment(alignment), *this; } auto setMargin(signed margin = 0) { return self().setMargin(margin), *this; } auto setSpacing(signed spacing = 5) { return self().setSpacing(spacing), *this; } @@ -25,6 +27,7 @@ struct VerticalLayout : sVerticalLayout { DeclareSharedLayout(VerticalLayout) auto append(sSizable sizable, Size size, signed spacing = 5) { return self().append(sizable, size, spacing), *this; } + auto modify(sSizable sizable, Size size, signed spacing = 5) { return self().modify(sizable, size, spacing), *this; } auto setAlignment(double alignment = 0.0) { return self().setAlignment(alignment), *this; } auto setMargin(signed margin = 0) { return self().setMargin(margin), *this; } auto setSpacing(signed spacing = 5) { return self().setSpacing(spacing), *this; } diff --git a/hiro/extension/vertical-layout.cpp b/hiro/extension/vertical-layout.cpp index 1c799a02..d5a2867a 100644 --- a/hiro/extension/vertical-layout.cpp +++ b/hiro/extension/vertical-layout.cpp @@ -6,6 +6,16 @@ auto mVerticalLayout::append(sSizable sizable, Size size, signed spacing) -> typ return *this; } +auto mVerticalLayout::modify(sSizable sizable, Size size, signed spacing) -> type& { + if(sizable && this->sizable(sizable->offset()) == sizable) { + auto& properties = this->properties[sizable->offset()]; + properties.width = size.width(); + properties.height = size.height(); + properties.spacing = spacing; + } + return *this; +} + auto mVerticalLayout::minimumSize() const -> Size { signed width = 0, height = 0; @@ -56,7 +66,7 @@ auto mVerticalLayout::setEnabled(bool enabled) -> type& { return *this; } -auto mVerticalLayout::setFont(const string& font) -> type& { +auto mVerticalLayout::setFont(const Font& font) -> type& { mLayout::setFont(font); for(auto n : range(sizableCount())) { sizable(n)->setFont(sizable(n)->font()); diff --git a/hiro/extension/vertical-layout.hpp b/hiro/extension/vertical-layout.hpp index f8dc968d..c208fc23 100644 --- a/hiro/extension/vertical-layout.hpp +++ b/hiro/extension/vertical-layout.hpp @@ -6,12 +6,13 @@ struct mVerticalLayout : mLayout { using mLayout::remove; auto append(sSizable sizable, Size size, signed spacing = 5) -> type&; + auto modify(sSizable sizable, Size size, signed spacing = 5) -> type&; auto minimumSize() const -> Size override; auto remove(sSizable sizable) -> type& override; auto reset() -> type& override; auto setAlignment(double alignment = 0.0) -> type&; auto setEnabled(bool enabled = true) -> type& override; - auto setFont(const string& font = "") -> type& override; + auto setFont(const Font& font = {}) -> type& override; auto setGeometry(Geometry geometry) -> type& override; auto setMargin(signed margin = 0) -> type&; auto setSpacing(signed spacing = 5) -> type&; diff --git a/hiro/gtk/action/action.cpp b/hiro/gtk/action/action.cpp index 9b0cd10a..010125bd 100644 --- a/hiro/gtk/action/action.cpp +++ b/hiro/gtk/action/action.cpp @@ -12,7 +12,7 @@ auto pAction::setEnabled(bool enabled) -> void { gtk_widget_set_sensitive(widget, enabled); } -auto pAction::setFont(const string& font) -> void { +auto pAction::setFont(const Font& font) -> void { pFont::setFont(widget, font); } diff --git a/hiro/gtk/action/action.hpp b/hiro/gtk/action/action.hpp index 2117d49a..62fe99fb 100644 --- a/hiro/gtk/action/action.hpp +++ b/hiro/gtk/action/action.hpp @@ -6,7 +6,7 @@ struct pAction : pObject { Declare(Action, Object) auto setEnabled(bool enabled) -> void override; - auto setFont(const string& font) -> void override; + auto setFont(const Font& font) -> void override; auto setVisible(bool visible) -> void override; auto _mnemonic(string text) -> string; diff --git a/hiro/gtk/action/menu-item.cpp b/hiro/gtk/action/menu-item.cpp index 383775e9..92ae2b92 100644 --- a/hiro/gtk/action/menu-item.cpp +++ b/hiro/gtk/action/menu-item.cpp @@ -9,6 +9,7 @@ static auto MenuItem_activate(GtkMenuItem*, pMenuItem* p) -> void { auto pMenuItem::construct() -> void { widget = gtk_image_menu_item_new_with_mnemonic(""); g_signal_connect(G_OBJECT(widget), "activate", G_CALLBACK(MenuItem_activate), (gpointer)this); + setImage(state().image); setText(state().text); } @@ -16,9 +17,9 @@ auto pMenuItem::destruct() -> void { if(widget) gtk_widget_destroy(widget), widget = nullptr; } -auto pMenuItem::setIcon(const image& icon) -> void { - if(icon) { - GtkImage* gtkImage = CreateImage(icon, true); +auto pMenuItem::setImage(const Image& image) -> void { + if(image) { + GtkImage* gtkImage = CreateImage(image, true); gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(widget), (GtkWidget*)gtkImage); } else { gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(widget), nullptr); diff --git a/hiro/gtk/action/menu-item.hpp b/hiro/gtk/action/menu-item.hpp index 8d980c34..263b3bb4 100644 --- a/hiro/gtk/action/menu-item.hpp +++ b/hiro/gtk/action/menu-item.hpp @@ -5,7 +5,7 @@ namespace hiro { struct pMenuItem : pAction { Declare(MenuItem, Action) - auto setIcon(const image& icon) -> void; + auto setImage(const Image& image) -> void; auto setText(const string& text) -> void; }; diff --git a/hiro/gtk/action/menu-radio-item.cpp b/hiro/gtk/action/menu-radio-item.cpp index 8dc75941..f1e28e71 100644 --- a/hiro/gtk/action/menu-radio-item.cpp +++ b/hiro/gtk/action/menu-radio-item.cpp @@ -27,8 +27,6 @@ auto pMenuRadioItem::setChecked() -> void { } auto pMenuRadioItem::setGroup(sGroup group) -> void { - if(!group) return; - maybe gtkRadioMenuItem; for(auto& weak : group->state.objects) { if(auto object = weak.acquire()) { @@ -36,9 +34,13 @@ auto pMenuRadioItem::setGroup(sGroup group) -> void { if(auto self = menuRadioItem->self()) { self->lock(); gtk_radio_menu_item_set_group(self->gtkRadioMenuItem, nullptr); - if(!gtkRadioMenuItem) gtkRadioMenuItem = self->gtkRadioMenuItem; - else gtk_radio_menu_item_set_group(self->gtkRadioMenuItem, gtk_radio_menu_item_get_group(*gtkRadioMenuItem)); - gtk_check_menu_item_set_active(self->gtkCheckMenuItem, menuRadioItem->checked()); + if(!gtkRadioMenuItem) { + gtkRadioMenuItem = self->gtkRadioMenuItem; + gtk_check_menu_item_set_active(self->gtkCheckMenuItem, menuRadioItem->state.checked = true); + } else { + gtk_radio_menu_item_set_group(self->gtkRadioMenuItem, gtk_radio_menu_item_get_group(*gtkRadioMenuItem)); + gtk_check_menu_item_set_active(self->gtkCheckMenuItem, menuRadioItem->state.checked = false); + } self->unlock(); } } diff --git a/hiro/gtk/action/menu.cpp b/hiro/gtk/action/menu.cpp index 574ac7ea..34c95572 100644 --- a/hiro/gtk/action/menu.cpp +++ b/hiro/gtk/action/menu.cpp @@ -6,6 +6,7 @@ auto pMenu::construct() -> void { gtkMenu = gtk_menu_new(); widget = gtk_image_menu_item_new_with_mnemonic(""); gtk_menu_item_set_submenu(GTK_MENU_ITEM(widget), gtkMenu); + setImage(state().image); setText(state().text); for(auto& action : state().actions) append(*action); @@ -27,16 +28,16 @@ auto pMenu::append(sAction action) -> void { auto pMenu::remove(sAction action) -> void { } -auto pMenu::setFont(const string& font) -> void { +auto pMenu::setFont(const Font& font) -> void { pAction::setFont(font); for(auto& action : state().actions) { if(action->self()) action->self()->setFont(action->font(true)); } } -auto pMenu::setIcon(const image& icon) -> void { - if(icon) { - GtkImage* gtkImage = CreateImage(icon, true); +auto pMenu::setImage(const Image& image) -> void { + if(image) { + GtkImage* gtkImage = CreateImage(image, true); gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(widget), (GtkWidget*)gtkImage); } else { gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(widget), nullptr); diff --git a/hiro/gtk/action/menu.hpp b/hiro/gtk/action/menu.hpp index a198cf09..34f0b8de 100644 --- a/hiro/gtk/action/menu.hpp +++ b/hiro/gtk/action/menu.hpp @@ -7,8 +7,8 @@ struct pMenu : pAction { auto append(sAction action) -> void; auto remove(sAction action) -> void; - auto setFont(const string& font) -> void override; - auto setIcon(const image& icon) -> void; + auto setFont(const Font& font) -> void override; + auto setImage(const Image& image) -> void; auto setText(const string& text) -> void; GtkWidget* gtkMenu = nullptr; diff --git a/hiro/gtk/font.cpp b/hiro/gtk/font.cpp index 5addb5c6..1a85e97f 100644 --- a/hiro/gtk/font.cpp +++ b/hiro/gtk/font.cpp @@ -2,67 +2,54 @@ namespace hiro { -auto pFont::serif(unsigned size, string style) -> string { - if(size == 0) size = 8; - if(style == "") style = "Normal"; - return {"Serif, ", size, ", ", style}; -} - -auto pFont::sans(unsigned size, string style) -> string { - if(size == 0) size = 8; - if(style == "") style = "Normal"; - return {"Sans, ", size, ", ", style}; -} - -auto pFont::monospace(unsigned size, string style) -> string { - if(size == 0) size = 8; - return {"Liberation Mono, ", size, ", ", style}; -} - -auto pFont::size(string font, string text) -> Size { +auto pFont::size(const Font& font, const string& text) -> Size { PangoFontDescription* description = create(font); Size size = pFont::size(description, text); free(description); return size; } -auto pFont::create(string description) -> PangoFontDescription* { - lstring part = description.split(",", 2L).strip(); +auto pFont::size(PangoFontDescription* font, const string& text) -> Size { + PangoContext* context = gdk_pango_context_get_for_screen(gdk_screen_get_default()); + PangoLayout* layout = pango_layout_new(context); + pango_layout_set_font_description(layout, font); + pango_layout_set_text(layout, text, -1); + signed width = 0, height = 0; + pango_layout_get_pixel_size(layout, &width, &height); + g_object_unref((gpointer)layout); + return {width, height}; +} - string family = "Sans"; - unsigned size = 8u; - bool bold = false; - bool italic = false; +auto pFont::family(const string& family) -> string { + #if defined(DISPLAY_WINDOWS) + if(family == Font::Sans ) return "Tahoma"; + if(family == Font::Serif) return "Georgia"; + if(family == Font::Mono ) return "Lucida Console"; + return family ? family : "Tahoma"; + #elif defined(DISPLAY_XORG) + if(family == Font::Sans ) return "Sans"; + if(family == Font::Serif) return "Serif"; + if(family == Font::Mono ) return "Liberation Mono"; + return family ? family : "Sans"; + #else + return family; + #endif +} - if(part[0] != "") family = part[0]; - if(part.size() >= 2) size = decimal(part[1]); - if(part.size() >= 3) bold = (bool)part[2].find("Bold"); - if(part.size() >= 3) italic = (bool)part[2].find("Italic"); - - PangoFontDescription* font = pango_font_description_new(); - pango_font_description_set_family(font, family); - pango_font_description_set_size(font, size * PANGO_SCALE); - pango_font_description_set_weight(font, !bold ? PANGO_WEIGHT_NORMAL : PANGO_WEIGHT_BOLD); - pango_font_description_set_style(font, !italic ? PANGO_STYLE_NORMAL : PANGO_STYLE_OBLIQUE); - return font; +auto pFont::create(const Font& font) -> PangoFontDescription* { + auto p = pango_font_description_new(); + pango_font_description_set_family(p, family(font.family())); + pango_font_description_set_size(p, (font.size() ? font.size() : 8) * PANGO_SCALE); + pango_font_description_set_weight(p, font.bold() ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL); + pango_font_description_set_style(p, font.italic() ? PANGO_STYLE_OBLIQUE : PANGO_STYLE_NORMAL); + return p; } auto pFont::free(PangoFontDescription* font) -> void { pango_font_description_free(font); } -auto pFont::size(PangoFontDescription* font, string text) -> Size { - PangoContext* context = gdk_pango_context_get_for_screen(gdk_screen_get_default()); - PangoLayout* layout = pango_layout_new(context); - pango_layout_set_font_description(layout, font); - pango_layout_set_text(layout, text, -1); - int width = 0, height = 0; - pango_layout_get_pixel_size(layout, &width, &height); - g_object_unref((gpointer)layout); - return {width, height}; -} - -auto pFont::setFont(GtkWidget* widget, string font) -> void { +auto pFont::setFont(GtkWidget* widget, const Font& font) -> void { auto gtkFont = pFont::create(font); pFont::setFont(widget, (gpointer)gtkFont); pFont::free(gtkFont); diff --git a/hiro/gtk/font.hpp b/hiro/gtk/font.hpp index 71101ddc..3ad59dd1 100644 --- a/hiro/gtk/font.hpp +++ b/hiro/gtk/font.hpp @@ -3,15 +3,12 @@ namespace hiro { struct pFont { - static auto serif(unsigned size, string style) -> string; - static auto sans(unsigned size, string style) -> string; - static auto monospace(unsigned size, string style) -> string; - static auto size(string font, string text) -> Size; - - static auto create(string description) -> PangoFontDescription*; + static auto size(const Font& font, const string& text) -> Size; + static auto size(PangoFontDescription* font, const string& text) -> Size; + static auto family(const string& family) -> string; + static auto create(const Font& font) -> PangoFontDescription*; static auto free(PangoFontDescription* font) -> void; - static auto size(PangoFontDescription* font, string text) -> Size; - static auto setFont(GtkWidget* widget, string font) -> void; + static auto setFont(GtkWidget* widget, const Font& font) -> void; static auto setFont(GtkWidget* widget, gpointer font) -> void; }; diff --git a/hiro/gtk/hotkey.cpp b/hiro/gtk/hotkey.cpp deleted file mode 100644 index 608ba7bb..00000000 --- a/hiro/gtk/hotkey.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#if defined(Hiro_Hotkey) - -namespace hiro { - -auto pHotkey::construct() -> void { -} - -auto pHotkey::destruct() -> void { -} - -} - -#endif diff --git a/hiro/gtk/hotkey.hpp b/hiro/gtk/hotkey.hpp deleted file mode 100644 index b1caa7bc..00000000 --- a/hiro/gtk/hotkey.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#if defined(Hiro_Hotkey) - -namespace hiro { - -struct pHotkey : pObject { - Declare(Hotkey, Object) -}; - -} - -#endif diff --git a/hiro/gtk/layout.cpp b/hiro/gtk/layout.cpp index 4872f933..6420d2fe 100644 --- a/hiro/gtk/layout.cpp +++ b/hiro/gtk/layout.cpp @@ -13,7 +13,7 @@ auto pLayout::destruct() -> void { auto pLayout::setEnabled(bool enabled) -> void { } -auto pLayout::setFont(const string& font) -> void { +auto pLayout::setFont(const Font& font) -> void { } auto pLayout::setVisible(bool visible) -> void { diff --git a/hiro/gtk/layout.hpp b/hiro/gtk/layout.hpp index 3895710c..dba11836 100644 --- a/hiro/gtk/layout.hpp +++ b/hiro/gtk/layout.hpp @@ -6,7 +6,7 @@ struct pLayout : pSizable { Declare(Layout, Sizable) auto setEnabled(bool enabled) -> void override; - auto setFont(const string& font) -> void override; + auto setFont(const Font& font) -> void override; auto setVisible(bool visible) -> void override; }; diff --git a/hiro/gtk/menu-bar.cpp b/hiro/gtk/menu-bar.cpp index eb7b2e37..d1522dd3 100644 --- a/hiro/gtk/menu-bar.cpp +++ b/hiro/gtk/menu-bar.cpp @@ -27,7 +27,7 @@ auto pMenuBar::setEnabled(bool enabled) -> void { } } -auto pMenuBar::setFont(const string& font) -> void { +auto pMenuBar::setFont(const Font& font) -> void { if(auto parent = _parent()) { parent->_setMenuFont(font); } diff --git a/hiro/gtk/menu-bar.hpp b/hiro/gtk/menu-bar.hpp index 6fd9801b..25ffbda0 100644 --- a/hiro/gtk/menu-bar.hpp +++ b/hiro/gtk/menu-bar.hpp @@ -8,7 +8,7 @@ struct pMenuBar : pObject { auto append(sMenu menu) -> void; auto remove(sMenu menu) -> void; auto setEnabled(bool enabled) -> void override; - auto setFont(const string& font) -> void override; + auto setFont(const Font& font) -> void override; auto setVisible(bool visible) -> void override; auto _parent() -> pWindow*; diff --git a/hiro/gtk/object.cpp b/hiro/gtk/object.cpp index fae97d8c..3c6ac442 100644 --- a/hiro/gtk/object.cpp +++ b/hiro/gtk/object.cpp @@ -24,7 +24,7 @@ auto pObject::setEnabled(bool enabled) -> void { auto pObject::setFocused() -> void { } -auto pObject::setFont(const string& font) -> void { +auto pObject::setFont(const Font& font) -> void { } auto pObject::setVisible(bool visible) -> void { diff --git a/hiro/gtk/object.hpp b/hiro/gtk/object.hpp index 84469aa5..056ed0b7 100644 --- a/hiro/gtk/object.hpp +++ b/hiro/gtk/object.hpp @@ -15,7 +15,7 @@ struct pObject { virtual auto reset() -> void; virtual auto setEnabled(bool enabled) -> void; virtual auto setFocused() -> void; - virtual auto setFont(const string& font) -> void; + virtual auto setFont(const Font& font) -> void; virtual auto setVisible(bool visible) -> void; auto locked() const -> bool { return locks != 0; } diff --git a/hiro/gtk/platform.cpp b/hiro/gtk/platform.cpp index 3253c49d..72a42b4a 100644 --- a/hiro/gtk/platform.cpp +++ b/hiro/gtk/platform.cpp @@ -12,7 +12,6 @@ #include "object.cpp" #include "group.cpp" -#include "hotkey.cpp" #include "timer.cpp" #include "window.cpp" #include "status-bar.cpp" diff --git a/hiro/gtk/platform.hpp b/hiro/gtk/platform.hpp index c572eab1..828959ed 100644 --- a/hiro/gtk/platform.hpp +++ b/hiro/gtk/platform.hpp @@ -23,7 +23,6 @@ namespace hiro { #include "object.hpp" #include "group.hpp" -#include "hotkey.hpp" #include "timer.hpp" #include "window.hpp" #include "status-bar.hpp" diff --git a/hiro/gtk/popup-menu.cpp b/hiro/gtk/popup-menu.cpp index 2f0f9d91..65fa6a3f 100644 --- a/hiro/gtk/popup-menu.cpp +++ b/hiro/gtk/popup-menu.cpp @@ -21,7 +21,7 @@ auto pPopupMenu::append(sAction action) -> void { auto pPopupMenu::remove(sAction action) -> void { } -auto pPopupMenu::setFont(const string& font) -> void { +auto pPopupMenu::setFont(const Font& font) -> void { for(auto& action : state().actions) { if(action->self()) action->self()->setFont(action->font(true)); } diff --git a/hiro/gtk/popup-menu.hpp b/hiro/gtk/popup-menu.hpp index 631ac17b..2c44cf7f 100644 --- a/hiro/gtk/popup-menu.hpp +++ b/hiro/gtk/popup-menu.hpp @@ -7,7 +7,7 @@ struct pPopupMenu : pObject { auto append(sAction action) -> void; auto remove(sAction action) -> void; - auto setFont(const string& font) -> void override; + auto setFont(const Font& font) -> void override; auto setVisible(bool visible) -> void; GtkWidget* gtkMenu = nullptr; diff --git a/hiro/gtk/status-bar.cpp b/hiro/gtk/status-bar.cpp index a58eae53..3d4d88d6 100644 --- a/hiro/gtk/status-bar.cpp +++ b/hiro/gtk/status-bar.cpp @@ -14,7 +14,7 @@ auto pStatusBar::setEnabled(bool enabled) -> void { } } -auto pStatusBar::setFont(const string& font) -> void { +auto pStatusBar::setFont(const Font& font) -> void { if(auto parent = _parent()) { parent->_setStatusFont(font); } diff --git a/hiro/gtk/status-bar.hpp b/hiro/gtk/status-bar.hpp index 61655437..7e2a9be6 100644 --- a/hiro/gtk/status-bar.hpp +++ b/hiro/gtk/status-bar.hpp @@ -6,7 +6,7 @@ struct pStatusBar : pObject { Declare(StatusBar, Object) auto setEnabled(bool enabled) -> void override; - auto setFont(const string& font) -> void override; + auto setFont(const Font& font) -> void override; auto setText(const string& text) -> void; auto setVisible(bool visible) -> void override; diff --git a/hiro/gtk/utility.cpp b/hiro/gtk/utility.cpp index 8593481b..41d5db38 100644 --- a/hiro/gtk/utility.cpp +++ b/hiro/gtk/utility.cpp @@ -20,6 +20,34 @@ static auto CreatePixbuf(image icon, bool scale = false) -> GdkPixbuf* { auto pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, icon.width(), icon.height()); memory::copy(gdk_pixbuf_get_pixels(pixbuf), icon.data(), icon.size()); + if(scale) { + auto scaled = gdk_pixbuf_scale_simple(pixbuf, 15, 15, GDK_INTERP_BILINEAR); + g_object_unref(pixbuf); + pixbuf = scaled; + } + + return pixbuf; +} + +static auto CreatePixbuf(const Image& image, bool scale = false) -> GdkPixbuf* { + if(!image.state.data) return nullptr; + + auto pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, image.width(), image.height()); + + //ARGB -> ABGR conversion + const uint32_t* source = image.data(); + uint32_t* target = (uint32_t*)gdk_pixbuf_get_pixels(pixbuf); + for(auto n : range(image.width() * image.height())) { + uint32_t pixel = *source++; + *target++ = (pixel & 0x00ff0000) >> 16 | (pixel & 0xff00ff00) | (pixel & 0x000000ff) << 16; + } + + if(scale) { + auto scaled = gdk_pixbuf_scale_simple(pixbuf, 15, 15, GDK_INTERP_BILINEAR); + g_object_unref(pixbuf); + pixbuf = scaled; + } + return pixbuf; } @@ -30,6 +58,13 @@ static auto CreateImage(const nall::image& image, bool scale = false) -> GtkImag return gtkImage; } +static auto CreateImage(const Image& image, bool scale = false) -> GtkImage* { + auto pixbuf = CreatePixbuf(image, scale); + auto gtkImage = (GtkImage*)gtk_image_new_from_pixbuf(pixbuf); + g_object_unref(pixbuf); + return gtkImage; +} + static auto DropPaths(GtkSelectionData* data) -> lstring { gchar** uris = gtk_selection_data_get_uris(data); if(uris == nullptr) return {}; diff --git a/hiro/gtk/widget/button.cpp b/hiro/gtk/widget/button.cpp index 764af857..63a28c6e 100644 --- a/hiro/gtk/widget/button.cpp +++ b/hiro/gtk/widget/button.cpp @@ -9,7 +9,7 @@ auto pButton::construct() -> void { gtkButton = GTK_BUTTON(gtkWidget); setBordered(state().bordered); - setIcon(state().icon); + setImage(state().image); setOrientation(state().orientation); setText(state().text); @@ -26,13 +26,13 @@ auto pButton::minimumSize() const -> Size { Size size = pFont::size(self().font(true), state().text); if(state().orientation == Orientation::Horizontal) { - size.setWidth(size.width() + state().icon.width()); - size.setHeight(max(size.height(), state().icon.height())); + size.setWidth(size.width() + state().image.width()); + size.setHeight(max(size.height(), state().image.height())); } if(state().orientation == Orientation::Vertical) { - size.setWidth(max(size.width(), state().icon.width())); - size.setHeight(size.height() + state().icon.height()); + size.setWidth(max(size.width(), state().image.width())); + size.setHeight(size.height() + state().image.height()); } return {size.width() + (state().text ? 24 : 12), size.height() + 12}; @@ -42,9 +42,9 @@ auto pButton::setBordered(bool bordered) -> void { gtk_button_set_relief(gtkButton, bordered ? GTK_RELIEF_NORMAL : GTK_RELIEF_NONE); } -auto pButton::setIcon(const image& icon) -> void { - if(icon) { - auto gtkImage = CreateImage(icon); +auto pButton::setImage(const Image& image) -> void { + if(image) { + auto gtkImage = CreateImage(image); gtk_button_set_image(gtkButton, (GtkWidget*)gtkImage); } else { gtk_button_set_image(gtkButton, nullptr); diff --git a/hiro/gtk/widget/button.hpp b/hiro/gtk/widget/button.hpp index 76108e67..3a769248 100644 --- a/hiro/gtk/widget/button.hpp +++ b/hiro/gtk/widget/button.hpp @@ -7,7 +7,7 @@ struct pButton : pWidget { auto minimumSize() const -> Size override; auto setBordered(bool bordered) -> void; - auto setIcon(const image& icon) -> void; + auto setImage(const Image& image) -> void; auto setOrientation(Orientation orientation) -> void; auto setText(const string& text) -> void; diff --git a/hiro/gtk/widget/canvas.cpp b/hiro/gtk/widget/canvas.cpp index 2761c1c2..d92beb73 100644 --- a/hiro/gtk/widget/canvas.cpp +++ b/hiro/gtk/widget/canvas.cpp @@ -73,16 +73,11 @@ auto pCanvas::destruct() -> void { } auto pCanvas::minimumSize() const -> Size { - return {max(0, state().size.width()), max(0, state().size.height())}; + if(auto& image = state().image) return image.size(); + return {0, 0}; } auto pCanvas::setColor(Color color) -> void { - mode = Mode::Color; - update(); -} - -auto pCanvas::setData(Size size) -> void { - mode = Mode::Data; update(); } @@ -98,13 +93,11 @@ auto pCanvas::setGeometry(Geometry geometry) -> void { pWidget::setGeometry(geometry); } -auto pCanvas::setGradient(Color topLeft, Color topRight, Color bottomLeft, Color bottomRight) -> void { - mode = Mode::Gradient; +auto pCanvas::setGradient(Gradient gradient) -> void { update(); } -auto pCanvas::setIcon(const image& icon) -> void { - mode = Mode::Icon; +auto pCanvas::setImage(const Image& image) -> void { update(); } @@ -146,14 +139,13 @@ auto pCanvas::_rasterize() -> void { signed width = 0; signed height = 0; - if(mode == Mode::Color || mode == Mode::Gradient) { + if(auto& image = state().image) { + width = image.width(); + height = image.height(); + } else { width = pSizable::state().geometry.width(); height = pSizable::state().geometry.height(); - } else { - width = state().size.width(); - height = state().size.height(); } - if(width <= 0 || height <= 0) return; if(width != surfaceWidth || height != surfaceHeight) _release(); @@ -161,33 +153,21 @@ auto pCanvas::_rasterize() -> void { surfaceHeight = height; if(!surface) surface = gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, width, height); - uint32_t* buffer = (uint32_t*)gdk_pixbuf_get_pixels(surface); + auto buffer = (uint32_t*)gdk_pixbuf_get_pixels(surface); - if(mode == Mode::Color) { + if(auto& image = state().image) { + memory::copy(buffer, state().image.data(), width * height * sizeof(uint32_t)); + } else if(auto& gradient = state().gradient) { + auto& colors = gradient.state.colors; + nall::image fill; + fill.allocate(width, height); + fill.gradient(colors[0].value(), colors[1].value(), colors[2].value(), colors[3].value()); + memory::copy(buffer, fill.data(), fill.size()); + } else { uint32_t color = state().color.value(); for(auto n : range(width * height)) buffer[n] = color; } - if(mode == Mode::Gradient) { - image fill; - fill.allocate(width, height); - fill.gradient( - state().gradient[0].value(), state().gradient[1].value(), state().gradient[2].value(), state().gradient[3].value() - ); - memory::copy(buffer, fill.data(), fill.size()); - } - - if(mode == Mode::Icon) { - auto icon = state().icon; - icon.scale(width, height); - icon.transform(); - memory::copy(buffer, icon.data(), icon.size()); - } - - if(mode == Mode::Data) { - memory::copy(buffer, state().data.data(), state().data.size() * sizeof(uint32_t)); - } - //ARGB -> ABGR conversion for(auto n : range(width * height)) { uint32_t color = *buffer; diff --git a/hiro/gtk/widget/canvas.hpp b/hiro/gtk/widget/canvas.hpp index 5a2a79b3..9b6a93ad 100644 --- a/hiro/gtk/widget/canvas.hpp +++ b/hiro/gtk/widget/canvas.hpp @@ -7,15 +7,12 @@ struct pCanvas : pWidget { auto minimumSize() const -> Size; auto setColor(Color color) -> void; - auto setData(Size size) -> void; auto setDroppable(bool droppable) -> void; auto setGeometry(Geometry geometry) -> void override; - auto setGradient(Color topLeft, Color topRight, Color bottomLeft, Color bottomRight) -> void; - auto setIcon(const image& icon) -> void; + auto setGradient(Gradient gradient) -> void; + auto setImage(const Image& image) -> void; auto update() -> void; - enum class Mode : unsigned { Color, Data, Gradient, Icon }; - auto _onExpose(GdkEventExpose* event) -> void; auto _rasterize() -> void; auto _redraw() -> void; @@ -24,7 +21,6 @@ struct pCanvas : pWidget { GdkPixbuf* surface = nullptr; unsigned surfaceWidth = 0; unsigned surfaceHeight = 0; - Mode mode = Mode::Color; }; } diff --git a/hiro/gtk/widget/check-button.cpp b/hiro/gtk/widget/check-button.cpp index 3ff37aa9..903b3c21 100644 --- a/hiro/gtk/widget/check-button.cpp +++ b/hiro/gtk/widget/check-button.cpp @@ -12,7 +12,7 @@ auto pCheckButton::construct() -> void { setBordered(state().bordered); setChecked(state().checked); - setIcon(state().icon); + setImage(state().image); setOrientation(state().orientation); setText(state().text); @@ -29,13 +29,13 @@ auto pCheckButton::minimumSize() const -> Size { Size size = pFont::size(self().font(true), state().text); if(state().orientation == Orientation::Horizontal) { - size.setWidth(size.width() + state().icon.width()); - size.setHeight(max(size.height(), state().icon.height())); + size.setWidth(size.width() + state().image.width()); + size.setHeight(max(size.height(), state().image.height())); } if(state().orientation == Orientation::Vertical) { - size.setWidth(max(size.width(), state().icon.width())); - size.setHeight(size.height() + state().icon.height()); + size.setWidth(max(size.width(), state().image.width())); + size.setHeight(size.height() + state().image.height()); } return {size.width() + 24, size.height() + 12}; @@ -51,9 +51,9 @@ auto pCheckButton::setChecked(bool checked) -> void { unlock(); } -auto pCheckButton::setIcon(const image& icon) -> void { - if(icon) { - GtkImage* gtkImage = CreateImage(icon); +auto pCheckButton::setImage(const Image& image) -> void { + if(image) { + GtkImage* gtkImage = CreateImage(image); gtk_button_set_image(GTK_BUTTON(gtkWidget), (GtkWidget*)gtkImage); } else { gtk_button_set_image(GTK_BUTTON(gtkWidget), nullptr); diff --git a/hiro/gtk/widget/check-button.hpp b/hiro/gtk/widget/check-button.hpp index 886f4e56..1b6cdf7b 100644 --- a/hiro/gtk/widget/check-button.hpp +++ b/hiro/gtk/widget/check-button.hpp @@ -8,7 +8,7 @@ struct pCheckButton : pWidget { auto minimumSize() const -> Size override; auto setBordered(bool bordered) -> void; auto setChecked(bool checked) -> void; - auto setIcon(const image& icon) -> void; + auto setImage(const Image& image) -> void; auto setOrientation(Orientation orientation) -> void; auto setText(const string& text) -> void; }; diff --git a/hiro/gtk/widget/combo-button-item.cpp b/hiro/gtk/widget/combo-button-item.cpp index 3a54544f..b475be36 100644 --- a/hiro/gtk/widget/combo-button-item.cpp +++ b/hiro/gtk/widget/combo-button-item.cpp @@ -8,11 +8,10 @@ auto pComboButtonItem::construct() -> void { auto pComboButtonItem::destruct() -> void { } -auto pComboButtonItem::setIcon(image icon) -> void { +auto pComboButtonItem::setImage(const Image& image) -> void { if(auto parent = _parent()) { auto size = pFont::size(self().font(true), " ").height(); - if(icon) icon.scale(size, size); - auto pixbuf = CreatePixbuf(icon); + auto pixbuf = CreatePixbuf(image, true); gtk_list_store_set(parent->gtkListStore, >kIter, 0, pixbuf, -1); } } diff --git a/hiro/gtk/widget/combo-button-item.hpp b/hiro/gtk/widget/combo-button-item.hpp index 0ed29bd9..f0c3c5d0 100644 --- a/hiro/gtk/widget/combo-button-item.hpp +++ b/hiro/gtk/widget/combo-button-item.hpp @@ -5,7 +5,7 @@ namespace hiro { struct pComboButtonItem : pObject { Declare(ComboButtonItem, Object) - auto setIcon(image icon) -> void; + auto setImage(const Image& icon) -> void; auto setSelected() -> void; auto setText(const string& text) -> void; diff --git a/hiro/gtk/widget/combo-button.cpp b/hiro/gtk/widget/combo-button.cpp index 08eebddd..1a93f2be 100644 --- a/hiro/gtk/widget/combo-button.cpp +++ b/hiro/gtk/widget/combo-button.cpp @@ -32,7 +32,7 @@ auto pComboButton::append(sComboButtonItem item) -> void { lock(); if(auto self = item->self()) { gtk_list_store_append(gtkListStore, &self->gtkIter); - self->setIcon(item->state.icon); + self->setImage(item->state.image); if(item->state.selected) self->setSelected(); self->setText(item->state.text); } @@ -41,11 +41,11 @@ auto pComboButton::append(sComboButtonItem item) -> void { } auto pComboButton::minimumSize() const -> Size { - string font = self().font(true); + auto font = self().font(true); signed maximumWidth = 0; for(auto& item : state().items) { maximumWidth = max(maximumWidth, - (item->state.icon ? pFont::size(font, " ").height() + 2 : 0) + (item->state.image ? pFont::size(font, " ").height() + 2 : 0) + pFont::size(font, item->state.text).width() ); } @@ -69,7 +69,7 @@ auto pComboButton::reset() -> void { unlock(); } -auto pComboButton::setFont(const string& font) -> void { +auto pComboButton::setFont(const Font& font) -> void { pWidget::setFont(font); auto fontDescription = pFont::create(font); g_object_set(G_OBJECT(gtkCellText), "font-desc", fontDescription, nullptr); diff --git a/hiro/gtk/widget/combo-button.hpp b/hiro/gtk/widget/combo-button.hpp index ae36b7f4..a81470e6 100644 --- a/hiro/gtk/widget/combo-button.hpp +++ b/hiro/gtk/widget/combo-button.hpp @@ -9,7 +9,7 @@ struct pComboButton : pWidget { auto minimumSize() const -> Size override; auto remove(sComboButtonItem item) -> void; auto reset() -> void; - auto setFont(const string& font) -> void override; + auto setFont(const Font& font) -> void override; auto _updateSelected() -> void; diff --git a/hiro/gtk/widget/frame.cpp b/hiro/gtk/widget/frame.cpp index 2d8c603c..e07f2a67 100644 --- a/hiro/gtk/widget/frame.cpp +++ b/hiro/gtk/widget/frame.cpp @@ -33,7 +33,7 @@ auto pFrame::setEnabled(bool enabled) -> void { pWidget::setEnabled(enabled); } -auto pFrame::setFont(const string& font) -> void { +auto pFrame::setFont(const Font& font) -> void { if(auto layout = _layout()) layout->setFont(layout->self().font(true)); pFont::setFont(gtkLabel, font); } diff --git a/hiro/gtk/widget/frame.hpp b/hiro/gtk/widget/frame.hpp index bdc09626..71f49004 100644 --- a/hiro/gtk/widget/frame.hpp +++ b/hiro/gtk/widget/frame.hpp @@ -9,7 +9,7 @@ struct pFrame : pWidget { auto container(mWidget& widget) -> GtkWidget* override; auto remove(shared_pointer layout) -> void; auto setEnabled(bool enabled) -> void override; - auto setFont(const string& font) -> void override; + auto setFont(const Font& font) -> void override; auto setGeometry(Geometry geometry) -> void override; auto setText(const string& text) -> void; auto setVisible(bool visible) -> void override; diff --git a/hiro/gtk/widget/icon-view-item.cpp b/hiro/gtk/widget/icon-view-item.cpp index 48081e86..2b766efc 100644 --- a/hiro/gtk/widget/icon-view-item.cpp +++ b/hiro/gtk/widget/icon-view-item.cpp @@ -8,9 +8,9 @@ auto pIconViewItem::construct() -> void { auto pIconViewItem::destruct() -> void { } -auto pIconViewItem::setIcon(const image& icon) -> void { +auto pIconViewItem::setImage(const Image& image) -> void { if(auto parent = _parent()) { - parent->setItemIcon(self().offset(), icon); + parent->setItemImage(self().offset(), image); } } diff --git a/hiro/gtk/widget/icon-view-item.hpp b/hiro/gtk/widget/icon-view-item.hpp index e4060110..a386069a 100644 --- a/hiro/gtk/widget/icon-view-item.hpp +++ b/hiro/gtk/widget/icon-view-item.hpp @@ -5,7 +5,7 @@ namespace hiro { struct pIconViewItem : pObject { Declare(IconViewItem, Object) - auto setIcon(const image& icon) -> void; + auto setImage(const Image& image) -> void; auto setSelected(bool selected) -> void; auto setText(const string& text) -> void; diff --git a/hiro/gtk/widget/icon-view.cpp b/hiro/gtk/widget/icon-view.cpp index f4d6bcdd..2fd3419f 100644 --- a/hiro/gtk/widget/icon-view.cpp +++ b/hiro/gtk/widget/icon-view.cpp @@ -39,9 +39,9 @@ auto pIconView::construct() -> void { gtk_widget_show(subWidget); setBackgroundColor(state().backgroundColor); + setBatchable(state().batchable); setFlow(state().flow); setForegroundColor(state().foregroundColor); - setMultiSelect(state().multiSelect); setOrientation(state().orientation); for(auto position : range(self().items())) { auto& item = state().items[position]; @@ -65,7 +65,7 @@ auto pIconView::destruct() -> void { auto pIconView::append(sIconViewItem item) -> void { GtkTreeIter iter; gtk_list_store_append(store, &iter); - setItemIcon(item->offset(), item->state.icon); + setItemImage(item->offset(), item->state.image); setItemSelected(item->offset(), item->state.selected); setItemText(item->offset(), item->state.text); } @@ -92,6 +92,12 @@ auto pIconView::setBackgroundColor(Color color) -> void { gtk_widget_modify_base(subWidget, GTK_STATE_NORMAL, color ? &gdkColor : nullptr); } +auto pIconView::setBatchable(bool batchable) -> void { + gtk_icon_view_set_selection_mode(GTK_ICON_VIEW(subWidget), + batchable ? GTK_SELECTION_MULTIPLE : GTK_SELECTION_SINGLE + ); +} + auto pIconView::setFlow(Orientation flow) -> void { //GTK+ does not support vertical flow ... the closest we can get is a horizontal flow with only one column if(flow == Orientation::Horizontal) { @@ -115,12 +121,12 @@ auto pIconView::setGeometry(Geometry geometry) -> void { } } -auto pIconView::setItemIcon(unsigned position, const image& icon) -> void { +auto pIconView::setItemImage(unsigned position, const Image& image) -> void { if(position >= self().itemCount()) return; GtkTreeIter iter; if(gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(store), &iter, string{position})) { - if(icon) { - GdkPixbuf* pixbuf = CreatePixbuf(icon); + if(image) { + GdkPixbuf* pixbuf = CreatePixbuf(image); gtk_list_store_set(store, &iter, 0, pixbuf, -1); } else { gtk_list_store_set(store, &iter, 0, nullptr, -1); @@ -172,12 +178,6 @@ auto pIconView::setItemText(unsigned position, const string& text) -> void { } } -auto pIconView::setMultiSelect(bool multiSelect) -> void { - gtk_icon_view_set_selection_mode(GTK_ICON_VIEW(subWidget), - multiSelect ? GTK_SELECTION_MULTIPLE : GTK_SELECTION_SINGLE - ); -} - auto pIconView::setOrientation(Orientation orientation) -> void { gtk_icon_view_set_item_orientation(GTK_ICON_VIEW(subWidget), orientation == Orientation::Horizontal ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL diff --git a/hiro/gtk/widget/icon-view.hpp b/hiro/gtk/widget/icon-view.hpp index 0be21b83..58923c21 100644 --- a/hiro/gtk/widget/icon-view.hpp +++ b/hiro/gtk/widget/icon-view.hpp @@ -9,16 +9,16 @@ struct pIconView : pWidget { auto remove(sIconViewItem item) -> void; auto reset() -> void; auto setBackgroundColor(Color color) -> void; + auto setBatchable(bool batchable) -> void; auto setFlow(Orientation flow) -> void; auto setForegroundColor(Color color) -> void; auto setGeometry(Geometry geometry) -> void; - auto setItemIcon(unsigned position, const image& icon) -> void; + auto setItemImage(unsigned position, const Image& image) -> void; auto setItemSelected(unsigned position, bool selected) -> void; auto setItemSelected(const vector& selections) -> void; auto setItemSelectedAll() -> void; auto setItemSelectedNone() -> void; auto setItemText(unsigned position, const string& text) -> void; - auto setMultiSelect(bool multiSelect) -> void; auto setOrientation(Orientation orientation) -> void; auto _updateSelected() -> void; diff --git a/hiro/gtk/widget/list-view-cell.cpp b/hiro/gtk/widget/list-view-cell.cpp index 94e22bcc..507d0341 100644 --- a/hiro/gtk/widget/list-view-cell.cpp +++ b/hiro/gtk/widget/list-view-cell.cpp @@ -25,7 +25,7 @@ auto pListViewCell::setChecked(bool checked) -> void { auto pListViewCell::setForegroundColor(Color color) -> void { } -auto pListViewCell::setIcon(const image& icon) -> void { +auto pListViewCell::setImage(const Image& image) -> void { _setState(); } @@ -50,7 +50,7 @@ auto pListViewCell::_setState() -> void { if(auto grandparent = _grandparent()) { grandparent->lock(); gtk_list_store_set(grandparent->gtkListStore, &parent->gtkIter, 3 * self().offset() + 0, state().checked, -1); - gtk_list_store_set(grandparent->gtkListStore, &parent->gtkIter, 3 * self().offset() + 1, CreatePixbuf(state().icon), -1); + gtk_list_store_set(grandparent->gtkListStore, &parent->gtkIter, 3 * self().offset() + 1, CreatePixbuf(state().image), -1); gtk_list_store_set(grandparent->gtkListStore, &parent->gtkIter, 3 * self().offset() + 2, state().text.data(), -1); grandparent->unlock(); } diff --git a/hiro/gtk/widget/list-view-cell.hpp b/hiro/gtk/widget/list-view-cell.hpp index 2fd00821..3587d44f 100644 --- a/hiro/gtk/widget/list-view-cell.hpp +++ b/hiro/gtk/widget/list-view-cell.hpp @@ -10,7 +10,7 @@ struct pListViewCell : pObject { auto setCheckable(bool checkable) -> void; auto setChecked(bool checked) -> void; auto setForegroundColor(Color color) -> void; - auto setIcon(const image& icon) -> void; + auto setImage(const Image& image) -> void; auto setText(const string& text) -> void; auto _grandparent() -> maybe; diff --git a/hiro/gtk/widget/list-view-column.cpp b/hiro/gtk/widget/list-view-column.cpp index 4ea0436f..8a984495 100644 --- a/hiro/gtk/widget/list-view-column.cpp +++ b/hiro/gtk/widget/list-view-column.cpp @@ -75,15 +75,15 @@ auto pListViewColumn::setExpandable(bool expandable) -> void { } } -auto pListViewColumn::setFont(const string& font) -> void { +auto pListViewColumn::setFont(const Font& font) -> void { } auto pListViewColumn::setForegroundColor(Color color) -> void { } -auto pListViewColumn::setIcon(const image& icon) -> void { - if(icon) { - gtk_image_set_from_pixbuf(GTK_IMAGE(gtkHeaderIcon), CreatePixbuf(icon)); +auto pListViewColumn::setImage(const Image& image) -> void { + if(image) { + gtk_image_set_from_pixbuf(GTK_IMAGE(gtkHeaderIcon), CreatePixbuf(image)); } else { gtk_image_clear(GTK_IMAGE(gtkHeaderIcon)); } diff --git a/hiro/gtk/widget/list-view-column.hpp b/hiro/gtk/widget/list-view-column.hpp index 2e2d37ee..1c58fb85 100644 --- a/hiro/gtk/widget/list-view-column.hpp +++ b/hiro/gtk/widget/list-view-column.hpp @@ -10,10 +10,10 @@ struct pListViewColumn : pObject { auto setBackgroundColor(Color color) -> void; auto setEditable(bool editable) -> void; auto setExpandable(bool expandable) -> void; - auto setFont(const string& font) -> void override; + auto setFont(const Font& font) -> void override; auto setForegroundColor(Color color) -> void; auto setHorizontalAlignment(double) -> void {} - auto setIcon(const image& icon) -> void; + auto setImage(const Image& image) -> void; auto setResizable(bool resizable) -> void; auto setSortable(bool sortable) -> void; auto setText(const string& text) -> void; diff --git a/hiro/gtk/widget/list-view.cpp b/hiro/gtk/widget/list-view.cpp index f2bd8ec3..9bc1b5e5 100644 --- a/hiro/gtk/widget/list-view.cpp +++ b/hiro/gtk/widget/list-view.cpp @@ -122,7 +122,7 @@ auto pListView::setFocused() -> void { gtk_widget_grab_focus(gtkWidgetChild); } -auto pListView::setFont(const string& font) -> void { +auto pListView::setFont(const Font& font) -> void { if(auto& header = state().header) { if(auto self = header->self()) self->_setState(); } @@ -149,11 +149,11 @@ auto pListView::_cellWidth(unsigned _row, unsigned _column) -> unsigned { if(cell->state.checkable) { width += 24; } - if(auto& icon = cell->state.icon) { - width += icon.width() + 2; + if(auto& image = cell->state.image) { + width += image.width() + 2; } if(auto& text = cell->state.text) { - width += Font::size(cell->font(true), text).width(); + width += pFont::size(cell->font(true), text).width(); } } } @@ -164,11 +164,11 @@ auto pListView::_columnWidth(unsigned _column) -> unsigned { unsigned width = 8; if(auto& header = state().header) { if(auto column = header->column(_column)) { - if(auto& icon = column->state.icon) { - width += icon.width() + 2; + if(auto& image = column->state.image) { + width += image.width() + 2; } if(auto& text = column->state.text) { - width += Font::size(column->font(true), text).width(); + width += pFont::size(column->font(true), text).width(); } } } diff --git a/hiro/gtk/widget/list-view.hpp b/hiro/gtk/widget/list-view.hpp index d87cc3bd..1df15dfb 100644 --- a/hiro/gtk/widget/list-view.hpp +++ b/hiro/gtk/widget/list-view.hpp @@ -16,7 +16,7 @@ struct pListView : pWidget { auto setBatchable(bool batchable) -> void; auto setBordered(bool bordered) -> void; auto setFocused() -> void override; - auto setFont(const string& font) -> void override; + auto setFont(const Font& font) -> void override; auto setForegroundColor(Color color) -> void; auto setGeometry(Geometry geometry) -> void override; diff --git a/hiro/gtk/widget/radio-button.cpp b/hiro/gtk/widget/radio-button.cpp index 74a5d0f8..9cdd8063 100644 --- a/hiro/gtk/widget/radio-button.cpp +++ b/hiro/gtk/widget/radio-button.cpp @@ -5,7 +5,7 @@ namespace hiro { static auto RadioButton_activate(GtkToggleButton*, pRadioButton* p) -> void { if(p->groupLocked()) return; bool wasChecked = p->state().checked; - p->setChecked(); + p->self().setChecked(); if(!wasChecked) p->self().doActivate(); } @@ -13,7 +13,7 @@ auto pRadioButton::construct() -> void { gtkWidget = gtk_toggle_button_new(); setBordered(state().bordered); - setIcon(state().icon); + setImage(state().image); setOrientation(state().orientation); setText(state().text); @@ -30,13 +30,13 @@ auto pRadioButton::minimumSize() const -> Size { Size size = pFont::size(self().font(true), state().text); if(state().orientation == Orientation::Horizontal) { - size.setWidth(size.width() + state().icon.width()); - size.setHeight(max(size.height(), state().icon.height())); + size.setWidth(size.width() + state().image.width()); + size.setHeight(max(size.height(), state().image.height())); } if(state().orientation == Orientation::Vertical) { - size.setWidth(max(size.width(), state().icon.width())); - size.setHeight(size.height() + state().icon.height()); + size.setWidth(max(size.width(), state().image.width())); + size.setHeight(size.height() + state().image.height()); } return {size.width() + 24, size.height() + 12}; @@ -47,14 +47,12 @@ auto pRadioButton::setBordered(bool bordered) -> void { } auto pRadioButton::setChecked() -> void { - if(!self().group()) return; for(auto& weak : self().group()->state.objects) { if(auto object = weak.acquire()) { if(auto radioButton = dynamic_cast(object.data())) { if(auto self = radioButton->self()) { self->lock(); - bool checked = self == this; - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->gtkWidget), radioButton->state.checked = checked); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->gtkWidget), radioButton->state.checked); self->unlock(); } } @@ -63,13 +61,14 @@ auto pRadioButton::setChecked() -> void { } auto pRadioButton::setGroup(sGroup group) -> void { - if(!group) return; + bool first = true; for(auto& weak : group->state.objects) { if(auto object = weak.acquire()) { if(auto radioButton = dynamic_cast(object.data())) { if(auto self = radioButton->self()) { self->lock(); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->gtkWidget), radioButton->checked()); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->gtkWidget), radioButton->state.checked = first); + first = false; self->unlock(); } } @@ -77,9 +76,9 @@ auto pRadioButton::setGroup(sGroup group) -> void { } } -auto pRadioButton::setIcon(const image& icon) -> void { - if(icon) { - GtkImage* gtkImage = CreateImage(icon); +auto pRadioButton::setImage(const Image& image) -> void { + if(image) { + GtkImage* gtkImage = CreateImage(image); gtk_button_set_image(GTK_BUTTON(gtkWidget), (GtkWidget*)gtkImage); } else { gtk_button_set_image(GTK_BUTTON(gtkWidget), nullptr); diff --git a/hiro/gtk/widget/radio-button.hpp b/hiro/gtk/widget/radio-button.hpp index 2be014dd..f1e77ead 100644 --- a/hiro/gtk/widget/radio-button.hpp +++ b/hiro/gtk/widget/radio-button.hpp @@ -9,7 +9,7 @@ struct pRadioButton : pWidget { auto setBordered(bool bordered) -> void; auto setChecked() -> void; auto setGroup(sGroup group) -> void; - auto setIcon(const image& icon) -> void; + auto setImage(const Image& image) -> void; auto setOrientation(Orientation orientation) -> void; auto setText(const string& text) -> void; diff --git a/hiro/gtk/widget/radio-label.cpp b/hiro/gtk/widget/radio-label.cpp index 9681cd29..89d6b6cd 100644 --- a/hiro/gtk/widget/radio-label.cpp +++ b/hiro/gtk/widget/radio-label.cpp @@ -5,7 +5,7 @@ namespace hiro { static auto RadioLabel_activate(GtkToggleButton*, pRadioLabel* p) -> void { if(p->groupLocked()) return; bool wasChecked = p->state().checked; - p->setChecked(); + p->self().setChecked(); if(!wasChecked) p->self().doActivate(); } @@ -37,8 +37,6 @@ auto pRadioLabel::setChecked() -> void { } auto pRadioLabel::setGroup(sGroup group) -> void { - if(!group) return; - maybe gtkRadioButton; for(auto& weak : group->state.objects) { if(auto object = weak.acquire()) { @@ -46,9 +44,13 @@ auto pRadioLabel::setGroup(sGroup group) -> void { if(auto self = radioLabel->self()) { self->lock(); gtk_radio_button_set_group(self->gtkRadioButton, nullptr); - if(!gtkRadioButton) gtkRadioButton = self->gtkRadioButton; - else gtk_radio_button_set_group(self->gtkRadioButton, gtk_radio_button_get_group(*gtkRadioButton)); - gtk_toggle_button_set_active(self->gtkToggleButton, radioLabel->checked()); + if(!gtkRadioButton) { + gtkRadioButton = self->gtkRadioButton; + gtk_toggle_button_set_active(self->gtkToggleButton, radioLabel->state.checked = true); + } else { + gtk_radio_button_set_group(self->gtkRadioButton, gtk_radio_button_get_group(*gtkRadioButton)); + gtk_toggle_button_set_active(self->gtkToggleButton, radioLabel->state.checked = false); + } self->unlock(); } } diff --git a/hiro/gtk/widget/source-edit.cpp b/hiro/gtk/widget/source-edit.cpp index 186c2e12..b1a6fd0d 100644 --- a/hiro/gtk/widget/source-edit.cpp +++ b/hiro/gtk/widget/source-edit.cpp @@ -7,11 +7,11 @@ static auto SourceEdit_change(GtkTextBuffer*, pSourceEdit* p) -> void { } static auto SourceEdit_move(GObject*, GParamSpec*, pSourceEdit* p) -> void { - signed position = 0; - g_object_get(G_OBJECT(p->gtkSourceBuffer), "cursor-position", &position, nullptr); + signed offset = 0; + g_object_get(G_OBJECT(p->gtkSourceBuffer), "cursor-position", &offset, nullptr); - if(p->state().position != position) { - p->state().position = position; + if(p->state().cursor.offset() != offset) { + p->state().cursor.setOffset(offset); if(!p->locked()) p->self().doMove(); } } @@ -68,10 +68,25 @@ auto pSourceEdit::destruct() -> void { gtk_widget_destroy(gtkWidget); } +auto pSourceEdit::setCursor(Cursor cursor) -> void { + lock(); + GtkTextIter offset, length; + gtk_text_buffer_get_end_iter(gtkTextBuffer, &offset); + gtk_text_buffer_get_end_iter(gtkTextBuffer, &length); + signed end = gtk_text_iter_get_offset(&offset); + gtk_text_iter_set_offset(&offset, max(0, min(end, cursor.offset()))); + gtk_text_iter_set_offset(&length, max(0, min(end, cursor.offset() + cursor.length()))); + gtk_text_buffer_select_range(gtkTextBuffer, &offset, &length); + auto mark = gtk_text_buffer_get_mark(gtkTextBuffer, "insert"); + gtk_text_view_scroll_mark_onscreen(gtkTextView, mark); + unlock(); +} + auto pSourceEdit::setFocused() -> void { gtk_widget_grab_focus(gtkWidgetSourceView); } +/* auto pSourceEdit::setPosition(signed position) -> void { lock(); GtkTextIter iter; @@ -105,6 +120,7 @@ auto pSourceEdit::setSelected(Position selected) -> void { gtk_text_buffer_select_range(gtkTextBuffer, &startIter, &endIter); unlock(); } +*/ auto pSourceEdit::setText(const string& text) -> void { lock(); diff --git a/hiro/gtk/widget/source-edit.hpp b/hiro/gtk/widget/source-edit.hpp index 2caf3cd1..f8923187 100644 --- a/hiro/gtk/widget/source-edit.hpp +++ b/hiro/gtk/widget/source-edit.hpp @@ -5,9 +5,8 @@ namespace hiro { struct pSourceEdit : pWidget { Declare(SourceEdit, Widget) + auto setCursor(Cursor cursor) -> void; auto setFocused() -> void override; - auto setPosition(signed position) -> void; - auto setSelected(Position selected) -> void; auto setText(const string& text) -> void; auto text() const -> string; diff --git a/hiro/gtk/widget/tab-frame-item.cpp b/hiro/gtk/widget/tab-frame-item.cpp index 3b8f548b..f9ae429b 100644 --- a/hiro/gtk/widget/tab-frame-item.cpp +++ b/hiro/gtk/widget/tab-frame-item.cpp @@ -22,9 +22,9 @@ auto pTabFrameItem::setClosable(bool closable) -> void { } } -auto pTabFrameItem::setIcon(const image& icon) -> void { +auto pTabFrameItem::setImage(const Image& image) -> void { if(auto parent = _parent()) { - parent->setItemIcon(self().offset(), icon); + parent->setItemImage(self().offset(), image); } } diff --git a/hiro/gtk/widget/tab-frame-item.hpp b/hiro/gtk/widget/tab-frame-item.hpp index 495555b0..b2cb4417 100644 --- a/hiro/gtk/widget/tab-frame-item.hpp +++ b/hiro/gtk/widget/tab-frame-item.hpp @@ -8,7 +8,7 @@ struct pTabFrameItem : pObject { auto append(sLayout layout) -> void; auto remove(sLayout layout) -> void; auto setClosable(bool closable) -> void; - auto setIcon(const image& icon) -> void; + auto setImage(const Image& image) -> void; auto setMovable(bool movable) -> void; auto setSelected() -> void; auto setText(const string& text) -> void; diff --git a/hiro/gtk/widget/tab-frame.cpp b/hiro/gtk/widget/tab-frame.cpp index ffc12a9d..2cdbfd0b 100644 --- a/hiro/gtk/widget/tab-frame.cpp +++ b/hiro/gtk/widget/tab-frame.cpp @@ -49,7 +49,7 @@ auto pTabFrame::construct() -> void { tabs.reset(); //todo: memory leak, need to release each tab for(auto& item : state().items) append(item); - setEdge(state().edge); + setNavigation(state().navigation); g_signal_connect(G_OBJECT(gtkWidget), "page-reordered", G_CALLBACK(TabFrame_move), (gpointer)this); g_signal_connect(G_OBJECT(gtkWidget), "switch-page", G_CALLBACK(TabFrame_change), (gpointer)this); @@ -72,7 +72,7 @@ auto pTabFrame::append(sTabFrameItem item) -> void { tab.close = gtk_button_new_with_label("\u00d7"); //Unicode multiplication sign (looks better than 'X') gtk_button_set_focus_on_click(GTK_BUTTON(tab.close), false); gtk_button_set_relief(GTK_BUTTON(tab.close), GTK_RELIEF_NONE); - pFont::setFont(tab.close, Font::sans(9, "Bold")); + pFont::setFont(tab.close, Font("sans", 9).setBold()); auto color = CreateColor({255, 0, 0}); gtk_widget_modify_fg(gtk_bin_get_child(GTK_BIN(tab.close)), GTK_STATE_PRELIGHT, &color); tabs.append(tab); @@ -140,18 +140,6 @@ auto pTabFrame::remove(sTabFrameItem item) -> void { unlock(); } -auto pTabFrame::setEdge(Edge edge) -> void { - GtkPositionType type; - switch(edge) { default: - case Edge::Top: type = GTK_POS_TOP; break; - case Edge::Bottom: type = GTK_POS_BOTTOM; break; - case Edge::Left: type = GTK_POS_LEFT; break; - case Edge::Right: type = GTK_POS_RIGHT; break; - } - gtk_notebook_set_tab_pos(GTK_NOTEBOOK(gtkWidget), type); - setGeometry(self().geometry()); -} - auto pTabFrame::setEnabled(bool enabled) -> void { for(auto& item : state().items) { if(auto layout = item->state.layout) { @@ -161,7 +149,7 @@ auto pTabFrame::setEnabled(bool enabled) -> void { pWidget::setEnabled(enabled); } -auto pTabFrame::setFont(const string& font) -> void { +auto pTabFrame::setFont(const Font& font) -> void { for(auto n : range(tabs.size())) { pFont::setFont(tabs[n].title, font); if(auto layout = state().items[n]->state.layout) { @@ -174,7 +162,7 @@ auto pTabFrame::setGeometry(Geometry geometry) -> void { pWidget::setGeometry(geometry); geometry.setPosition(0, 0); - if(state().edge == Edge::Top || state().edge == Edge::Bottom) { + if(state().navigation == Navigation::Top || state().navigation == Navigation::Bottom) { geometry.setWidth(geometry.width() - 6); geometry.setHeight(geometry.height() - (15 + _tabHeight())); } else { @@ -190,7 +178,7 @@ auto pTabFrame::setItemClosable(unsigned position, bool closable) -> void { _synchronizeTab(position); } -auto pTabFrame::setItemIcon(unsigned position, const image& icon) -> void { +auto pTabFrame::setItemImage(unsigned position, const Image& image) -> void { _synchronizeTab(position); } @@ -214,6 +202,18 @@ auto pTabFrame::setItemText(unsigned position, const string& text) -> void { _synchronizeTab(position); } +auto pTabFrame::setNavigation(Navigation navigation) -> void { + GtkPositionType type; + switch(navigation) { default: + case Navigation::Top: type = GTK_POS_TOP; break; + case Navigation::Bottom: type = GTK_POS_BOTTOM; break; + case Navigation::Left: type = GTK_POS_LEFT; break; + case Navigation::Right: type = GTK_POS_RIGHT; break; + } + gtk_notebook_set_tab_pos(GTK_NOTEBOOK(gtkWidget), type); + setGeometry(self().geometry()); +} + auto pTabFrame::setVisible(bool visible) -> void { for(auto& item : state().items) { if(auto layout = item->state.layout) { @@ -239,16 +239,15 @@ auto pTabFrame::_synchronizeTab(unsigned position) -> void { auto& item = state().items[position]; auto& tab = tabs[position]; gtk_widget_set_visible(tab.close, item->closable()); - if(auto copy = item->state.icon) { + if(auto& image = item->state.image) { unsigned size = pFont::size(self().font(true), " ").height(); - copy.scale(size, size); - auto pixbuf = CreatePixbuf(copy); + auto pixbuf = CreatePixbuf(image, true); gtk_image_set_from_pixbuf(GTK_IMAGE(tab.image), pixbuf); } else { gtk_image_clear(GTK_IMAGE(tab.image)); } string text = { - item->state.icon && item->state.text ? " " : "", + item->state.image && item->state.text ? " " : "", item->state.text, item->state.text && item->state.closable ? " " : "" }; diff --git a/hiro/gtk/widget/tab-frame.hpp b/hiro/gtk/widget/tab-frame.hpp index f6392033..07d41d3f 100644 --- a/hiro/gtk/widget/tab-frame.hpp +++ b/hiro/gtk/widget/tab-frame.hpp @@ -8,16 +8,16 @@ struct pTabFrame : pWidget { auto append(sTabFrameItem item) -> void; auto container(mWidget& widget) -> GtkWidget* override; auto remove(sTabFrameItem item) -> void; - auto setEdge(Edge edge) -> void; auto setEnabled(bool enabled) -> void override; - auto setFont(const string& font) -> void override; + auto setFont(const Font& font) -> void override; auto setGeometry(Geometry geometry) -> void override; auto setItemClosable(unsigned position, bool closable) -> void; - auto setItemIcon(unsigned position, const image& icon) -> void; + auto setItemImage(unsigned position, const Image& image) -> void; auto setItemLayout(unsigned position, sLayout layout) -> void; auto setItemMovable(unsigned position, bool movable) -> void; auto setItemSelected(unsigned position) -> void; auto setItemText(unsigned position, const string& text) -> void; + auto setNavigation(Navigation navigation) -> void; auto setVisible(bool visible) -> void override; auto _append(shared_pointer item) -> void; diff --git a/hiro/gtk/widget/text-edit.cpp b/hiro/gtk/widget/text-edit.cpp index 46c0fd11..7e6c4e38 100644 --- a/hiro/gtk/widget/text-edit.cpp +++ b/hiro/gtk/widget/text-edit.cpp @@ -7,11 +7,11 @@ static auto TextEdit_change(GtkTextBuffer* textBuffer, pTextEdit* p) -> void { } static auto TextEdit_move(GObject* object, GParamSpec* spec, pTextEdit* p) -> void { - int position = 0; - g_object_get(p->textBuffer, "cursor-position", &position, nullptr); + signed offset = 0; + g_object_get(p->textBuffer, "cursor-position", &offset, nullptr); - if(p->state().cursorPosition != position) { - p->state().cursorPosition = position; + if(p->state().cursor.offset() != offset) { + p->state().cursor.setOffset(offset); if(!p->locked()) p->self().doMove(); } } @@ -33,6 +33,7 @@ auto pTextEdit::construct() -> void { setForegroundColor(state().foregroundColor); setText(state().text); setWordWrap(state().wordWrap); + setCursor(state().cursor); g_signal_connect(G_OBJECT(textBuffer), "changed", G_CALLBACK(TextEdit_change), (gpointer)this); g_signal_connect(G_OBJECT(textBuffer), "notify::cursor-position", G_CALLBACK(TextEdit_move), (gpointer)this); @@ -55,13 +56,16 @@ auto pTextEdit::setBackgroundColor(Color color) -> void { gtk_widget_modify_base(subWidget, GTK_STATE_NORMAL, color ? &gdkColor : nullptr); } -auto pTextEdit::setCursorPosition(unsigned position) -> void { +auto pTextEdit::setCursor(Cursor cursor) -> void { lock(); - GtkTextMark* mark = gtk_text_buffer_get_mark(textBuffer, "insert"); - GtkTextIter iter; - gtk_text_buffer_get_end_iter(textBuffer, &iter); - gtk_text_iter_set_offset(&iter, min(position, gtk_text_iter_get_offset(&iter))); - gtk_text_buffer_place_cursor(textBuffer, &iter); + GtkTextIter offset, length; + gtk_text_buffer_get_end_iter(textBuffer, &offset); + gtk_text_buffer_get_end_iter(textBuffer, &length); + signed end = gtk_text_iter_get_offset(&offset); + gtk_text_iter_set_offset(&offset, max(0, min(end, cursor.offset()))); + gtk_text_iter_set_offset(&length, max(0, min(end, cursor.offset() + cursor.length()))); + gtk_text_buffer_select_range(textBuffer, &offset, &length); + auto mark = gtk_text_buffer_get_mark(textBuffer, "insert"); gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(subWidget), mark); unlock(); } @@ -70,6 +74,10 @@ auto pTextEdit::setEditable(bool editable) -> void { gtk_text_view_set_editable(GTK_TEXT_VIEW(subWidget), editable); } +auto pTextEdit::setFocused() -> void { + gtk_widget_grab_focus(subWidget); +} + auto pTextEdit::setForegroundColor(Color color) -> void { GdkColor gdkColor = CreateColor(color); gtk_widget_modify_text(subWidget, GTK_STATE_NORMAL, color ? &gdkColor : nullptr); diff --git a/hiro/gtk/widget/text-edit.hpp b/hiro/gtk/widget/text-edit.hpp index 92a22dc6..4bb7e1fe 100644 --- a/hiro/gtk/widget/text-edit.hpp +++ b/hiro/gtk/widget/text-edit.hpp @@ -7,8 +7,9 @@ struct pTextEdit : pWidget { auto focused() const -> bool override; auto setBackgroundColor(Color color) -> void; - auto setCursorPosition(unsigned position) -> void; + auto setCursor(Cursor cursor) -> void; auto setEditable(bool editable) -> void; + auto setFocused() -> void override; auto setForegroundColor(Color color) -> void; auto setText(const string& text) -> void; auto setWordWrap(bool wordWrap) -> void; diff --git a/hiro/gtk/widget/tree-view-item.cpp b/hiro/gtk/widget/tree-view-item.cpp index f66912a4..00042970 100644 --- a/hiro/gtk/widget/tree-view-item.cpp +++ b/hiro/gtk/widget/tree-view-item.cpp @@ -3,26 +3,36 @@ namespace hiro { auto pTreeViewItem::construct() -> void { + if(auto parentWidget = _parentWidget()) { + if(auto parentItem = _parentItem()) { + gtk_tree_store_append(parentWidget->gtkTreeStore, >kIter, &parentItem->gtkIter); + } else { + gtk_tree_store_append(parentWidget->gtkTreeStore, >kIter, nullptr); + } + setChecked(state().checked); + setImage(state().image); + setText(state().text); + } } auto pTreeViewItem::destruct() -> void { + if(auto parent = _parentWidget()) { + gtk_tree_store_remove(parent->gtkTreeStore, >kIter); + } } // auto pTreeViewItem::append(sTreeViewItem item) -> void { - if(auto parentWidget = _parentWidget()) { - gtk_tree_store_append(parentWidget->gtkTreeStore, &item->self()->gtkIter, >kIter); - item->setChecked(item->checked()); - item->setIcon(item->icon()); - item->setText(item->text()); - } } auto pTreeViewItem::remove(sTreeViewItem item) -> void { - if(auto parentWidget = _parentWidget()) { - gtk_tree_store_remove(parentWidget->gtkTreeStore, &item->self()->gtkIter); - } +} + +auto pTreeViewItem::setBackgroundColor(Color color) -> void { +} + +auto pTreeViewItem::setCheckable(bool checkable) -> void { } auto pTreeViewItem::setChecked(bool checked) -> void { @@ -31,6 +41,18 @@ auto pTreeViewItem::setChecked(bool checked) -> void { } } +auto pTreeViewItem::setExpanded(bool expanded) -> void { + if(auto parentWidget = _parentWidget()) { + auto path = gtk_tree_model_get_path(parentWidget->gtkTreeModel, >kIter); + if(expanded) { + gtk_tree_view_expand_row(parentWidget->gtkTreeView, path, false); + } else { + gtk_tree_view_collapse_row(parentWidget->gtkTreeView, path); + } + gtk_tree_path_free(path); + } +} + auto pTreeViewItem::setFocused() -> void { if(auto parentWidget = _parentWidget()) { GtkTreePath* path = gtk_tree_path_new_from_string(self().path().transform("/", ":")); @@ -40,10 +62,13 @@ auto pTreeViewItem::setFocused() -> void { } } -auto pTreeViewItem::setIcon(const image& icon) -> void { +auto pTreeViewItem::setForegroundColor(Color color) -> void { +} + +auto pTreeViewItem::setImage(const Image& image) -> void { if(auto parentWidget = _parentWidget()) { - if(icon) { - auto pixbuf = CreatePixbuf(icon, true); + if(image) { + auto pixbuf = CreatePixbuf(image); gtk_tree_store_set(parentWidget->gtkTreeStore, >kIter, 1, pixbuf, -1); } else { gtk_tree_store_set(parentWidget->gtkTreeStore, >kIter, 1, nullptr, -1); diff --git a/hiro/gtk/widget/tree-view-item.hpp b/hiro/gtk/widget/tree-view-item.hpp index a6a08b64..775f4bc5 100644 --- a/hiro/gtk/widget/tree-view-item.hpp +++ b/hiro/gtk/widget/tree-view-item.hpp @@ -7,9 +7,13 @@ struct pTreeViewItem : pObject { auto append(sTreeViewItem item) -> void; auto remove(sTreeViewItem item) -> void; + auto setBackgroundColor(Color color) -> void; + auto setCheckable(bool checkable) -> void; auto setChecked(bool checked) -> void; + auto setExpanded(bool expanded) -> void; auto setFocused() -> void; - auto setIcon(const image& icon) -> void; + auto setForegroundColor(Color color) -> void; + auto setImage(const Image& image) -> void; auto setSelected() -> void; auto setText(const string& text) -> void; diff --git a/hiro/gtk/widget/tree-view.cpp b/hiro/gtk/widget/tree-view.cpp index f1cc2f0b..7649e482 100644 --- a/hiro/gtk/widget/tree-view.cpp +++ b/hiro/gtk/widget/tree-view.cpp @@ -2,10 +2,14 @@ namespace hiro { +//gtk_tree_view_collapse_all(gtkTreeView); +//gtk_tree_view_expand_all(gtkTreeView); + static auto TreeView_activate(GtkTreeView*, GtkTreePath* gtkPath, GtkTreeViewColumn*, pTreeView* p) -> void { p->_activatePath(gtkPath); } static auto TreeView_buttonEvent(GtkTreeView*, GdkEventButton* gdkEvent, pTreeView* p) -> signed { return p->_buttonEvent(gdkEvent); } static auto TreeView_change(GtkTreeSelection*, pTreeView* p) -> void { p->_updateSelected(); } static auto TreeView_context(GtkTreeView*, pTreeView* p) -> void { p->self().doContext(); } +static auto TreeView_dataFunc(GtkTreeViewColumn* column, GtkCellRenderer* renderer, GtkTreeModel* model, GtkTreeIter* iter, pTreeView* p) -> void { return p->_doDataFunc(column, renderer, iter); } static auto TreeView_toggle(GtkCellRendererToggle*, char* path, pTreeView* p) -> void { p->_togglePath(path); } auto pTreeView::construct() -> void { @@ -29,15 +33,17 @@ auto pTreeView::construct() -> void { gtkCellToggle = gtk_cell_renderer_toggle_new(); gtk_tree_view_column_pack_start(gtkTreeViewColumn, gtkCellToggle, false); gtk_tree_view_column_set_attributes(gtkTreeViewColumn, gtkCellToggle, "active", 0, nullptr); - gtk_cell_renderer_set_visible(gtkCellToggle, state().checkable); + gtk_tree_view_column_set_cell_data_func(gtkTreeViewColumn, GTK_CELL_RENDERER(gtkCellToggle), (GtkTreeCellDataFunc)TreeView_dataFunc, (gpointer)this, nullptr); gtkCellPixbuf = gtk_cell_renderer_pixbuf_new(); gtk_tree_view_column_pack_start(gtkTreeViewColumn, gtkCellPixbuf, false); gtk_tree_view_column_set_attributes(gtkTreeViewColumn, gtkCellPixbuf, "pixbuf", 1, nullptr); + gtk_tree_view_column_set_cell_data_func(gtkTreeViewColumn, GTK_CELL_RENDERER(gtkCellPixbuf), (GtkTreeCellDataFunc)TreeView_dataFunc, (gpointer)this, nullptr); gtkCellText = gtk_cell_renderer_text_new(); gtk_tree_view_column_pack_start(gtkTreeViewColumn, gtkCellText, true); gtk_tree_view_column_set_attributes(gtkTreeViewColumn, gtkCellText, "text", 2, nullptr); + gtk_tree_view_column_set_cell_data_func(gtkTreeViewColumn, GTK_CELL_RENDERER(gtkCellText), (GtkTreeCellDataFunc)TreeView_dataFunc, (gpointer)this, nullptr); gtk_tree_view_append_column(gtkTreeView, gtkTreeViewColumn); gtk_tree_view_set_search_column(gtkTreeView, 2); @@ -63,29 +69,9 @@ auto pTreeView::destruct() -> void { // auto pTreeView::append(sTreeViewItem item) -> void { - gtk_tree_store_append(gtkTreeStore, &item->self()->gtkIter, nullptr); - item->setChecked(item->checked()); - item->setIcon(item->icon()); - item->setText(item->text()); -} - -auto pTreeView::collapse() -> void { - gtk_tree_view_collapse_all(gtkTreeView); -} - -auto pTreeView::expand() -> void { - gtk_tree_view_expand_all(gtkTreeView); } auto pTreeView::remove(sTreeViewItem item) -> void { - gtk_tree_store_remove(gtkTreeStore, &item->self()->gtkIter); -} - -auto pTreeView::reset() -> void { - gtk_tree_view_set_model(gtkTreeView, nullptr); - gtkTreeStore = gtk_tree_store_new(3, G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, G_TYPE_STRING); - gtkTreeModel = GTK_TREE_MODEL(gtkTreeStore); - gtk_tree_view_set_model(gtkTreeView, gtkTreeModel); } auto pTreeView::setBackgroundColor(Color color) -> void { @@ -93,10 +79,6 @@ auto pTreeView::setBackgroundColor(Color color) -> void { gtk_widget_modify_base(gtkWidgetChild, GTK_STATE_NORMAL, color ? &gdkColor : nullptr); } -auto pTreeView::setCheckable(bool checkable) -> void { - gtk_cell_renderer_set_visible(gtkCellToggle, checkable); -} - auto pTreeView::setForegroundColor(Color color) -> void { auto gdkColor = CreateColor(color); gtk_widget_modify_text(gtkWidgetChild, GTK_STATE_NORMAL, color ? &gdkColor : nullptr); @@ -136,6 +118,41 @@ auto pTreeView::_buttonEvent(GdkEventButton* gdkEvent) -> signed { return false; } +auto pTreeView::_doDataFunc(GtkTreeViewColumn* column, GtkCellRenderer* renderer, GtkTreeIter* iter) -> void { + auto path = gtk_tree_model_get_string_from_iter(gtkTreeModel, iter); + auto parts = string{path}.split(":"); + g_free(path); + + auto item = self().item(decimal(parts.takeFirst())); + if(!item) return; + while(parts) { + item = item.item(decimal(parts.takeFirst())); + if(!item) return; + } + + if(renderer == GTK_CELL_RENDERER(gtkCellToggle)) { + gtk_cell_renderer_set_visible(renderer, item->state.checkable); + } else if(renderer == GTK_CELL_RENDERER(gtkCellPixbuf)) { + gtk_cell_renderer_set_visible(renderer, (bool)item->state.image); + } else if(renderer == GTK_CELL_RENDERER(gtkCellText)) { + auto font = pFont::create(item->font(true)); + g_object_set(G_OBJECT(renderer), "font-desc", font, nullptr); + pango_font_description_free(font); + if(auto color = item->foregroundColor(true)) { + auto gdkColor = CreateColor(color); + g_object_set(G_OBJECT(renderer), "foreground-gdk", &gdkColor, nullptr); + } else { + g_object_set(G_OBJECT(renderer), "foreground-set", false, nullptr); + } + } + if(auto color = item->backgroundColor(true)) { + auto gdkColor = CreateColor(color); + g_object_set(G_OBJECT(renderer), "cell-background-gdk", &gdkColor, nullptr); + } else { + g_object_set(G_OBJECT(renderer), "cell-background-set", false, nullptr); + } +} + auto pTreeView::_togglePath(string path) -> void { if(auto item = self().item(path.transform(":", "/"))) { bool checked = !item->checked(); diff --git a/hiro/gtk/widget/tree-view.hpp b/hiro/gtk/widget/tree-view.hpp index e73e66d9..23becafd 100644 --- a/hiro/gtk/widget/tree-view.hpp +++ b/hiro/gtk/widget/tree-view.hpp @@ -6,16 +6,13 @@ struct pTreeView : pWidget { Declare(TreeView, Widget) auto append(sTreeViewItem item) -> void; - auto collapse() -> void; - auto expand() -> void; auto remove(sTreeViewItem item) -> void; - auto reset() -> void; auto setBackgroundColor(Color color) -> void; - auto setCheckable(bool checkable) -> void; auto setForegroundColor(Color color) -> void; auto _activatePath(GtkTreePath* gtkPath) -> void; auto _buttonEvent(GdkEventButton* gdkEvent) -> signed; + auto _doDataFunc(GtkTreeViewColumn* column, GtkCellRenderer* renderer, GtkTreeIter* iter) -> void; auto _togglePath(string path) -> void; auto _updateSelected() -> void; diff --git a/hiro/gtk/widget/widget.cpp b/hiro/gtk/widget/widget.cpp index 3729f5d2..8d7a783d 100644 --- a/hiro/gtk/widget/widget.cpp +++ b/hiro/gtk/widget/widget.cpp @@ -33,7 +33,7 @@ auto pWidget::setFocused() -> void { gtk_widget_grab_focus(gtkWidget); } -auto pWidget::setFont(const string& font) -> void { +auto pWidget::setFont(const Font& font) -> void { if(!gtkWidget) return; return pFont::setFont(gtkWidget, font); } diff --git a/hiro/gtk/widget/widget.hpp b/hiro/gtk/widget/widget.hpp index 453f045d..0a8ec0cb 100644 --- a/hiro/gtk/widget/widget.hpp +++ b/hiro/gtk/widget/widget.hpp @@ -9,7 +9,7 @@ struct pWidget : pSizable { virtual auto focused() const -> bool override; auto setEnabled(bool enabled) -> void override; virtual auto setFocused() -> void override; - auto setFont(const string& font) -> void override; + auto setFont(const Font& font) -> void override; auto setGeometry(Geometry geometry) -> void override; auto setVisible(bool visible) -> void override; diff --git a/hiro/gtk/window.cpp b/hiro/gtk/window.cpp index 20313733..009993bc 100644 --- a/hiro/gtk/window.cpp +++ b/hiro/gtk/window.cpp @@ -388,7 +388,7 @@ auto pWindow::_setMenuEnabled(bool enabled) -> void { gtk_widget_set_sensitive(gtkMenu, enabled); } -auto pWindow::_setMenuFont(const string& font) -> void { +auto pWindow::_setMenuFont(const Font& font) -> void { pFont::setFont(gtkMenu, font); } @@ -400,7 +400,7 @@ auto pWindow::_setStatusEnabled(bool enabled) -> void { gtk_widget_set_sensitive(gtkStatus, enabled); } -auto pWindow::_setStatusFont(const string& font) -> void { +auto pWindow::_setStatusFont(const Font& font) -> void { pFont::setFont(gtkStatus, font); } diff --git a/hiro/gtk/window.hpp b/hiro/gtk/window.hpp index c095c532..ac6fc841 100644 --- a/hiro/gtk/window.hpp +++ b/hiro/gtk/window.hpp @@ -29,10 +29,10 @@ struct pWindow : pObject { auto _menuHeight() const -> signed; auto _setIcon(const string& basename) -> bool; auto _setMenuEnabled(bool enabled) -> void; - auto _setMenuFont(const string& font) -> void; + auto _setMenuFont(const Font& font) -> void; auto _setMenuVisible(bool visible) -> void; auto _setStatusEnabled(bool enabled) -> void; - auto _setStatusFont(const string& font) -> void; + auto _setStatusFont(const Font& font) -> void; auto _setStatusText(const string& text) -> void; auto _setStatusVisible(bool visible) -> void; auto _statusHeight() const -> signed; diff --git a/hiro/qt/action/action.cpp b/hiro/qt/action/action.cpp index cf4541b8..37b27628 100644 --- a/hiro/qt/action/action.cpp +++ b/hiro/qt/action/action.cpp @@ -12,7 +12,7 @@ auto pAction::setEnabled(bool enabled) -> void { _setState(); } -auto pAction::setFont(const string& font) -> void { +auto pAction::setFont(const Font& font) -> void { _setState(); } diff --git a/hiro/qt/action/action.hpp b/hiro/qt/action/action.hpp index 8b9d302c..584d4a8e 100644 --- a/hiro/qt/action/action.hpp +++ b/hiro/qt/action/action.hpp @@ -6,7 +6,7 @@ struct pAction : pObject { Declare(Action, Object) auto setEnabled(bool enabled) -> void override; - auto setFont(const string& font) -> void override; + auto setFont(const Font& font) -> void override; auto setVisible(bool visible) -> void override; auto _parentMenu() -> maybe; diff --git a/hiro/qt/action/menu-item.cpp b/hiro/qt/action/menu-item.cpp index 18dc5cfd..0323cb9d 100644 --- a/hiro/qt/action/menu-item.cpp +++ b/hiro/qt/action/menu-item.cpp @@ -22,7 +22,7 @@ auto pMenuItem::destruct() -> void { qtMenuItem = nullptr; } -auto pMenuItem::setIcon(const image& icon) -> void { +auto pMenuItem::setImage(const Image& icon) -> void { _setState(); } @@ -31,7 +31,7 @@ auto pMenuItem::setText(const string& text) -> void { } auto pMenuItem::_setState() -> void { - qtMenuItem->setIcon(CreateIcon(state().icon)); + qtMenuItem->setIcon(CreateImage(state().image)); qtMenuItem->setText(state().text); } diff --git a/hiro/qt/action/menu-item.hpp b/hiro/qt/action/menu-item.hpp index 35d815f7..4da61c42 100644 --- a/hiro/qt/action/menu-item.hpp +++ b/hiro/qt/action/menu-item.hpp @@ -5,7 +5,7 @@ namespace hiro { struct pMenuItem : pAction { Declare(MenuItem, Action) - auto setIcon(const image& icon) -> void; + auto setImage(const Image& image) -> void; auto setText(const string& text) -> void; auto _setState() -> void override; diff --git a/hiro/qt/action/menu-radio-item.cpp b/hiro/qt/action/menu-radio-item.cpp index a3cf8f16..a8d1dcb6 100644 --- a/hiro/qt/action/menu-radio-item.cpp +++ b/hiro/qt/action/menu-radio-item.cpp @@ -18,6 +18,7 @@ auto pMenuRadioItem::construct() -> void { parent->qtPopupMenu->addAction(qtMenuRadioItem); } + setGroup(state().group); _setState(); } @@ -33,7 +34,19 @@ auto pMenuRadioItem::setChecked() -> void { } auto pMenuRadioItem::setGroup(sGroup group) -> void { - _setState(); + bool first = true; + if(auto& group = state().group) { + for(auto& weak : group->state.objects) { + if(auto object = weak.acquire()) { + if(auto menuRadioItem = dynamic_cast(object.data())) { + if(auto self = menuRadioItem->self()) { + self->qtMenuRadioItem->setChecked(menuRadioItem->state.checked = first); + first = false; + } + } + } + } + } } auto pMenuRadioItem::setText(const string& text) -> void { @@ -41,7 +54,7 @@ auto pMenuRadioItem::setText(const string& text) -> void { } auto pMenuRadioItem::_setState() -> void { - if(auto group = state().group) { + if(auto& group = state().group) { if(auto object = group->object(0)) { if(auto menuRadioItem = dynamic_cast(object.data())) { if(auto self = menuRadioItem->self()) { @@ -56,7 +69,7 @@ auto pMenuRadioItem::_setState() -> void { auto QtMenuRadioItem::onActivate() -> void { if(p.state().checked) return; - p.state().checked = true; + p.self().setChecked(); p.self().doActivate(); } diff --git a/hiro/qt/action/menu.cpp b/hiro/qt/action/menu.cpp index 001444bd..eaf4c2a6 100644 --- a/hiro/qt/action/menu.cpp +++ b/hiro/qt/action/menu.cpp @@ -33,7 +33,7 @@ auto pMenu::append(sAction action) -> void { auto pMenu::remove(sAction action) -> void { } -auto pMenu::setIcon(const image& icon) -> void { +auto pMenu::setImage(const Image& image) -> void { _setState(); } @@ -44,7 +44,7 @@ auto pMenu::setText(const string& text) -> void { auto pMenu::_setState() -> void { qtMenu->setEnabled(self().enabled()); qtMenu->setFont(pFont::create(self().font(true))); - qtMenu->setIcon(CreateIcon(state().icon)); + qtMenu->setIcon(CreateImage(state().image)); qtMenu->setTitle(QString::fromUtf8(state().text)); qtMenu->menuAction()->setVisible(self().visible()); diff --git a/hiro/qt/action/menu.hpp b/hiro/qt/action/menu.hpp index f09ca43d..44cf3d05 100644 --- a/hiro/qt/action/menu.hpp +++ b/hiro/qt/action/menu.hpp @@ -7,7 +7,7 @@ struct pMenu : public pAction { auto append(sAction action) -> void; auto remove(sAction action) -> void; - auto setIcon(const image& icon) -> void; + auto setImage(const Image& image) -> void; auto setText(const string& text) -> void; auto _setState() -> void override; diff --git a/hiro/qt/font.cpp b/hiro/qt/font.cpp index b839328a..3a6dc1ae 100644 --- a/hiro/qt/font.cpp +++ b/hiro/qt/font.cpp @@ -2,61 +2,34 @@ namespace hiro { -auto pFont::serif(unsigned size, string style) -> string { - if(size == 0) size = 8; - if(style == "") style = "Normal"; - return {"Serif, ", size, ", ", style}; -} - -auto pFont::sans(unsigned size, string style) -> string { - if(size == 0) size = 8; - if(style == "") style = "Normal"; - return {"Sans, ", size, ", ", style}; -} - -auto pFont::monospace(unsigned size, string style) -> string { - if(size == 0) size = 8; - if(style == "") style = "Normal"; - return {"Liberation Mono, ", size, ", ", style}; -} - -auto pFont::size(string font, string text) -> Size { +auto pFont::size(const Font& font, const string& text) -> Size { return pFont::size(pFont::create(font), text); } -auto pFont::create(string description) -> QFont { - lstring part = description.split(",", 2L).strip(); - - string family = "Sans"; - unsigned size = 8u; - bool bold = false; - bool italic = false; - - if(part[0] != "") family = part[0]; - if(part.size() >= 2) size = decimal(part[1]); - if(part.size() >= 3) bold = (bool)part[2].find("Bold"); - if(part.size() >= 3) italic = (bool)part[2].find("Italic"); - - QFont qtFont; - qtFont.setFamily(family); - qtFont.setPointSize(size); - if(bold) qtFont.setBold(true); - if(italic) qtFont.setItalic(true); - return qtFont; -} - auto pFont::size(const QFont& qtFont, const string& text) -> Size { QFontMetrics metrics(qtFont); - - lstring lines; - lines.split(text ? text : " ", "\n"); - - unsigned maxWidth = 0; + signed maxWidth = 0; + auto lines = text.split("\n"); for(auto& line : lines) { maxWidth = max(maxWidth, metrics.width(line)); } + return {maxWidth, metrics.height() * (signed)lines.size()}; +} - return Size().setWidth(maxWidth).setHeight(metrics.height() * lines.size()); +auto pFont::family(const string& family) -> string { + if(family == Font::Sans ) return "Sans"; + if(family == Font::Serif) return "Serif"; + if(family == Font::Mono ) return "Liberation Mono"; + return family ? family : "Sans"; +} + +auto pFont::create(const Font& font) -> QFont { + QFont qtFont; + qtFont.setFamily(family(font.family())); + qtFont.setPointSize(font.size() ? font.size() : 8); + qtFont.setBold(font.bold()); + qtFont.setItalic(font.italic()); + return qtFont; } } diff --git a/hiro/qt/font.hpp b/hiro/qt/font.hpp index 7928d775..3b7fe79c 100644 --- a/hiro/qt/font.hpp +++ b/hiro/qt/font.hpp @@ -3,13 +3,10 @@ namespace hiro { struct pFont { - static auto serif(unsigned size, string style) -> string; - static auto sans(unsigned size, string style) -> string; - static auto monospace(unsigned size, string style) -> string; - static auto size(string font, string text) -> Size; - - static auto create(string description) -> QFont; + static auto size(const Font& font, const string& text) -> Size; static auto size(const QFont& qtFont, const string& text) -> Size; + static auto family(const string& family) -> string; + static auto create(const Font& font) -> QFont; }; } diff --git a/hiro/qt/hotkey.cpp b/hiro/qt/hotkey.cpp deleted file mode 100644 index 608ba7bb..00000000 --- a/hiro/qt/hotkey.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#if defined(Hiro_Hotkey) - -namespace hiro { - -auto pHotkey::construct() -> void { -} - -auto pHotkey::destruct() -> void { -} - -} - -#endif diff --git a/hiro/qt/hotkey.hpp b/hiro/qt/hotkey.hpp deleted file mode 100644 index b1caa7bc..00000000 --- a/hiro/qt/hotkey.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#if defined(Hiro_Hotkey) - -namespace hiro { - -struct pHotkey : pObject { - Declare(Hotkey, Object) -}; - -} - -#endif diff --git a/hiro/qt/menu-bar.cpp b/hiro/qt/menu-bar.cpp index 979ff305..a2432c0a 100644 --- a/hiro/qt/menu-bar.cpp +++ b/hiro/qt/menu-bar.cpp @@ -19,7 +19,7 @@ auto pMenuBar::setEnabled(bool enabled) -> void { _setState(); } -auto pMenuBar::setFont(const string& font) -> void { +auto pMenuBar::setFont(const Font& font) -> void { _setState(); } diff --git a/hiro/qt/menu-bar.hpp b/hiro/qt/menu-bar.hpp index ddf8bf05..e1fed335 100644 --- a/hiro/qt/menu-bar.hpp +++ b/hiro/qt/menu-bar.hpp @@ -8,7 +8,7 @@ struct pMenuBar : pObject { auto append(sMenu menu) -> void; auto remove(sMenu menu) -> void; auto setEnabled(bool enabled) -> void override; - auto setFont(const string& font) -> void override; + auto setFont(const Font& font) -> void override; auto setVisible(bool visible) -> void override; auto _parent() -> maybe; diff --git a/hiro/qt/object.cpp b/hiro/qt/object.cpp index fae97d8c..3c6ac442 100644 --- a/hiro/qt/object.cpp +++ b/hiro/qt/object.cpp @@ -24,7 +24,7 @@ auto pObject::setEnabled(bool enabled) -> void { auto pObject::setFocused() -> void { } -auto pObject::setFont(const string& font) -> void { +auto pObject::setFont(const Font& font) -> void { } auto pObject::setVisible(bool visible) -> void { diff --git a/hiro/qt/object.hpp b/hiro/qt/object.hpp index 5e198d81..ec5b878a 100644 --- a/hiro/qt/object.hpp +++ b/hiro/qt/object.hpp @@ -15,7 +15,7 @@ struct pObject { virtual auto reset() -> void; virtual auto setEnabled(bool enabled) -> void; virtual auto setFocused() -> void; - virtual auto setFont(const string& font) -> void; + virtual auto setFont(const Font& font) -> void; virtual auto setVisible(bool visible) -> void; auto locked() const -> bool { return locks != 0; } diff --git a/hiro/qt/platform.cpp b/hiro/qt/platform.cpp index 91b09819..e529b916 100644 --- a/hiro/qt/platform.cpp +++ b/hiro/qt/platform.cpp @@ -16,7 +16,6 @@ #include "object.cpp" #include "group.cpp" -#include "hotkey.cpp" #include "timer.cpp" #include "window.cpp" #include "status-bar.cpp" diff --git a/hiro/qt/platform.hpp b/hiro/qt/platform.hpp index d4454121..2f223cdf 100644 --- a/hiro/qt/platform.hpp +++ b/hiro/qt/platform.hpp @@ -19,7 +19,6 @@ #include "object.hpp" #include "group.hpp" -#include "hotkey.hpp" #include "timer.hpp" #include "window.hpp" #include "status-bar.hpp" diff --git a/hiro/qt/popup-menu.cpp b/hiro/qt/popup-menu.cpp index cc74a027..dcaef0e4 100644 --- a/hiro/qt/popup-menu.cpp +++ b/hiro/qt/popup-menu.cpp @@ -20,7 +20,7 @@ auto pPopupMenu::remove(sAction action) -> void { _setState(); } -auto pPopupMenu::setFont(const string& font) -> void { +auto pPopupMenu::setFont(const Font& font) -> void { _setState(); } diff --git a/hiro/qt/popup-menu.hpp b/hiro/qt/popup-menu.hpp index 04bf4d76..707d81d7 100644 --- a/hiro/qt/popup-menu.hpp +++ b/hiro/qt/popup-menu.hpp @@ -7,7 +7,7 @@ struct pPopupMenu : pObject { auto append(sAction action) -> void; auto remove(sAction action) -> void; - auto setFont(const string& font) -> void override; + auto setFont(const Font& font) -> void override; auto setVisible(bool visible) -> void override; auto _setState() -> void; diff --git a/hiro/qt/status-bar.cpp b/hiro/qt/status-bar.cpp index bf8e3faf..17b0d81a 100644 --- a/hiro/qt/status-bar.cpp +++ b/hiro/qt/status-bar.cpp @@ -13,7 +13,7 @@ auto pStatusBar::setEnabled(bool enabled) -> void { _setState(); } -auto pStatusBar::setFont(const string& font) -> void { +auto pStatusBar::setFont(const Font& font) -> void { _setState(); } diff --git a/hiro/qt/status-bar.hpp b/hiro/qt/status-bar.hpp index 7d0f9bec..309e6c7c 100644 --- a/hiro/qt/status-bar.hpp +++ b/hiro/qt/status-bar.hpp @@ -6,7 +6,7 @@ struct pStatusBar : pObject { Declare(StatusBar, Object) auto setEnabled(bool enabled) -> void override; - auto setFont(const string& font) -> void override; + auto setFont(const Font& font) -> void override; auto setText(const string& text) -> void; auto setVisible(bool visible) -> void override; diff --git a/hiro/qt/utility.cpp b/hiro/qt/utility.cpp index 86eeeef1..d8739c0e 100644 --- a/hiro/qt/utility.cpp +++ b/hiro/qt/utility.cpp @@ -24,6 +24,12 @@ static auto CreateIcon(const nall::image& icon, bool scale = false) -> QIcon { return QIcon(QPixmap::fromImage(qtImage)); } +static auto CreateImage(const Image& image, bool scale = false) -> QIcon { + if(!image) return QIcon(); + QImage qtImage((const uint8_t*)image.data(), image.width(), image.height(), QImage::Format_ARGB32); + return QIcon(QPixmap::fromImage(qtImage)); +} + static auto DropPaths(QDropEvent* event) -> lstring { QList urls = event->mimeData()->urls(); if(urls.size() == 0) return {}; diff --git a/hiro/qt/widget/button.cpp b/hiro/qt/widget/button.cpp index e0593c04..168292ad 100644 --- a/hiro/qt/widget/button.cpp +++ b/hiro/qt/widget/button.cpp @@ -8,7 +8,7 @@ auto pButton::construct() -> void { qtButton->connect(qtButton, SIGNAL(released()), SLOT(onActivate())); setBordered(state().bordered); - setIcon(state().icon); + setImage(state().image); setOrientation(state().orientation); setText(state().text); @@ -21,16 +21,16 @@ auto pButton::destruct() -> void { } auto pButton::minimumSize() const -> Size { - auto size = pFont::size(qtWidget->font(), state().text); + auto size = pFont::size(qtWidget->font(), state().text ? state().text : " "); if(state().orientation == Orientation::Horizontal) { - size.setWidth(size.width() + state().icon.width()); - size.setHeight(max(state().icon.height(), size.height())); + size.setWidth(size.width() + state().image.width()); + size.setHeight(max(state().image.height(), size.height())); } if(state().orientation == Orientation::Vertical) { - size.setWidth(max(state().icon.width(), size.width())); - size.setHeight(size.height() + state().icon.height()); + size.setWidth(max(state().image.width(), size.width())); + size.setHeight(size.height() + state().image.height()); } return {size.width() + (state().text ? 20 : 12), size.height() + 12}; @@ -40,9 +40,9 @@ auto pButton::setBordered(bool bordered) -> void { qtButton->setAutoRaise(!bordered); } -auto pButton::setIcon(const image& icon) -> void { - qtButton->setIconSize(QSize(icon.width(), icon.height())); - qtButton->setIcon(CreateIcon(icon)); +auto pButton::setImage(const Image& image) -> void { + qtButton->setIconSize(QSize(image.width(), image.height())); + qtButton->setIcon(CreateImage(image)); qtButton->setStyleSheet("text-align: top;"); } diff --git a/hiro/qt/widget/button.hpp b/hiro/qt/widget/button.hpp index 2cc25b0d..215383e6 100644 --- a/hiro/qt/widget/button.hpp +++ b/hiro/qt/widget/button.hpp @@ -7,7 +7,7 @@ struct pButton : pWidget { auto minimumSize() const -> Size override; auto setBordered(bool bordered) -> void; - auto setIcon(const image& icon) -> void; + auto setImage(const Image& image) -> void; auto setOrientation(Orientation orientation) -> void; auto setText(const string& text) -> void; diff --git a/hiro/qt/widget/canvas.cpp b/hiro/qt/widget/canvas.cpp index 37181a1b..0dcd1995 100644 --- a/hiro/qt/widget/canvas.cpp +++ b/hiro/qt/widget/canvas.cpp @@ -18,16 +18,11 @@ auto pCanvas::destruct() -> void { } auto pCanvas::minimumSize() const -> Size { - return {max(0, state().size.width()), max(0, state().size.height())}; + if(auto& image = state().image) return image.size(); + return {0, 0}; } auto pCanvas::setColor(Color color) -> void { - mode = Mode::Color; - update(); -} - -auto pCanvas::setData(Size size) -> void { - mode = Mode::Data; update(); } @@ -40,13 +35,11 @@ auto pCanvas::setGeometry(Geometry geometry) -> void { pWidget::setGeometry(geometry); } -auto pCanvas::setGradient(Color topLeft, Color topRight, Color bottomLeft, Color bottomRight) -> void { - mode = Mode::Gradient; +auto pCanvas::setGradient(Gradient gradient) -> void { update(); } -auto pCanvas::setIcon(const image& icon) -> void { - mode = Mode::Icon; +auto pCanvas::setImage(const Image& image) -> void { update(); } @@ -59,14 +52,13 @@ auto pCanvas::_rasterize() -> void { signed width = 0; signed height = 0; - if(mode == Mode::Color || mode == Mode::Gradient) { + if(auto& image = state().image) { + width = image.width(); + height = image.height(); + } else { width = pSizable::state().geometry.width(); height = pSizable::state().geometry.height(); - } else { - width = state().size.width(); - height = state().size.height(); } - if(width <= 0 || height <= 0) return; if(width != qtImageWidth || height != qtImageHeight) _release(); @@ -76,30 +68,18 @@ auto pCanvas::_rasterize() -> void { if(!qtImage) qtImage = new QImage(width, height, QImage::Format_ARGB32); auto buffer = (uint32_t*)qtImage->bits(); - if(mode == Mode::Color) { + if(auto& image = state().image) { + memory::copy(buffer, state().image.data(), width * height * sizeof(uint32_t)); + } else if(auto& gradient = state().gradient) { + auto& colors = gradient.state.colors; + nall::image fill; + fill.allocate(width, height); + fill.gradient(colors[0].value(), colors[1].value(), colors[2].value(), colors[3].value()); + memory::copy(buffer, fill.data(), fill.size()); + } else { uint32_t color = state().color.value(); for(auto n : range(width * height)) buffer[n] = color; } - - if(mode == Mode::Gradient) { - image fill; - fill.allocate(width, height); - fill.gradient( - state().gradient[0].value(), state().gradient[1].value(), state().gradient[2].value(), state().gradient[3].value() - ); - memory::copy(buffer, fill.data(), fill.size()); - } - - if(mode == Mode::Icon) { - auto icon = state().icon; - icon.scale(width, height); - icon.transform(); - memory::copy(buffer, icon.data(), icon.size()); - } - - if(mode == Mode::Data) { - memory::copy(buffer, state().data.data(), state().data.size() * sizeof(uint32_t)); - } } auto pCanvas::_release() -> void { diff --git a/hiro/qt/widget/canvas.hpp b/hiro/qt/widget/canvas.hpp index 91682f43..de30b602 100644 --- a/hiro/qt/widget/canvas.hpp +++ b/hiro/qt/widget/canvas.hpp @@ -7,15 +7,12 @@ struct pCanvas : pWidget { auto minimumSize() const -> Size; auto setColor(Color color) -> void; - auto setData(Size size) -> void; auto setDroppable(bool droppable) -> void; auto setGeometry(Geometry geometry) -> void; - auto setGradient(Color topLeft, Color topRight, Color bottomLeft, Color bottomRight) -> void; - auto setIcon(const image& icon) -> void; + auto setGradient(Gradient gradient) -> void; + auto setImage(const Image& image) -> void; auto update() -> void; - enum class Mode : unsigned { Color, Data, Gradient, Icon }; - auto _rasterize() -> void; auto _release() -> void; @@ -23,7 +20,6 @@ struct pCanvas : pWidget { QImage* qtImage = nullptr; unsigned qtImageWidth = 0; unsigned qtImageHeight = 0; - Mode mode = Mode::Color; }; } diff --git a/hiro/qt/widget/check-button.cpp b/hiro/qt/widget/check-button.cpp index 4e71ef8b..9cf20fbd 100644 --- a/hiro/qt/widget/check-button.cpp +++ b/hiro/qt/widget/check-button.cpp @@ -21,13 +21,13 @@ auto pCheckButton::minimumSize() const -> Size { auto size = pFont::size(qtWidget->font(), state().text); if(state().orientation == Orientation::Horizontal) { - size.setWidth(size.width() + state().icon.width()); - size.setHeight(max(state().icon.height(), size.height())); + size.setWidth(size.width() + state().image.width()); + size.setHeight(max(state().image.height(), size.height())); } if(state().orientation == Orientation::Vertical) { - size.setWidth(max(state().icon.width(), size.width())); - size.setHeight(size.height() + state().icon.height()); + size.setWidth(max(state().image.width(), size.width())); + size.setHeight(size.height() + state().image.height()); } return {size.width() + 20, size.height() + 12}; @@ -41,7 +41,7 @@ auto pCheckButton::setChecked(bool checked) -> void { _setState(); } -auto pCheckButton::setIcon(const image& icon) -> void { +auto pCheckButton::setImage(const Image& image) -> void { _setState(); } @@ -57,8 +57,8 @@ auto pCheckButton::_setState() -> void { lock(); qtCheckButton->setAutoRaise(!state().bordered); qtCheckButton->setChecked(state().checked); - qtCheckButton->setIconSize(QSize(state().icon.width(), state().icon.height())); - qtCheckButton->setIcon(CreateIcon(state().icon)); + qtCheckButton->setIconSize(QSize(state().image.width(), state().image.height())); + qtCheckButton->setIcon(CreateImage(state().image)); qtCheckButton->setStyleSheet("text-align: top;"); switch(state().orientation) { case Orientation::Horizontal: qtCheckButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); break; diff --git a/hiro/qt/widget/check-button.hpp b/hiro/qt/widget/check-button.hpp index 092c8024..49493a6e 100644 --- a/hiro/qt/widget/check-button.hpp +++ b/hiro/qt/widget/check-button.hpp @@ -8,7 +8,7 @@ struct pCheckButton : pWidget { auto minimumSize() const -> Size override; auto setBordered(bool bordered) -> void; auto setChecked(bool checked) -> void; - auto setIcon(const image& icon) -> void; + auto setImage(const Image& image) -> void; auto setOrientation(Orientation orientation) -> void; auto setText(const string& text) -> void; diff --git a/hiro/qt/widget/combo-button-item.cpp b/hiro/qt/widget/combo-button-item.cpp index a38266a4..64cf12a6 100644 --- a/hiro/qt/widget/combo-button-item.cpp +++ b/hiro/qt/widget/combo-button-item.cpp @@ -19,7 +19,7 @@ auto pComboButtonItem::destruct() -> void { } } -auto pComboButtonItem::setIcon(const image& icon) -> void { +auto pComboButtonItem::setImage(const Image& image) -> void { _setState(); } @@ -40,7 +40,7 @@ auto pComboButtonItem::_parent() -> maybe { auto pComboButtonItem::_setState() -> void { if(auto parent = _parent()) { - parent->qtComboButton->setItemIcon(self().offset(), CreateIcon(state().icon)); + parent->qtComboButton->setItemIcon(self().offset(), CreateImage(state().image)); if(state().selected) parent->qtComboButton->setCurrentIndex(self().offset()); parent->qtComboButton->setItemText(self().offset(), QString::fromUtf8(state().text)); } diff --git a/hiro/qt/widget/combo-button-item.hpp b/hiro/qt/widget/combo-button-item.hpp index 11e65a4e..5918b19a 100644 --- a/hiro/qt/widget/combo-button-item.hpp +++ b/hiro/qt/widget/combo-button-item.hpp @@ -5,7 +5,7 @@ namespace hiro { struct pComboButtonItem : pObject { Declare(ComboButtonItem, Object) - auto setIcon(const image& icon) -> void; + auto setImage(const Image& image) -> void; auto setSelected() -> void; auto setText(const string& text) -> void; diff --git a/hiro/qt/widget/list-view-cell.cpp b/hiro/qt/widget/list-view-cell.cpp index 6469ca64..d9125044 100644 --- a/hiro/qt/widget/list-view-cell.cpp +++ b/hiro/qt/widget/list-view-cell.cpp @@ -32,7 +32,7 @@ auto pListViewCell::setForegroundColor(Color color) -> void { _setState(); } -auto pListViewCell::setIcon(const image& icon) -> void { +auto pListViewCell::setImage(const Image& image) -> void { _setState(); } @@ -60,7 +60,7 @@ auto pListViewCell::_setState() -> void { } parent->qtItem->setFont(self().offset(), pFont::create(self().font(true))); parent->qtItem->setForeground(self().offset(), CreateBrush(self().foregroundColor(true))); - parent->qtItem->setIcon(self().offset(), CreateIcon(state().icon)); + parent->qtItem->setIcon(self().offset(), CreateImage(state().image)); parent->qtItem->setText(self().offset(), QString::fromUtf8(state().text)); parent->qtItem->setTextAlignment(self().offset(), CalculateAlignment(self().alignment(true))); grandparent->unlock(); diff --git a/hiro/qt/widget/list-view-cell.hpp b/hiro/qt/widget/list-view-cell.hpp index 4eddcc5e..14f89ee2 100644 --- a/hiro/qt/widget/list-view-cell.hpp +++ b/hiro/qt/widget/list-view-cell.hpp @@ -11,7 +11,7 @@ struct pListViewCell : pObject { auto setChecked(bool checked) -> void; auto setFont(const string& font) -> void; auto setForegroundColor(Color color) -> void; - auto setIcon(const image& icon) -> void; + auto setImage(const Image& image) -> void; auto setText(const string& text) -> void; auto _parent() -> maybe; diff --git a/hiro/qt/widget/list-view-column.cpp b/hiro/qt/widget/list-view-column.cpp index 48b5cc41..aac67854 100644 --- a/hiro/qt/widget/list-view-column.cpp +++ b/hiro/qt/widget/list-view-column.cpp @@ -28,7 +28,7 @@ auto pListViewColumn::setExpandable(bool expandable) -> void { _setState(); } -auto pListViewColumn::setFont(const string& font) -> void { +auto pListViewColumn::setFont(const Font& font) -> void { _setState(); } @@ -40,7 +40,7 @@ auto pListViewColumn::setHorizontalAlignment(double alignment) -> void { _setState(); } -auto pListViewColumn::setIcon(const image& icon) -> void { +auto pListViewColumn::setImage(const Image& image) -> void { //unsupported } diff --git a/hiro/qt/widget/list-view-column.hpp b/hiro/qt/widget/list-view-column.hpp index a3e450a4..b5d1eec5 100644 --- a/hiro/qt/widget/list-view-column.hpp +++ b/hiro/qt/widget/list-view-column.hpp @@ -10,10 +10,10 @@ struct pListViewColumn : pObject { auto setBackgroundColor(Color color) -> void; auto setEditable(bool editable) -> void; auto setExpandable(bool expandable) -> void; - auto setFont(const string& font) -> void override; + auto setFont(const Font& font) -> void override; auto setForegroundColor(Color color) -> void; auto setHorizontalAlignment(double alignment) -> void; - auto setIcon(const image& icon) -> void; + auto setImage(const Image& image) -> void; auto setResizable(bool resizable) -> void; auto setSortable(bool sortable) -> void; auto setText(const string& text) -> void; diff --git a/hiro/qt/widget/list-view-item.cpp b/hiro/qt/widget/list-view-item.cpp index 7bd9d922..b3e71064 100644 --- a/hiro/qt/widget/list-view-item.cpp +++ b/hiro/qt/widget/list-view-item.cpp @@ -28,7 +28,7 @@ auto pListViewItem::setBackgroundColor(Color color) -> void { _setState(); } -auto pListViewItem::setFont(const string& font) -> void { +auto pListViewItem::setFont(const Font& font) -> void { _setState(); } diff --git a/hiro/qt/widget/list-view-item.hpp b/hiro/qt/widget/list-view-item.hpp index ed20122b..eb51d302 100644 --- a/hiro/qt/widget/list-view-item.hpp +++ b/hiro/qt/widget/list-view-item.hpp @@ -9,7 +9,7 @@ struct pListViewItem : pObject { auto remove(sListViewCell cell) -> void; auto setAlignment(Alignment alignment) -> void; auto setBackgroundColor(Color color) -> void; - auto setFont(const string& font) -> void override; + auto setFont(const Font& font) -> void override; auto setForegroundColor(Color color) -> void; auto setSelected(bool selected) -> void; diff --git a/hiro/qt/widget/list-view.cpp b/hiro/qt/widget/list-view.cpp index 2c2e87c6..1a51d5cb 100644 --- a/hiro/qt/widget/list-view.cpp +++ b/hiro/qt/widget/list-view.cpp @@ -154,11 +154,11 @@ auto pListView::_widthOfColumn(unsigned _column) -> unsigned { unsigned width = 8; if(auto& header = state().header) { if(auto column = header->column(_column)) { - if(auto& icon = column->state.icon) { - width += icon.width() + 2; + if(auto& image = column->state.image) { + width += image.width() + 2; } if(auto& text = column->state.text) { - width += Font::size(column->font(true), text).width(); + width += pFont::size(column->font(true), text).width(); } } } @@ -172,11 +172,11 @@ auto pListView::_widthOfCell(unsigned _row, unsigned _column) -> unsigned { if(cell->state.checkable) { width += 16 + 2; } - if(auto& icon = cell->state.icon) { - width += icon.width() + 2; + if(auto& image = cell->state.image) { + width += image.width() + 2; } if(auto& text = cell->state.text) { - width += Font::size(cell->font(true), text).width(); + width += pFont::size(cell->font(true), text).width(); } } } diff --git a/hiro/qt/widget/radio-button.cpp b/hiro/qt/widget/radio-button.cpp index be24041e..0e7a9f55 100644 --- a/hiro/qt/widget/radio-button.cpp +++ b/hiro/qt/widget/radio-button.cpp @@ -9,6 +9,7 @@ auto pRadioButton::construct() -> void { qtRadioButton->connect(qtRadioButton, SIGNAL(toggled(bool)), SLOT(onActivate())); pWidget::construct(); + setGroup(state().group); _setState(); } @@ -21,13 +22,13 @@ auto pRadioButton::minimumSize() const -> Size { auto size = pFont::size(qtWidget->font(), state().text); if(state().orientation == Orientation::Horizontal) { - size.setWidth(size.width() + state().icon.width()); - size.setHeight(max(state().icon.height(), size.height())); + size.setWidth(size.width() + state().image.width()); + size.setHeight(max(state().image.height(), size.height())); } if(state().orientation == Orientation::Vertical) { - size.setWidth(max(state().icon.width(), size.width())); - size.setHeight(size.height() + state().icon.height()); + size.setWidth(max(state().image.width(), size.width())); + size.setHeight(size.height() + state().image.height()); } return {size.width() + 20, size.height() + 12}; @@ -41,10 +42,24 @@ auto pRadioButton::setChecked() -> void { } auto pRadioButton::setGroup(sGroup group) -> void { - _setState(); + bool first = true; + if(auto& group = state().group) { + group->self()->lock(); + for(auto& weak : group->state.objects) { + if(auto object = weak.acquire()) { + if(auto radioButton = dynamic_cast(object.data())) { + if(auto self = radioButton->self()) { + self->qtRadioButton->setChecked(radioButton->state.checked = first); + first = false; + } + } + } + } + group->self()->unlock(); + } } -auto pRadioButton::setIcon(const image& icon) -> void { +auto pRadioButton::setImage(const Image& image) -> void { _setState(); } @@ -70,8 +85,8 @@ auto pRadioButton::_setState() -> void { } group->self()->unlock(); } - qtRadioButton->setIconSize(QSize(state().icon.width(), state().icon.height())); - qtRadioButton->setIcon(CreateIcon(state().icon)); + qtRadioButton->setIconSize(QSize(state().image.width(), state().image.height())); + qtRadioButton->setIcon(CreateImage(state().image)); qtRadioButton->setStyleSheet("text-align: top;"); switch(state().orientation) { case Orientation::Horizontal: qtRadioButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); break; diff --git a/hiro/qt/widget/radio-button.hpp b/hiro/qt/widget/radio-button.hpp index 2aec616b..1ebe7a7e 100644 --- a/hiro/qt/widget/radio-button.hpp +++ b/hiro/qt/widget/radio-button.hpp @@ -9,7 +9,7 @@ struct pRadioButton : pWidget { auto setBordered(bool bordered) -> void; auto setChecked() -> void; auto setGroup(sGroup group) -> void; - auto setIcon(const image& icon) -> void; + auto setImage(const Image& image) -> void; auto setOrientation(Orientation orientation) -> void; auto setText(const string& text) -> void; diff --git a/hiro/qt/widget/radio-label.cpp b/hiro/qt/widget/radio-label.cpp index 81a7d6f6..4fb310f5 100644 --- a/hiro/qt/widget/radio-label.cpp +++ b/hiro/qt/widget/radio-label.cpp @@ -8,6 +8,7 @@ auto pRadioLabel::construct() -> void { qtRadioLabel->connect(qtRadioLabel, SIGNAL(toggled(bool)), SLOT(onActivate())); pWidget::construct(); + setGroup(state().group); _setState(); } @@ -26,7 +27,21 @@ auto pRadioLabel::setChecked() -> void { } auto pRadioLabel::setGroup(sGroup group) -> void { - _setState(); + bool first = true; + if(auto& group = state().group) { + group->self()->lock(); + for(auto& weak : group->state.objects) { + if(auto object = weak.acquire()) { + if(auto radioLabel = dynamic_cast(object.data())) { + if(auto self = radioLabel->self()) { + self->qtRadioLabel->setChecked(radioLabel->state.checked = first); + first = false; + } + } + } + } + group->self()->unlock(); + } } auto pRadioLabel::setText(const string& text) -> void { diff --git a/hiro/qt/widget/tab-frame-item.cpp b/hiro/qt/widget/tab-frame-item.cpp index 72d8ca73..fa0b4f54 100644 --- a/hiro/qt/widget/tab-frame-item.cpp +++ b/hiro/qt/widget/tab-frame-item.cpp @@ -34,7 +34,7 @@ auto pTabFrameItem::setGeometry(Geometry geometry) -> void { } } -auto pTabFrameItem::setIcon(const image& icon) -> void { +auto pTabFrameItem::setImage(const Image& image) -> void { _setState(); } @@ -62,7 +62,7 @@ auto pTabFrameItem::_parent() -> maybe { auto pTabFrameItem::_setState() -> void { if(auto parent = _parent()) { - parent->qtTabFrame->setTabIcon(self().offset(), CreateIcon(state().icon)); + parent->qtTabFrame->setTabIcon(self().offset(), CreateImage(state().image)); if(state().selected) parent->qtTabFrame->setCurrentIndex(self().offset()); parent->qtTabFrame->setTabText(self().offset(), QString::fromUtf8(state().text)); if(auto layout = state().layout) { diff --git a/hiro/qt/widget/tab-frame-item.hpp b/hiro/qt/widget/tab-frame-item.hpp index 4b8d59c8..cb383b6e 100644 --- a/hiro/qt/widget/tab-frame-item.hpp +++ b/hiro/qt/widget/tab-frame-item.hpp @@ -8,7 +8,7 @@ struct pTabFrameItem : pObject { auto append(sLayout layout) -> void; auto remove(sLayout layout) -> void; auto setClosable(bool closable) -> void; - auto setIcon(const image& icon) -> void; + auto setImage(const Image& image) -> void; auto setGeometry(Geometry geometry) -> void; auto setMovable(bool movable) -> void; auto setSelected() -> void; diff --git a/hiro/qt/widget/tab-frame.cpp b/hiro/qt/widget/tab-frame.cpp index d1fb1a5d..89670136 100644 --- a/hiro/qt/widget/tab-frame.cpp +++ b/hiro/qt/widget/tab-frame.cpp @@ -22,9 +22,6 @@ auto pTabFrame::append(sTabFrameItem item) -> void { auto pTabFrame::remove(sTabFrameItem item) -> void { } -auto pTabFrame::setEdge(Edge edge) -> void { -} - auto pTabFrame::setGeometry(Geometry geometry) -> void { pWidget::setGeometry(geometry); @@ -33,7 +30,18 @@ auto pTabFrame::setGeometry(Geometry geometry) -> void { } } +auto pTabFrame::setNavigation(Navigation navigation) -> void { + _setState(); +} + auto pTabFrame::_setState() -> void { + switch(state().navigation) { default: + case Navigation::Top: qtTabFrame->setTabPosition(QTabWidget::TabPosition::North); break; + case Navigation::Bottom: qtTabFrame->setTabPosition(QTabWidget::TabPosition::South); break; + case Navigation::Left: qtTabFrame->setTabPosition(QTabWidget::TabPosition::West); break; + case Navigation::Right: qtTabFrame->setTabPosition(QTabWidget::TabPosition::East); break; + } + for(auto& item : state().items) { if(auto self = item->self()) self->_setState(); } diff --git a/hiro/qt/widget/tab-frame.hpp b/hiro/qt/widget/tab-frame.hpp index 4345e748..e87cdc36 100644 --- a/hiro/qt/widget/tab-frame.hpp +++ b/hiro/qt/widget/tab-frame.hpp @@ -7,8 +7,8 @@ struct pTabFrame : pWidget { auto append(sTabFrameItem item) -> void; auto remove(sTabFrameItem item) -> void; - auto setEdge(Edge edge) -> void; auto setGeometry(Geometry geometry) -> void override; + auto setNavigation(Navigation navigation) -> void; auto _setState() -> void; diff --git a/hiro/qt/widget/text-edit.cpp b/hiro/qt/widget/text-edit.cpp index ab3e5ae4..f5d55313 100644 --- a/hiro/qt/widget/text-edit.cpp +++ b/hiro/qt/widget/text-edit.cpp @@ -19,7 +19,7 @@ auto pTextEdit::setBackgroundColor(Color color) -> void { _setState(); } -auto pTextEdit::setCursorPosition(unsigned position) -> void { +auto pTextEdit::setCursor(Cursor cursor) -> void { _setState(); } @@ -53,8 +53,9 @@ auto pTextEdit::_setState() -> void { //todo } QTextCursor cursor = qtTextEdit->textCursor(); - unsigned lastCharacter = strlen(qtTextEdit->toPlainText().toUtf8().constData()); - cursor.setPosition(min(state().cursorPosition, lastCharacter)); + signed lastCharacter = strlen(qtTextEdit->toPlainText().toUtf8().constData()); + cursor.setPosition(max(0, min(lastCharacter, state().cursor.offset()))); + cursor.setPosition(max(0, min(lastCharacter, state().cursor.offset() + state().cursor.length())), QTextCursor::KeepAnchor); qtTextEdit->setTextCursor(cursor); qtTextEdit->setTextInteractionFlags(state().editable ? Qt::TextEditorInteraction diff --git a/hiro/qt/widget/text-edit.hpp b/hiro/qt/widget/text-edit.hpp index 882d2881..a810a3ed 100644 --- a/hiro/qt/widget/text-edit.hpp +++ b/hiro/qt/widget/text-edit.hpp @@ -6,7 +6,7 @@ struct pTextEdit : pWidget { Declare(TextEdit, Widget) auto setBackgroundColor(Color color) -> void; - auto setCursorPosition(unsigned position) -> void; + auto setCursor(Cursor cursor) -> void; auto setEditable(bool editable) -> void; auto setForegroundColor(Color color) -> void; auto setText(const string& text) -> void; diff --git a/hiro/qt/widget/widget.cpp b/hiro/qt/widget/widget.cpp index 95335701..03666911 100644 --- a/hiro/qt/widget/widget.cpp +++ b/hiro/qt/widget/widget.cpp @@ -47,7 +47,7 @@ auto pWidget::setFocused() -> void { qtWidget->setFocus(Qt::OtherFocusReason); } -auto pWidget::setFont(const string& font) -> void { +auto pWidget::setFont(const Font& font) -> void { if(!qtWidget) return; qtWidget->setFont(pFont::create(font)); } diff --git a/hiro/qt/widget/widget.hpp b/hiro/qt/widget/widget.hpp index dc714610..080c2a14 100644 --- a/hiro/qt/widget/widget.hpp +++ b/hiro/qt/widget/widget.hpp @@ -8,7 +8,7 @@ struct pWidget : pSizable { auto focused() const -> bool override; auto setEnabled(bool enabled) -> void override; auto setFocused() -> void override; - auto setFont(const string& font) -> void override; + auto setFont(const Font& font) -> void override; auto setGeometry(Geometry geometry) -> void override; auto setVisible(bool visible) -> void override; diff --git a/hiro/windows/action/menu-item.cpp b/hiro/windows/action/menu-item.cpp index fa67aefc..df2bbeb5 100644 --- a/hiro/windows/action/menu-item.cpp +++ b/hiro/windows/action/menu-item.cpp @@ -10,7 +10,7 @@ auto pMenuItem::destruct() -> void { if(hbitmap) { DeleteObject(hbitmap); hbitmap = nullptr; } } -auto pMenuItem::setIcon(const image& icon) -> void { +auto pMenuItem::setImage(const Image& image) -> void { _createBitmap(); _synchronize(); } @@ -26,8 +26,10 @@ auto pMenuItem::onActivate() -> void { auto pMenuItem::_createBitmap() -> void { if(hbitmap) { DeleteObject(hbitmap); hbitmap = nullptr; } - if(auto icon = state().icon) { - icon.transform(); + if(auto& image = state().image) { + nall::image icon; + icon.allocate(image.width(), image.height()); + memory::copy(icon.data(), image.data(), icon.size()); icon.alphaBlend(GetSysColor(COLOR_MENU)); //Windows does not alpha blend menu icons properly (leaves black outline) icon.scale(GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK), Interpolation::Linear); hbitmap = CreateBitmap(icon); diff --git a/hiro/windows/action/menu-item.hpp b/hiro/windows/action/menu-item.hpp index 636eef4a..8f94950a 100644 --- a/hiro/windows/action/menu-item.hpp +++ b/hiro/windows/action/menu-item.hpp @@ -5,7 +5,7 @@ namespace hiro { struct pMenuItem : pAction { Declare(MenuItem, Action) - auto setIcon(const image& icon) -> void; + auto setImage(const Image& image) -> void; auto setText(const string& text) -> void; auto onActivate() -> void; diff --git a/hiro/windows/action/menu-radio-item.cpp b/hiro/windows/action/menu-radio-item.cpp index 56de8b3d..04f12e7c 100644 --- a/hiro/windows/action/menu-radio-item.cpp +++ b/hiro/windows/action/menu-radio-item.cpp @@ -3,13 +3,14 @@ namespace hiro { auto pMenuRadioItem::construct() -> void { + setGroup(state().group); } auto pMenuRadioItem::destruct() -> void { } auto pMenuRadioItem::setChecked() -> void { - if(auto group = self().group()) { + if(auto& group = state().group) { for(auto& weak : group->state.objects) { if(auto object = weak.acquire()) { if(auto menuRadioItem = dynamic_cast(object.data())) { @@ -32,6 +33,20 @@ auto pMenuRadioItem::setChecked() -> void { } auto pMenuRadioItem::setGroup(sGroup group) -> void { + bool first = true; + if(auto& group = state().group) { + for(auto& weak : group->state.objects) { + if(auto object = weak.acquire()) { + if(auto menuRadioItem = dynamic_cast(object.data())) { + if(auto self = menuRadioItem->self()) { + menuRadioItem->state.checked = first; + first = false; + } + } + } + } + } + setChecked(); } auto pMenuRadioItem::setText(const string& text) -> void { diff --git a/hiro/windows/action/menu.cpp b/hiro/windows/action/menu.cpp index 778e8dcd..56f2e03b 100644 --- a/hiro/windows/action/menu.cpp +++ b/hiro/windows/action/menu.cpp @@ -19,7 +19,7 @@ auto pMenu::remove(sAction action) -> void { _synchronize(); } -auto pMenu::setIcon(const image& icon) -> void { +auto pMenu::setImage(const Image& image) -> void { _createBitmap(); _synchronize(); } @@ -31,8 +31,10 @@ auto pMenu::setText(const string& text) -> void { auto pMenu::_createBitmap() -> void { if(hbitmap) { DeleteObject(hbitmap); hbitmap = 0; } - if(auto icon = state().icon) { - icon.transform(); + if(auto& image = state().image) { + nall::image icon; + icon.allocate(image.width(), image.height()); + memory::copy(icon.data(), image.data(), icon.size()); icon.alphaBlend(GetSysColor(COLOR_MENU)); //Windows does not alpha blend menu icons properly (leaves black outline) icon.scale(GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK), Interpolation::Linear); hbitmap = CreateBitmap(icon); diff --git a/hiro/windows/action/menu.hpp b/hiro/windows/action/menu.hpp index b8caee56..63aea704 100644 --- a/hiro/windows/action/menu.hpp +++ b/hiro/windows/action/menu.hpp @@ -7,7 +7,7 @@ struct pMenu : pAction { auto append(sAction action) -> void; auto remove(sAction action) -> void; - auto setIcon(const image& icon) -> void; + auto setImage(const Image& image) -> void; auto setText(const string& text) -> void; auto _createBitmap() -> void; diff --git a/hiro/windows/font.cpp b/hiro/windows/font.cpp index 80d240be..f3c87851 100644 --- a/hiro/windows/font.cpp +++ b/hiro/windows/font.cpp @@ -2,59 +2,14 @@ namespace hiro { -auto pFont::serif(unsigned size, string style) -> string { - if(size == 0) size = 8; - if(style == "") style = "Normal"; - return {"Georgia, ", size, ", ", style}; -} - -auto pFont::sans(unsigned size, string style) -> string { - if(size == 0) size = 8; - if(style == "") style = "Normal"; - return {"Tahoma, ", size, ", ", style}; -} - -auto pFont::monospace(unsigned size, string style) -> string { - if(size == 0) size = 8; - if(style == "") style = "Normal"; - return {"Lucida Console, ", size, ", ", style}; -} - -auto pFont::size(const string& font, const string& text) -> Size { - HFONT hfont = pFont::create(font); - Size size = pFont::size(hfont, text); +auto pFont::size(const Font& font, const string& text) -> Size { + auto hfont = pFont::create(font); + auto size = pFont::size(hfont, text); pFont::free(hfont); return size; } -auto pFont::create(const string& description) -> HFONT { - lstring part = description.split(",", 2L).strip(); - - string family = "Tahoma"; - unsigned size = 8u; - bool bold = false; - bool italic = false; - - if(part[0] != "") family = part[0]; - if(part.size() >= 2) size = decimal(part[1]); - if(part.size() >= 3) bold = (bool)part[2].find("Bold"); - if(part.size() >= 3) italic = (bool)part[2].find("Italic"); - - return CreateFont( - -(size * 96.0 / 72.0 + 0.5), - 0, 0, 0, bold ? FW_BOLD : FW_NORMAL, italic, 0, 0, 0, 0, 0, 0, 0, - utf16_t(family) - ); -} - -auto pFont::free(HFONT hfont) -> void { - DeleteObject(hfont); -} - -auto pFont::size(HFONT hfont, string text) -> Size { - //temporary fix: empty text string returns height of zero; bad for eg Button height - if(text.empty()) text = " "; - +auto pFont::size(HFONT hfont, const string& text) -> Size { HDC hdc = GetDC(0); SelectObject(hdc, hfont); RECT rc = {0, 0, 0, 0}; @@ -63,6 +18,25 @@ auto pFont::size(HFONT hfont, string text) -> Size { return {rc.right, rc.bottom}; } +auto pFont::family(const string& family) -> string { + if(family == Font::Sans ) return "Tahoma"; + if(family == Font::Serif) return "Georgia"; + if(family == Font::Mono ) return "Lucida Console"; + return family ? family : "Tahoma"; +} + +auto pFont::create(const Font& font) -> HFONT { + return CreateFont( + -((font.size() ? font.size() : 8) * 96.0 / 72.0 + 0.5), + 0, 0, 0, font.bold() ? FW_BOLD : FW_NORMAL, font.italic(), 0, 0, 0, 0, 0, 0, 0, + utf16_t(family(font.family())) + ); +} + +auto pFont::free(HFONT hfont) -> void { + DeleteObject(hfont); +} + } #endif diff --git a/hiro/windows/font.hpp b/hiro/windows/font.hpp index c899e31f..1882d550 100644 --- a/hiro/windows/font.hpp +++ b/hiro/windows/font.hpp @@ -3,14 +3,11 @@ namespace hiro { struct pFont { - static auto serif(unsigned size, string style) -> string; - static auto sans(unsigned size, string style) -> string; - static auto monospace(unsigned size, string style) -> string; - static auto size(const string& font, const string& text) -> Size; - - static auto create(const string& description) -> HFONT; + static auto size(const Font& font, const string& text) -> Size; + static auto size(HFONT hfont, const string& text) -> Size; + static auto family(const string& family) -> string; + static auto create(const Font& font) -> HFONT; static auto free(HFONT hfont) -> void; - static auto size(HFONT hfont, string text) -> Size; }; } diff --git a/hiro/windows/header.hpp b/hiro/windows/header.hpp index 6314c8eb..e260678e 100644 --- a/hiro/windows/header.hpp +++ b/hiro/windows/header.hpp @@ -35,6 +35,30 @@ #define Button_SetImageList(hwnd, pbuttonImagelist) (WINBOOL)SNDMSG((hwnd),BCM_SETIMAGELIST,0,(LPARAM)(pbuttonImagelist)) #endif +#if !defined(BP_PUSHBUTTON) + #define BP_PUSHBUTTON 1 +#endif + +#if !defined(PBS_NORMAL) + #define PBS_NORMAL 1 +#endif + +#if !defined(PBS_HOT) + #define PBS_HOT 2 +#endif + +#if !defined(PBS_PRESSED) + #define PBS_PRESSED 3 +#endif + +#if !defined(PBS_DISABLED) + #define PBS_DISABLED 4 +#endif + +#if !defined(PBS_DEFAULTED) + #define PBS_DEFAULTED 5 +#endif + #if !defined(BP_CHECKBOX) #define BP_CHECKBOX 3 #endif diff --git a/hiro/windows/hotkey.cpp b/hiro/windows/hotkey.cpp deleted file mode 100644 index 608ba7bb..00000000 --- a/hiro/windows/hotkey.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#if defined(Hiro_Hotkey) - -namespace hiro { - -auto pHotkey::construct() -> void { -} - -auto pHotkey::destruct() -> void { -} - -} - -#endif diff --git a/hiro/windows/hotkey.hpp b/hiro/windows/hotkey.hpp deleted file mode 100644 index b1caa7bc..00000000 --- a/hiro/windows/hotkey.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#if defined(Hiro_Hotkey) - -namespace hiro { - -struct pHotkey : pObject { - Declare(Hotkey, Object) -}; - -} - -#endif diff --git a/hiro/windows/layout.cpp b/hiro/windows/layout.cpp index caec0182..fbd3dcd4 100644 --- a/hiro/windows/layout.cpp +++ b/hiro/windows/layout.cpp @@ -14,7 +14,7 @@ auto pLayout::setEnabled(bool enabled) -> void { } } -auto pLayout::setFont(const string& font) -> void { +auto pLayout::setFont(const Font& font) -> void { for(auto& sizable : state().sizables) { if(auto self = sizable->self()) self->setFont(sizable->font(true)); } diff --git a/hiro/windows/layout.hpp b/hiro/windows/layout.hpp index 3895710c..dba11836 100644 --- a/hiro/windows/layout.hpp +++ b/hiro/windows/layout.hpp @@ -6,7 +6,7 @@ struct pLayout : pSizable { Declare(Layout, Sizable) auto setEnabled(bool enabled) -> void override; - auto setFont(const string& font) -> void override; + auto setFont(const Font& font) -> void override; auto setVisible(bool visible) -> void override; }; diff --git a/hiro/windows/menu-bar.cpp b/hiro/windows/menu-bar.cpp index bc98b95c..fc2c5bc5 100644 --- a/hiro/windows/menu-bar.cpp +++ b/hiro/windows/menu-bar.cpp @@ -25,7 +25,7 @@ auto pMenuBar::setEnabled(bool enabled) -> void { _update(); } -auto pMenuBar::setFont(const string& font) -> void { +auto pMenuBar::setFont(const Font& font) -> void { //unsupported } diff --git a/hiro/windows/menu-bar.hpp b/hiro/windows/menu-bar.hpp index 13d4b2af..b804f256 100644 --- a/hiro/windows/menu-bar.hpp +++ b/hiro/windows/menu-bar.hpp @@ -8,7 +8,7 @@ struct pMenuBar : pObject { auto append(sMenu menu) -> void; auto remove(sMenu menu) -> void; auto setEnabled(bool enabled) -> void override; - auto setFont(const string& font) -> void override; + auto setFont(const Font& font) -> void override; auto setVisible(bool visible) -> void override; auto _parent() -> maybe; diff --git a/hiro/windows/object.cpp b/hiro/windows/object.cpp index bc074c4a..4cf170d7 100644 --- a/hiro/windows/object.cpp +++ b/hiro/windows/object.cpp @@ -27,7 +27,7 @@ auto pObject::setEnabled(bool enabled) -> void { auto pObject::setFocused() -> void { } -auto pObject::setFont(const string& font) -> void { +auto pObject::setFont(const Font& font) -> void { } auto pObject::setGroup(sGroup group) -> void { diff --git a/hiro/windows/object.hpp b/hiro/windows/object.hpp index 4951650e..c30c8ade 100644 --- a/hiro/windows/object.hpp +++ b/hiro/windows/object.hpp @@ -14,7 +14,7 @@ struct pObject { virtual auto reset() -> void; virtual auto setEnabled(bool enabled) -> void; virtual auto setFocused() -> void; - virtual auto setFont(const string& font) -> void; + virtual auto setFont(const Font& font) -> void; virtual auto setGroup(sGroup group) -> void; virtual auto setVisible(bool visible) -> void; diff --git a/hiro/windows/platform.cpp b/hiro/windows/platform.cpp index 36fc5196..42dac191 100644 --- a/hiro/windows/platform.cpp +++ b/hiro/windows/platform.cpp @@ -11,7 +11,6 @@ #include "object.cpp" #include "group.cpp" -#include "hotkey.cpp" #include "font.cpp" #include "timer.cpp" #include "window.cpp" diff --git a/hiro/windows/platform.hpp b/hiro/windows/platform.hpp index 954c2bc0..7c6be74c 100644 --- a/hiro/windows/platform.hpp +++ b/hiro/windows/platform.hpp @@ -41,7 +41,6 @@ static vector windows; #include "object.hpp" #include "group.hpp" -#include "hotkey.hpp" #include "timer.hpp" #include "window.hpp" #include "status-bar.hpp" diff --git a/hiro/windows/popup-menu.cpp b/hiro/windows/popup-menu.cpp index 3ef1058d..001fbfb7 100644 --- a/hiro/windows/popup-menu.cpp +++ b/hiro/windows/popup-menu.cpp @@ -17,7 +17,7 @@ auto pPopupMenu::append(sAction action) -> void { auto pPopupMenu::remove(sAction action) -> void { } -auto pPopupMenu::setFont(const string& font) -> void { +auto pPopupMenu::setFont(const Font& font) -> void { } auto pPopupMenu::setVisible(bool visible) -> void { diff --git a/hiro/windows/popup-menu.hpp b/hiro/windows/popup-menu.hpp index 34fe094a..a51cedfa 100644 --- a/hiro/windows/popup-menu.hpp +++ b/hiro/windows/popup-menu.hpp @@ -7,7 +7,7 @@ struct pPopupMenu : pObject { auto append(sAction action) -> void; auto remove(sAction action) -> void; - auto setFont(const string& font) -> void override; + auto setFont(const Font& font) -> void override; auto setVisible(bool visible) -> void override; HWND hwnd = nullptr; diff --git a/hiro/windows/status-bar.cpp b/hiro/windows/status-bar.cpp index 19b22f8a..68ef63c4 100644 --- a/hiro/windows/status-bar.cpp +++ b/hiro/windows/status-bar.cpp @@ -25,7 +25,7 @@ auto pStatusBar::setEnabled(bool enabled) -> void { //unsupported } -auto pStatusBar::setFont(const string& font) -> void { +auto pStatusBar::setFont(const Font& font) -> void { if(hfont) DeleteObject(hfont); hfont = pFont::create(font); if(hwnd) SendMessage(hwnd, WM_SETFONT, (WPARAM)hfont, 0); diff --git a/hiro/windows/status-bar.hpp b/hiro/windows/status-bar.hpp index 75df1012..618e0268 100644 --- a/hiro/windows/status-bar.hpp +++ b/hiro/windows/status-bar.hpp @@ -6,7 +6,7 @@ struct pStatusBar : pObject { Declare(StatusBar, Object) auto setEnabled(bool enabled) -> void override; - auto setFont(const string& font) -> void override; + auto setFont(const Font& font) -> void override; auto setText(const string& text) -> void; auto setVisible(bool visible) -> void override; diff --git a/hiro/windows/utility.cpp b/hiro/windows/utility.cpp index e4b023fe..21700816 100644 --- a/hiro/windows/utility.cpp +++ b/hiro/windows/utility.cpp @@ -5,6 +5,8 @@ static const unsigned WindowsXP = 0x0501; static const unsigned WindowsVista = 0x0600; static const unsigned Windows7 = 0x0601; +static auto Button_CustomDraw(HWND, PAINTSTRUCT&, unsigned, const Font&, const Image&, Orientation, const string&) -> void; + static auto OsVersion() -> unsigned { OSVERSIONINFO versionInfo{0}; versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); @@ -12,7 +14,9 @@ static auto OsVersion() -> unsigned { return (versionInfo.dwMajorVersion << 8) + (versionInfo.dwMajorVersion << 0); } -static auto CreateBitmap(const image& icon) -> HBITMAP { +static auto CreateBitmap(image icon) -> HBITMAP { + icon.alphaMultiply(); //Windows AlphaBlend() requires premultiplied image data + icon.transform(); HDC hdc = GetDC(0); BITMAPINFO bitmapInfo; memset(&bitmapInfo, 0, sizeof(BITMAPINFO)); @@ -30,6 +34,33 @@ static auto CreateBitmap(const image& icon) -> HBITMAP { return hbitmap; } +static auto CreateBitmap(const Image& image) -> HBITMAP { + HDC hdc = GetDC(0); + BITMAPINFO bitmapInfo{0}; + bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bitmapInfo.bmiHeader.biWidth = image.width(); + bitmapInfo.bmiHeader.biHeight = -image.height(); //bitmaps are stored upside down unless we negate height + bitmapInfo.bmiHeader.biPlanes = 1; + bitmapInfo.bmiHeader.biBitCount = 32; + bitmapInfo.bmiHeader.biCompression = BI_RGB; + bitmapInfo.bmiHeader.biSizeImage = image.width() * image.height() * sizeof(uint32_t); + void* bits = nullptr; + HBITMAP hbitmap = CreateDIBSection(hdc, &bitmapInfo, DIB_RGB_COLORS, &bits, nullptr, 0); + if(bits) { + auto source = (const uint8_t*)image.data(); + auto target = (uint8_t*)bits; + for(auto n : range(image.width() * image.height())) { + target[0] = (source[0] * source[3]) / 255; + target[1] = (source[1] * source[3]) / 255; + target[2] = (source[2] * source[3]) / 255; + target[3] = (source[3]); + source += 4, target += 4; + } + } + ReleaseDC(0, hdc); + return hbitmap; +} + static auto CreateRGB(const Color& color) -> COLORREF { return RGB(color.red(), color.green(), color.blue()); } @@ -62,16 +93,18 @@ static auto GetWindowZOrder(HWND hwnd) -> unsigned { return z; } -static auto ImageList_Append(HIMAGELIST imageList, const image& source, unsigned scale) -> void { - auto image = source; - if(image.empty()) { - image.allocate(scale, scale); - image.fill(GetSysColor(COLOR_WINDOW)); +static auto ImageList_Append(HIMAGELIST imageList, const Image& image, unsigned scale) -> void { + nall::image icon; + if(image) { + icon.allocate(image.width(), image.height()); + memory::copy(icon.data(), image.data(), icon.size()); + icon.scale(scale, scale); + } else { + icon.allocate(scale, scale); + icon.fill(GetSysColor(COLOR_WINDOW)); } - image.transform(); - image.scale(scale, scale); - HBITMAP bitmap = CreateBitmap(image); - ImageList_Add(imageList, bitmap, NULL); + HBITMAP bitmap = CreateBitmap(icon); + ImageList_Add(imageList, bitmap, nullptr); DeleteObject(bitmap); } diff --git a/hiro/windows/widget/button.cpp b/hiro/windows/widget/button.cpp index 76bc6b1e..eabd2b77 100644 --- a/hiro/windows/widget/button.cpp +++ b/hiro/windows/widget/button.cpp @@ -2,44 +2,92 @@ namespace hiro { +static auto Button_paintProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, + bool bordered, bool checked, bool enabled, const Font& font, const Image& image, Orientation orientation, const string& text +) -> LRESULT { + if(msg == WM_PAINT) { + PAINTSTRUCT ps; + BeginPaint(hwnd, &ps); + auto state = Button_GetState(hwnd); + Button_CustomDraw(hwnd, ps, + (state & BST_PUSHED || checked) ? PBS_PRESSED + : (state & BST_HOT) ? PBS_HOT + : bordered ? (enabled ? PBS_NORMAL : PBS_DISABLED) + : 0, font, image, orientation, text + ); + EndPaint(hwnd, &ps); + } + return DefWindowProc(hwnd, msg, wparam, lparam); +} + +//BUTTON cannot draw borderless buttons on its own +//BS_OWNERDRAW will send WM_DRAWITEM; but will disable hot-tracking notifications +//to gain hot-tracking plus borderless buttons; BUTTON is superclassed and WM_PAINT is hijacked +//note: letting hiro paint bordered buttons will lose the fade animations on Vista+; +//however, it will allow placing icons immediately next to text (original forces icon left alignment) +static auto CALLBACK Button_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> LRESULT { + if(auto object = (mObject*)GetWindowLongPtr(hwnd, GWLP_USERDATA)) { + if(auto button = dynamic_cast(object)) { + if(auto self = button->self()) { + if(msg == WM_ERASEBKGND) return DefWindowProc(hwnd, msg, wparam, lparam); + if(msg == WM_PAINT) return Button_paintProc(hwnd, msg, wparam, lparam, + button->state.bordered, false, button->enabled(true), button->font(true), + button->state.image, button->state.orientation, button->state.text + ); + return self->windowProc(hwnd, msg, wparam, lparam); + } + } + } + return DefWindowProc(hwnd, msg, wparam, lparam); +} + auto pButton::construct() -> void { hwnd = CreateWindow( - L"BUTTON", L"", - WS_CHILD | WS_TABSTOP, + L"BUTTON", L"", WS_CHILD | WS_TABSTOP, 0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0 ); SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference); + windowProc = (WindowProc)GetWindowLongPtr(hwnd, GWLP_WNDPROC); + SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)Button_windowProc); pWidget::_setState(); _setState(); - setBordered(state().bordered); } auto pButton::destruct() -> void { - if(hbitmap) { DeleteObject(hbitmap); hbitmap = 0; } - if(himagelist) { ImageList_Destroy(himagelist); himagelist = 0; } DestroyWindow(hwnd); } auto pButton::minimumSize() const -> Size { - auto size = pFont::size(hfont, state().text); - + Size image = state().image.size(); + Size text = state().text ? pFont::size(self().font(true), state().text) : Size{}; + Size size; if(state().orientation == Orientation::Horizontal) { - size.setWidth(size.width() + state().icon.width()); - size.setHeight(max(size.height(), state().icon.height())); + size.setWidth(image.width() + (image && text ? 5 : 0) + text.width()); + size.setHeight(max(image.height(), text.height())); } - if(state().orientation == Orientation::Vertical) { - size.setWidth(max(size.width(), state().icon.width())); - size.setHeight(size.height() + state().icon.height()); + size.setWidth(max(image.width(), text.width())); + size.setHeight(image.height() + (image && text ? 5 : 0) + text.height()); } - - return {size.width() + (state().text ? 20 : 13), size.height() + 10}; + size.setHeight(max(size.height(), pFont::size(self().font(true), " ").height())); + return {size.width() + (state().bordered && text ? 20 : 10), size.height() + 10}; } auto pButton::setBordered(bool bordered) -> void { + _setState(); } -auto pButton::setIcon(const image& icon) -> void { +auto pButton::setEnabled(bool enabled) -> void { + pWidget::setEnabled(enabled); + _setState(); +} + +auto pButton::setFont(const Font& font) -> void { + pWidget::setFont(font); + _setState(); +} + +auto pButton::setImage(const Image& image) -> void { _setState(); } @@ -51,42 +99,84 @@ auto pButton::setText(const string& text) -> void { _setState(); } +auto pButton::setVisible(bool visible) -> void { + pWidget::setVisible(visible); + _setState(); +} + auto pButton::onActivate() -> void { self().doActivate(); } -//performs setIcon, setOrientation, setText auto pButton::_setState() -> void { - image icon = state().icon; - icon.transform(); + InvalidateRect(hwnd, 0, false); +} - if(hbitmap) { DeleteObject(hbitmap); hbitmap = 0; } - if(himagelist) { ImageList_Destroy(himagelist); himagelist = 0; } +//this function is designed to be used with Button, CheckButton, and RadioButton +auto Button_CustomDraw(HWND hwnd, PAINTSTRUCT& ps, unsigned state, const Font& font, const Image& image, Orientation orientation, const string& text) -> void { + RECT rc; + GetClientRect(hwnd, &rc); + Geometry geometry{rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top}, imageGeometry, textGeometry; + if(image) imageGeometry.setSize(image.size()); + if(text) textGeometry.setSize(pFont::size(font, text)); - //Windows XP and earlier do not support translucent images - //so we must blend with the button background color (which does not look great with XP gradient-button themes) - if(OsVersion() < WindowsVista) icon.alphaBlend(GetSysColor(COLOR_BTNFACE)); + Position position; + Size size; - hbitmap = CreateBitmap(icon); - himagelist = ImageList_Create(icon.width(), icon.height(), ILC_COLOR32, 1, 0); - ImageList_Add(himagelist, hbitmap, NULL); - BUTTON_IMAGELIST list; - list.himl = himagelist; - switch(state().orientation) { - case Orientation::Horizontal: SetRect(&list.margin, 5, 0, 0, 0); list.uAlign = BUTTON_IMAGELIST_ALIGN_LEFT; break; - case Orientation::Vertical: SetRect(&list.margin, 0, 5, 0, 0); list.uAlign = BUTTON_IMAGELIST_ALIGN_TOP; break; + switch(orientation) { + case Orientation::Horizontal: + size = {imageGeometry.width() + (image && text ? 5 : 0) + textGeometry.width(), max(imageGeometry.height(), textGeometry.height())}; + position = {(geometry.width() - size.width()) / 2, (geometry.height() - size.height()) / 2}; + imageGeometry.setPosition({position.x(), position.y() + (size.height() - imageGeometry.height()) / 2}); + textGeometry.setPosition({position.x() + size.width() - textGeometry.width(), position.y() + (size.height() - textGeometry.height()) / 2}); + break; + case Orientation::Vertical: + size = {max(imageGeometry.width(), textGeometry.width()), imageGeometry.height() + (image && text ? 5 : 0) + textGeometry.height()}; + position = {(geometry.width() - size.width()) / 2, (geometry.height() - size.height()) / 2}; + imageGeometry.setPosition({position.x() + (size.width() - imageGeometry.width()) / 2, position.y()}); + textGeometry.setPosition({position.x() + (size.width() - textGeometry.width()) / 2, position.y() + size.height() - textGeometry.height()}); + break; } - Button_SetImageList(hwnd, &list); - if(auto text = state().text) { - //text will not show up if BS_BITMAP is set - SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) &~ BS_BITMAP); - SetWindowText(hwnd, utf16_t(text)); - } else { - //bitmaps will not show up if text is empty - SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) | BS_BITMAP); - SetWindowText(hwnd, L""); + HDC hdcSource = CreateCompatibleDC(ps.hdc); + DrawThemeParentBackground(hwnd, ps.hdc, &rc); + + if(state) { + if(auto theme = OpenThemeData(hwnd, L"BUTTON")) { + DrawThemeBackground(theme, ps.hdc, BP_PUSHBUTTON, state, &rc, &ps.rcPaint); + CloseThemeData(theme); + } } + + if(GetFocus() == hwnd) { + signed offset = state ? 4 : 1; + RECT rcFocus{rc.left + offset, rc.top + offset, rc.right - offset, rc.bottom - offset}; + if(!state || state == PBS_NORMAL) DrawFocusRect(ps.hdc, &rcFocus); + } + + if(image) { + auto bitmap = CreateBitmap(image); + SelectBitmap(hdcSource, bitmap); + BLENDFUNCTION blend{AC_SRC_OVER, 0, (BYTE)(IsWindowEnabled(hwnd) ? 255 : 128), AC_SRC_ALPHA}; + AlphaBlend( + ps.hdc, imageGeometry.x(), imageGeometry.y(), image.width(), image.height(), + hdcSource, 0, 0, image.width(), image.height(), blend + ); + DeleteObject(bitmap); + } + + if(text) { + utf16_t wText(text); + SetBkMode(ps.hdc, TRANSPARENT); + SetTextColor(ps.hdc, GetSysColor(IsWindowEnabled(hwnd) ? COLOR_BTNTEXT : COLOR_GRAYTEXT)); + auto hFont = pFont::create(font); + SelectObject(ps.hdc, hFont); + RECT rcText{textGeometry.x(), textGeometry.y(), textGeometry.x() + textGeometry.width(), textGeometry.y() + textGeometry.height()}; + DrawText(ps.hdc, wText, -1, &rcText, DT_NOPREFIX | DT_END_ELLIPSIS); + DeleteObject(hFont); + } + + DeleteDC(hdcSource); } } diff --git a/hiro/windows/widget/button.hpp b/hiro/windows/widget/button.hpp index 12ee854f..3ff16470 100644 --- a/hiro/windows/widget/button.hpp +++ b/hiro/windows/widget/button.hpp @@ -7,16 +7,18 @@ struct pButton : pWidget { auto minimumSize() const -> Size override; auto setBordered(bool bordered) -> void; - auto setIcon(const image& icon) -> void; + auto setEnabled(bool enabled) -> void override; + auto setFont(const Font& font) -> void override; + auto setImage(const Image& image) -> void; auto setOrientation(Orientation orientation) -> void; auto setText(const string& text) -> void; + auto setVisible(bool visible) -> void override; auto onActivate() -> void; auto _setState() -> void; - HBITMAP hbitmap; - HIMAGELIST himagelist; + WindowProc windowProc = nullptr; }; } diff --git a/hiro/windows/widget/canvas.cpp b/hiro/windows/widget/canvas.cpp index b30dd9f9..6118e6e4 100644 --- a/hiro/windows/widget/canvas.cpp +++ b/hiro/windows/widget/canvas.cpp @@ -69,16 +69,11 @@ auto pCanvas::destruct() -> void { } auto pCanvas::minimumSize() const -> Size { - return {max(0, state().size.width()), max(0, state().size.height())}; + if(auto& image = state().image) return image.size(); + return {0, 0}; } auto pCanvas::setColor(Color color) -> void { - mode = Mode::Color; - update(); -} - -auto pCanvas::setData(Size size) -> void { - mode = Mode::Data; update(); } @@ -91,13 +86,11 @@ auto pCanvas::setGeometry(Geometry geometry) -> void { update(); } -auto pCanvas::setGradient(Color topLeft, Color topRight, Color bottomLeft, Color bottomRight) -> void { - mode = Mode::Gradient; +auto pCanvas::setGradient(Gradient gradient) -> void { update(); } -auto pCanvas::setIcon(const image& icon) -> void { - mode = Mode::Icon; +auto pCanvas::setImage(const Image& image) -> void { update(); } @@ -121,8 +114,18 @@ auto pCanvas::_paint() -> void { bmi.bmiHeader.biHeight = -height; //GDI stores bitmaps upside now; negative height flips bitmap bmi.bmiHeader.biSizeImage = pixels.size() * sizeof(uint32_t); void* bits = nullptr; - HBITMAP bitmap = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, &bits, NULL, 0); - if(bits) memory::copy(bits, pixels.data(), pixels.size() * sizeof(uint32_t)); + HBITMAP bitmap = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, &bits, nullptr, 0); + if(bits) { + auto source = (const uint8_t*)pixels.data(); + auto target = (uint8_t*)bits; + for(auto n : range(width * height)) { + target[0] = (source[0] * source[3]) / 255; + target[1] = (source[1] * source[3]) / 255; + target[2] = (source[2] * source[3]) / 255; + target[3] = (source[3]); + source += 4, target += 4; + } + } SelectObject(hdc, bitmap); RECT rc; @@ -139,45 +142,29 @@ auto pCanvas::_paint() -> void { } auto pCanvas::_rasterize() -> void { - if(mode == Mode::Color || mode == Mode::Gradient) { + if(auto& image = state().image) { + width = image.width(); + height = image.height(); + } else { width = self().geometry().width(); height = self().geometry().height(); - } else { - width = state().size.width(); - height = state().size.height(); } if(width <= 0 || height <= 0) return; pixels.reallocate(width * height); - if(mode == Mode::Color) { + if(auto& image = state().image) { + memory::copy(pixels.data(), image.data(), width * height * sizeof(uint32_t)); + } else if(auto& gradient = state().gradient) { + auto& colors = gradient.state.colors; + nall::image fill; + fill.allocate(width, height); + fill.gradient(colors[0].value(), colors[1].value(), colors[2].value(), colors[3].value()); + memory::copy(pixels.data(), fill.data(), fill.size()); + } else { uint32_t color = state().color.value(); for(auto& pixel : pixels) pixel = color; } - - if(mode == Mode::Gradient) { - image fill; - fill.allocate(width, height); - fill.gradient( - state().gradient[0].value(), state().gradient[1].value(), - state().gradient[2].value(), state().gradient[3].value() - ); - memory::copy(pixels.data(), fill.data(), fill.size()); - } - - if(mode == Mode::Icon) { - auto icon = state().icon; - icon.scale(width, height); - icon.transform(); - memory::copy(pixels.data(), icon.data(), icon.size()); - } - - if(mode == Mode::Data) { - memory::copy( - pixels.data(), pixels.size() * sizeof(uint32_t), - state().data.data(), state().data.size() * sizeof(uint32_t) - ); - } } auto pCanvas::_redraw() -> void { diff --git a/hiro/windows/widget/canvas.hpp b/hiro/windows/widget/canvas.hpp index f66bb8be..a76f5cfb 100644 --- a/hiro/windows/widget/canvas.hpp +++ b/hiro/windows/widget/canvas.hpp @@ -7,20 +7,16 @@ struct pCanvas : pWidget { auto minimumSize() const -> Size override; auto setColor(Color color) -> void; - auto setData(Size size) -> void; auto setDroppable(bool droppable) -> void; auto setGeometry(Geometry geometry) -> void override; - auto setGradient(Color topLeft, Color topRight, Color bottomLeft, Color bottomRight) -> void; - auto setIcon(const image& icon) -> void; + auto setGradient(Gradient gradient) -> void; + auto setImage(const Image& image) -> void; auto update() -> void; - enum class Mode : unsigned { Color, Data, Gradient, Icon }; - auto _paint() -> void; auto _rasterize() -> void; auto _redraw() -> void; - Mode mode = Mode::Color; vector pixels; signed width = 0; signed height = 0; diff --git a/hiro/windows/widget/check-button.cpp b/hiro/windows/widget/check-button.cpp index badd781e..7da36753 100644 --- a/hiro/windows/widget/check-button.cpp +++ b/hiro/windows/widget/check-button.cpp @@ -2,47 +2,73 @@ namespace hiro { +static auto CALLBACK CheckButton_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> LRESULT { + if(auto object = (mObject*)GetWindowLongPtr(hwnd, GWLP_USERDATA)) { + if(auto button = dynamic_cast(object)) { + if(auto self = button->self()) { + if(msg == WM_ERASEBKGND) return DefWindowProc(hwnd, msg, wparam, lparam); + if(msg == WM_PAINT) return Button_paintProc(hwnd, msg, wparam, lparam, + button->state.bordered, button->state.checked, button->enabled(true), button->font(true), + button->state.image, button->state.orientation, button->state.text + ); + return self->windowProc(hwnd, msg, wparam, lparam); + } + } + } + return DefWindowProc(hwnd, msg, wparam, lparam); +} + auto pCheckButton::construct() -> void { hwnd = CreateWindow(L"BUTTON", L"", WS_CHILD | WS_TABSTOP | BS_CHECKBOX | BS_PUSHLIKE, 0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0); SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference); + windowProc = (WindowProc)GetWindowLongPtr(hwnd, GWLP_WNDPROC); + SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)CheckButton_windowProc); pWidget::_setState(); _setState(); - setBordered(state().bordered); setChecked(state().checked); } auto pCheckButton::destruct() -> void { - if(hbitmap) { DeleteObject(hbitmap); hbitmap = 0; } - if(himagelist) { ImageList_Destroy(himagelist); himagelist = 0; } DestroyWindow(hwnd); } auto pCheckButton::minimumSize() const -> Size { - auto size = pFont::size(hfont, state().text); - + Size image = state().image.size(); + Size text = state().text ? pFont::size(self().font(true), state().text) : Size{}; + Size size; if(state().orientation == Orientation::Horizontal) { - size.setWidth(size.width() + state().icon.width()); - size.setHeight(max(size.height(), state().icon.height())); + size.setWidth(image.width() + (image && text ? 5 : 0) + text.width()); + size.setHeight(max(image.height(), text.height())); } - if(state().orientation == Orientation::Vertical) { - size.setWidth(max(size.width(), state().icon.width())); - size.setHeight(size.height() + state().icon.height()); + size.setWidth(max(image.width(), text.width())); + size.setHeight(image.height() + (image && text ? 5 : 0) + text.height()); } - - return {size.width() + (state().text ? 20 : 10), size.height() + 10}; + size.setHeight(max(size.height(), pFont::size(self().font(true), " ").height())); + return {size.width() + (state().bordered && text ? 20 : 10), size.height() + 10}; } auto pCheckButton::setBordered(bool bordered) -> void { + _setState(); } auto pCheckButton::setChecked(bool checked) -> void { SendMessage(hwnd, BM_SETCHECK, (WPARAM)checked, 0); } -auto pCheckButton::setIcon(const image& icon) -> void { +auto pCheckButton::setEnabled(bool enabled) -> void { + pWidget::setEnabled(enabled); + _setState(); +} + +auto pCheckButton::setFont(const Font& font) -> void { + pWidget::setFont(font); + _setState(); +} + +auto pCheckButton::setImage(const Image& image) -> void { _setState(); } @@ -54,6 +80,11 @@ auto pCheckButton::setText(const string& text) -> void { _setState(); } +auto pCheckButton::setVisible(bool visible) -> void { + pWidget::setVisible(visible); + _setState(); +} + auto pCheckButton::onToggle() -> void { state().checked = !state().checked; setChecked(state().checked); @@ -61,32 +92,7 @@ auto pCheckButton::onToggle() -> void { } auto pCheckButton::_setState() -> void { - image icon = state().icon; - icon.transform(); - - if(hbitmap) { DeleteObject(hbitmap); hbitmap = nullptr; } - if(himagelist) { ImageList_Destroy(himagelist); himagelist = nullptr; } - - if(OsVersion() < WindowsVista) icon.alphaBlend(GetSysColor(COLOR_BTNFACE)); - - hbitmap = CreateBitmap(icon); - himagelist = ImageList_Create(icon.width(), icon.height(), ILC_COLOR32, 1, 0); - ImageList_Add(himagelist, hbitmap, nullptr); - BUTTON_IMAGELIST list; - list.himl = himagelist; - switch(state().orientation) { - case Orientation::Horizontal: SetRect(&list.margin, 5, 0, 0, 0); list.uAlign = BUTTON_IMAGELIST_ALIGN_LEFT; break; - case Orientation::Vertical: SetRect(&list.margin, 0, 5, 0, 0); list.uAlign = BUTTON_IMAGELIST_ALIGN_TOP; break; - } - Button_SetImageList(hwnd, &list); - - if(auto text = state().text) { - SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) &~ BS_BITMAP); - SetWindowText(hwnd, utf16_t(text)); - } else { - SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) | BS_BITMAP); - SetWindowText(hwnd, L""); - } + InvalidateRect(hwnd, 0, false); } } diff --git a/hiro/windows/widget/check-button.hpp b/hiro/windows/widget/check-button.hpp index 3083cb2d..62ab9a82 100644 --- a/hiro/windows/widget/check-button.hpp +++ b/hiro/windows/widget/check-button.hpp @@ -7,17 +7,19 @@ struct pCheckButton : pWidget { auto minimumSize() const -> Size override; auto setBordered(bool bordered) -> void; + auto setEnabled(bool enabled) -> void override; + auto setFont(const Font& font) -> void override; auto setChecked(bool checked) -> void; - auto setIcon(const image& icon) -> void; + auto setImage(const Image& image) -> void; auto setOrientation(Orientation orientation) -> void; auto setText(const string& text) -> void; + auto setVisible(bool visible) -> void override; auto onToggle() -> void; auto _setState() -> void; - HBITMAP hbitmap = 0; - HIMAGELIST himagelist = 0; + WindowProc windowProc = nullptr; }; } diff --git a/hiro/windows/widget/check-label.cpp b/hiro/windows/widget/check-label.cpp index d263c7bb..7aa12508 100644 --- a/hiro/windows/widget/check-label.cpp +++ b/hiro/windows/widget/check-label.cpp @@ -19,7 +19,7 @@ auto pCheckLabel::destruct() -> void { } auto pCheckLabel::minimumSize() const -> Size { - auto size = pFont::size(hfont, state().text); + auto size = pFont::size(self().font(true), state().text ? state().text : " "); return {size.width() + 20, size.height() + 4}; } diff --git a/hiro/windows/widget/combo-button-item.cpp b/hiro/windows/widget/combo-button-item.cpp index 0b8b1a0e..dc802202 100644 --- a/hiro/windows/widget/combo-button-item.cpp +++ b/hiro/windows/widget/combo-button-item.cpp @@ -8,7 +8,7 @@ auto pComboButtonItem::construct() -> void { auto pComboButtonItem::destruct() -> void { } -auto pComboButtonItem::setIcon(const image& icon) -> void { +auto pComboButtonItem::setImage(const Image& image) -> void { //unsupported } diff --git a/hiro/windows/widget/combo-button-item.hpp b/hiro/windows/widget/combo-button-item.hpp index 335e489a..045996ab 100644 --- a/hiro/windows/widget/combo-button-item.hpp +++ b/hiro/windows/widget/combo-button-item.hpp @@ -5,7 +5,7 @@ namespace hiro { struct pComboButtonItem : pObject { Declare(ComboButtonItem, Object) - auto setIcon(const image& icon) -> void; + auto setImage(const Image& image) -> void; auto setSelected() -> void; auto setText(const string& text) -> void; diff --git a/hiro/windows/widget/line-edit.cpp b/hiro/windows/widget/line-edit.cpp index 5dd03e13..81311ee0 100644 --- a/hiro/windows/widget/line-edit.cpp +++ b/hiro/windows/widget/line-edit.cpp @@ -21,7 +21,7 @@ auto pLineEdit::destruct() -> void { } auto pLineEdit::minimumSize() const -> Size { - auto size = pFont::size(hfont, state().text); + auto size = pFont::size(hfont, state().text ? state().text : " "); return {size.width() + 12, size.height() + 10}; } diff --git a/hiro/windows/widget/list-view-cell.cpp b/hiro/windows/widget/list-view-cell.cpp index 0b912aae..f37b62b6 100644 --- a/hiro/windows/widget/list-view-cell.cpp +++ b/hiro/windows/widget/list-view-cell.cpp @@ -27,7 +27,7 @@ auto pListViewCell::setForegroundColor(Color color) -> void { _repaint(); } -auto pListViewCell::setIcon(const image& icon) -> void { +auto pListViewCell::setImage(const Image& icon) -> void { _repaint(); } diff --git a/hiro/windows/widget/list-view-cell.hpp b/hiro/windows/widget/list-view-cell.hpp index d5c62923..4cc0e0d0 100644 --- a/hiro/windows/widget/list-view-cell.hpp +++ b/hiro/windows/widget/list-view-cell.hpp @@ -10,7 +10,7 @@ struct pListViewCell : pObject { auto setCheckable(bool checkable) -> void; auto setChecked(bool checked) -> void; auto setForegroundColor(Color color) -> void; - auto setIcon(const image& icon) -> void; + auto setImage(const Image& image) -> void; auto setText(const string& text) -> void; auto _parent() -> maybe; diff --git a/hiro/windows/widget/list-view-column.cpp b/hiro/windows/widget/list-view-column.cpp index ef2871b3..06a1b26c 100644 --- a/hiro/windows/widget/list-view-column.cpp +++ b/hiro/windows/widget/list-view-column.cpp @@ -49,7 +49,7 @@ auto pListViewColumn::setHorizontalAlignment(double alignment) -> void { _setState(); } -auto pListViewColumn::setIcon(const image& icon) -> void { +auto pListViewColumn::setImage(const Image& image) -> void { _setState(); } @@ -97,7 +97,7 @@ auto pListViewColumn::_setState() -> void { lvColumn.cx = _width; if(state().horizontalAlignment < 0.333) lvColumn.fmt = LVCFMT_LEFT; if(state().horizontalAlignment > 0.666) lvColumn.fmt = LVCFMT_RIGHT; - if(state().icon) lvColumn.mask |= LVCF_IMAGE; + if(state().image) lvColumn.mask |= LVCF_IMAGE; if(!state().resizable) lvColumn.fmt |= LVCFMT_FIXED_WIDTH; ListView_SetColumn(grandparent->hwnd, self().offset(), &lvColumn); grandparent->unlock(); diff --git a/hiro/windows/widget/list-view-column.hpp b/hiro/windows/widget/list-view-column.hpp index 2c00c654..bf13aeac 100644 --- a/hiro/windows/widget/list-view-column.hpp +++ b/hiro/windows/widget/list-view-column.hpp @@ -12,7 +12,7 @@ struct pListViewColumn : pObject { auto setExpandable(bool expandable) -> void; auto setForegroundColor(Color color) -> void; auto setHorizontalAlignment(double alignment) -> void; - auto setIcon(const image& icon) -> void; + auto setImage(const Image& image) -> void; auto setResizable(bool resizable) -> void; auto setSortable(bool sortable) -> void; auto setText(const string& text) -> void; diff --git a/hiro/windows/widget/list-view.cpp b/hiro/windows/widget/list-view.cpp index a9bc6de3..4d573bed 100644 --- a/hiro/windows/widget/list-view.cpp +++ b/hiro/windows/widget/list-view.cpp @@ -219,13 +219,11 @@ auto pListView::onCustomDraw(LPARAM lparam) -> LRESULT { rc.left += 2; } - if(auto icon = cell->state.icon) { - icon.scale(iconSize, iconSize); - icon.transform(); - auto bitmap = CreateBitmap(icon); + if(auto& image = cell->state.image) { + auto bitmap = CreateBitmap(image); SelectBitmap(hdcSource, bitmap); BLENDFUNCTION blend{AC_SRC_OVER, 0, (BYTE)(selected ? 128 : 255), AC_SRC_ALPHA}; - AlphaBlend(hdc, rc.left, rc.top, iconSize, iconSize, hdcSource, 0, 0, iconSize, iconSize, blend); + AlphaBlend(hdc, rc.left, rc.top, iconSize, iconSize, hdcSource, 0, 0, image.width(), image.height(), blend); DeleteObject(bitmap); rc.left += iconSize + 2; } @@ -325,11 +323,11 @@ auto pListView::_cellWidth(unsigned _row, unsigned _column) -> unsigned { if(cell->state.checkable) { width += 16 + 2; } - if(auto& icon = cell->state.icon) { + if(auto& image = cell->state.image) { width += 16 + 2; } if(auto& text = cell->state.text) { - width += Font::size(_font(_row, _column), text).width(); + width += pFont::size(_font(_row, _column), text).width(); } } } @@ -340,18 +338,18 @@ auto pListView::_columnWidth(unsigned _column) -> unsigned { unsigned width = 12; if(auto header = state().header) { if(auto column = header->column(_column)) { - if(auto& icon = column->state.icon) { + if(auto& image = column->state.image) { width += 16 + 12; //yes; icon spacing in column headers is excessive } if(auto& text = column->state.text) { - width += Font::size(self().font(true), text).width(); + width += pFont::size(self().font(true), text).width(); } } } return width; } -auto pListView::_font(unsigned _row, unsigned _column) -> string { +auto pListView::_font(unsigned _row, unsigned _column) -> Font { if(auto item = self().item(_row)) { if(auto cell = item->cell(_column)) { if(auto font = cell->font()) return font; @@ -362,7 +360,7 @@ auto pListView::_font(unsigned _row, unsigned _column) -> string { // if(auto font = column->font()) return font; // } if(auto font = self().font(true)) return font; - return Font::sans(8); + return {}; } auto pListView::_foregroundColor(unsigned _row, unsigned _column) -> Color { @@ -387,10 +385,11 @@ auto pListView::_setIcons() -> void { if(auto& header = state().header) { for(auto column : range(header->columnCount())) { - auto icon = header->state.columns[column]->state.icon; - if(icon) { + nall::image icon; + if(auto& image = header->state.columns[column]->state.image) { + icon.allocate(image.width(), image.height()); + memory::copy(icon.data(), image.data(), icon.size()); icon.scale(16, 16); - icon.transform(); } else { icon.allocate(16, 16); icon.fill(0x00ffffff); @@ -402,7 +401,7 @@ auto pListView::_setIcons() -> void { } //empty icon used for ListViewItems (drawn manually via onCustomDraw) - image icon; + nall::image icon; icon.allocate(16, 16); icon.fill(0x00ffffff); auto bitmap = CreateBitmap(icon); diff --git a/hiro/windows/widget/list-view.hpp b/hiro/windows/widget/list-view.hpp index 4aff0796..3e3af014 100644 --- a/hiro/windows/widget/list-view.hpp +++ b/hiro/windows/widget/list-view.hpp @@ -27,7 +27,7 @@ struct pListView : pWidget { auto _backgroundColor(unsigned row, unsigned column) -> Color; auto _cellWidth(unsigned row, unsigned column) -> unsigned; auto _columnWidth(unsigned column) -> unsigned; - auto _font(unsigned row, unsigned column) -> string; + auto _font(unsigned row, unsigned column) -> Font; auto _foregroundColor(unsigned row, unsigned column) -> Color; auto _setIcons() -> void; auto _setSortable() -> void; diff --git a/hiro/windows/widget/radio-button.cpp b/hiro/windows/widget/radio-button.cpp index 06751286..fabba761 100644 --- a/hiro/windows/widget/radio-button.cpp +++ b/hiro/windows/widget/radio-button.cpp @@ -2,44 +2,60 @@ namespace hiro { +static auto CALLBACK RadioButton_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> LRESULT { + if(auto object = (mObject*)GetWindowLongPtr(hwnd, GWLP_USERDATA)) { + if(auto button = dynamic_cast(object)) { + if(auto self = button->self()) { + if(msg == WM_ERASEBKGND) return DefWindowProc(hwnd, msg, wparam, lparam); + if(msg == WM_PAINT) return Button_paintProc(hwnd, msg, wparam, lparam, + button->state.bordered, button->state.checked, button->enabled(true), button->font(true), + button->state.image, button->state.orientation, button->state.text + ); + return self->windowProc(hwnd, msg, wparam, lparam); + } + } + } + return DefWindowProc(hwnd, msg, wparam, lparam); +} + auto pRadioButton::construct() -> void { hwnd = CreateWindow(L"BUTTON", L"", WS_CHILD | WS_TABSTOP | BS_CHECKBOX | BS_PUSHLIKE, 0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0); SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference); + windowProc = (WindowProc)GetWindowLongPtr(hwnd, GWLP_WNDPROC); + SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)RadioButton_windowProc); pWidget::_setState(); + setGroup(state().group); _setState(); - setBordered(state().bordered); - if(state().checked) setChecked(); } auto pRadioButton::destruct() -> void { - if(hbitmap) { DeleteObject(hbitmap); hbitmap = 0; } - if(himagelist) { ImageList_Destroy(himagelist); himagelist = 0; } DestroyWindow(hwnd); } auto pRadioButton::minimumSize() const -> Size { - auto size = pFont::size(hfont, state().text); - + Size image = state().image.size(); + Size text = state().text ? pFont::size(self().font(true), state().text) : Size{}; + Size size; if(state().orientation == Orientation::Horizontal) { - size.setWidth(size.width() + state().icon.width()); - size.setHeight(max(size.height(), state().icon.height())); + size.setWidth(image.width() + (image && text ? 5 : 0) + text.width()); + size.setHeight(max(image.height(), text.height())); } - if(state().orientation == Orientation::Vertical) { - size.setWidth(max(size.width(), state().icon.width())); - size.setHeight(size.height() + state().icon.height()); + size.setWidth(max(image.width(), text.width())); + size.setHeight(image.height() + (image && text ? 5 : 0) + text.height()); } - - return {size.width() + (state().text ? 20 : 10), size.height() + 10}; + size.setHeight(max(size.height(), pFont::size(self().font(true), " ").height())); + return {size.width() + (state().bordered && text ? 20 : 10), size.height() + 10}; } auto pRadioButton::setBordered(bool bordered) -> void { + _setState(); } auto pRadioButton::setChecked() -> void { - if(auto group = self().group()) { + if(auto& group = state().group) { for(auto& weak : group->state.objects) { if(auto object = weak.acquire()) { if(auto radioButton = dynamic_cast(object.data())) { @@ -52,10 +68,33 @@ auto pRadioButton::setChecked() -> void { } } -auto pRadioButton::setGroup(sGroup group) -> void { +auto pRadioButton::setEnabled(bool enabled) -> void { + pWidget::setEnabled(enabled); + _setState(); } -auto pRadioButton::setIcon(const image& icon) -> void { +auto pRadioButton::setFont(const Font& font) -> void { + pWidget::setFont(font); + _setState(); +} + +auto pRadioButton::setGroup(sGroup group) -> void { + bool first = true; + if(auto& group = state().group) { + for(auto& weak : group->state.objects) { + if(auto object = weak.acquire()) { + if(auto radioButton = dynamic_cast(object.data())) { + if(auto self = radioButton->self()) { + SendMessage(self->hwnd, BM_SETCHECK, (WPARAM)(radioButton->state.checked = first), 0); + first = false; + } + } + } + } + } +} + +auto pRadioButton::setImage(const Image& image) -> void { _setState(); } @@ -63,43 +102,23 @@ auto pRadioButton::setOrientation(Orientation orientation) -> void { _setState(); } -void pRadioButton::setText(const string& text) { +auto pRadioButton::setText(const string& text) -> void { _setState(); } -void pRadioButton::onActivate() { +auto pRadioButton::setVisible(bool visible) -> void { + pWidget::setVisible(visible); + _setState(); +} + +auto pRadioButton::onActivate() -> void { if(state().checked) return; self().setChecked(); self().doActivate(); } auto pRadioButton::_setState() -> void { - image icon = state().icon; - icon.transform(); - - if(hbitmap) { DeleteObject(hbitmap); hbitmap = nullptr; } - if(himagelist) { ImageList_Destroy(himagelist); himagelist = nullptr; } - - if(OsVersion() < WindowsVista) icon.alphaBlend(GetSysColor(COLOR_BTNFACE)); - - hbitmap = CreateBitmap(icon); - himagelist = ImageList_Create(icon.width(), icon.height(), ILC_COLOR32, 1, 0); - ImageList_Add(himagelist, hbitmap, nullptr); - BUTTON_IMAGELIST list; - list.himl = himagelist; - switch(state().orientation) { - case Orientation::Horizontal: SetRect(&list.margin, 5, 0, 0, 0); list.uAlign = BUTTON_IMAGELIST_ALIGN_LEFT; break; - case Orientation::Vertical: SetRect(&list.margin, 0, 5, 0, 0); list.uAlign = BUTTON_IMAGELIST_ALIGN_TOP; break; - } - Button_SetImageList(hwnd, &list); - - if(auto text = state().text) { - SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) &~ BS_BITMAP); - SetWindowText(hwnd, utf16_t(text)); - } else { - SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) | BS_BITMAP); - SetWindowText(hwnd, L""); - } + InvalidateRect(hwnd, 0, false); } } diff --git a/hiro/windows/widget/radio-button.hpp b/hiro/windows/widget/radio-button.hpp index 31f4d917..2a94db2f 100644 --- a/hiro/windows/widget/radio-button.hpp +++ b/hiro/windows/widget/radio-button.hpp @@ -8,17 +8,19 @@ struct pRadioButton : pWidget { auto minimumSize() const -> Size override; auto setBordered(bool bordered) -> void; auto setChecked() -> void; + auto setEnabled(bool enabled) -> void override; + auto setFont(const Font& font) -> void override; auto setGroup(sGroup group) -> void override; - auto setIcon(const image& icon) -> void; + auto setImage(const Image& image) -> void; auto setOrientation(Orientation orientation) -> void; auto setText(const string& text) -> void; + auto setVisible(bool visible) -> void override; auto onActivate() -> void; auto _setState() -> void; - HBITMAP hbitmap = 0; - HIMAGELIST himagelist = 0; + WindowProc windowProc = nullptr; }; } diff --git a/hiro/windows/widget/radio-label.cpp b/hiro/windows/widget/radio-label.cpp index 01231102..90db50b7 100644 --- a/hiro/windows/widget/radio-label.cpp +++ b/hiro/windows/widget/radio-label.cpp @@ -4,13 +4,12 @@ namespace hiro { auto pRadioLabel::construct() -> void { hwnd = CreateWindow( - L"BUTTON", L"", - WS_CHILD | WS_TABSTOP | BS_RADIOBUTTON, + L"BUTTON", L"", WS_CHILD | WS_TABSTOP | BS_RADIOBUTTON, 0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0 ); SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference); pWidget::_setState(); - if(state().checked) setChecked(); + setGroup(state().group); setText(state().text); } @@ -18,13 +17,13 @@ auto pRadioLabel::destruct() -> void { DestroyWindow(hwnd); } -auto pRadioLabel::minimumSize() -> Size { - auto size = pFont::size(hfont, state().text); +auto pRadioLabel::minimumSize() const -> Size { + auto size = pFont::size(self().font(true), state().text ? state().text : " "); return {size.width() + 20, size.height() + 4}; } auto pRadioLabel::setChecked() -> void { - if(auto group = self().group()) { + if(auto& group = state().group) { for(auto& weak : group->state.objects) { if(auto object = weak.acquire()) { if(auto radioLabel = dynamic_cast(object.data())) { @@ -38,6 +37,19 @@ auto pRadioLabel::setChecked() -> void { } auto pRadioLabel::setGroup(sGroup group) -> void { + bool first = true; + if(auto& group = state().group) { + for(auto& weak : group->state.objects) { + if(auto object = weak.acquire()) { + if(auto radioLabel = dynamic_cast(object.data())) { + if(auto self = radioLabel->self()) { + SendMessage(self->hwnd, BM_SETCHECK, (WPARAM)(radioLabel->state.checked = first), 0); + first = false; + } + } + } + } + } } auto pRadioLabel::setText(const string& text) -> void { diff --git a/hiro/windows/widget/radio-label.hpp b/hiro/windows/widget/radio-label.hpp index 38b5b6f7..28f1b29d 100644 --- a/hiro/windows/widget/radio-label.hpp +++ b/hiro/windows/widget/radio-label.hpp @@ -5,7 +5,7 @@ namespace hiro { struct pRadioLabel : pWidget { Declare(RadioLabel, Widget) - auto minimumSize() -> Size; + auto minimumSize() const -> Size override; auto setChecked() -> void; auto setGroup(sGroup group) -> void override; auto setText(const string& text) -> void; diff --git a/hiro/windows/widget/tab-frame-item.cpp b/hiro/windows/widget/tab-frame-item.cpp index 2583d648..e9615d50 100644 --- a/hiro/windows/widget/tab-frame-item.cpp +++ b/hiro/windows/widget/tab-frame-item.cpp @@ -24,7 +24,7 @@ auto pTabFrameItem::setClosable(bool closable) -> void { //unsupported } -auto pTabFrameItem::setIcon(const image& icon) -> void { +auto pTabFrameItem::setImage(const Image& image) -> void { if(auto parent = _parent()) { parent->_buildImageList(); } diff --git a/hiro/windows/widget/tab-frame-item.hpp b/hiro/windows/widget/tab-frame-item.hpp index d08a2696..37bef59d 100644 --- a/hiro/windows/widget/tab-frame-item.hpp +++ b/hiro/windows/widget/tab-frame-item.hpp @@ -8,7 +8,7 @@ struct pTabFrameItem : pObject { auto append(sLayout layout) -> void; auto remove(sLayout layout) -> void; auto setClosable(bool closable) -> void; - auto setIcon(const image& icon) -> void; + auto setImage(const Image& image) -> void; auto setMovable(bool movable) -> void; auto setSelected() -> void; auto setText(const string& text) -> void; diff --git a/hiro/windows/widget/tab-frame.cpp b/hiro/windows/widget/tab-frame.cpp index 22efbc66..3c320e71 100644 --- a/hiro/windows/widget/tab-frame.cpp +++ b/hiro/windows/widget/tab-frame.cpp @@ -15,9 +15,10 @@ static auto CALLBACK TabFrame_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPA } auto pTabFrame::construct() -> void { - hwnd = CreateWindow(WC_TABCONTROL, L"", - WS_CHILD | WS_TABSTOP, - 0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0); + hwnd = CreateWindow( + WC_TABCONTROL, L"", WS_CHILD | WS_TABSTOP, + 0, 0, 0, 0, _parentHandle(), nullptr, GetModuleHandle(0), 0 + ); SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference); windowProc = (WindowProc)GetWindowLongPtr(hwnd, GWLP_WNDPROC); SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)TabFrame_windowProc); @@ -38,7 +39,7 @@ auto pTabFrame::append(sTabFrameItem item) -> void { TabCtrl_InsertItem(hwnd, item->offset(), &tcItem); if(auto self = item->self()) { self->setClosable(item->state.closable); - self->setIcon(item->state.icon); + self->setImage(item->state.image); self->setMovable(item->state.movable); self->setText(item->state.text); if(item->selected()) self->setSelected(); @@ -52,10 +53,6 @@ auto pTabFrame::remove(sTabFrameItem item) -> void { _buildImageList(); } -auto pTabFrame::setEdge(Edge edge) -> void { - //unsupported -} - auto pTabFrame::setEnabled(bool enabled) -> void { pWidget::setEnabled(enabled); for(auto& item : state().items) { @@ -78,6 +75,10 @@ auto pTabFrame::setGeometry(Geometry geometry) -> void { } } +auto pTabFrame::setNavigation(Navigation navigation) -> void { + //unsupported +} + auto pTabFrame::setVisible(bool visible) -> void { pWidget::setVisible(visible); for(auto& item : state().items) { @@ -93,13 +94,13 @@ auto pTabFrame::_buildImageList() -> void { if(imageList) { ImageList_Destroy(imageList); imageList = nullptr; } imageList = ImageList_Create(size, size, ILC_COLOR32, 1, 0); for(auto& item : state().items) { - ImageList_Append(imageList, item->state.icon, size); + ImageList_Append(imageList, item->state.image, size); } TabCtrl_SetImageList(hwnd, imageList); for(auto offset : range(state().items)) { TCITEM tcItem; tcItem.mask = TCIF_IMAGE; - tcItem.iImage = state().items[offset]->state.icon ? offset : -1; + tcItem.iImage = state().items[offset]->state.image ? offset : -1; TabCtrl_SetItem(hwnd, offset, &tcItem); } } diff --git a/hiro/windows/widget/tab-frame.hpp b/hiro/windows/widget/tab-frame.hpp index cfbd0d8a..e2356a6b 100644 --- a/hiro/windows/widget/tab-frame.hpp +++ b/hiro/windows/widget/tab-frame.hpp @@ -7,9 +7,9 @@ struct pTabFrame : pWidget { auto append(sTabFrameItem item) -> void; auto remove(sTabFrameItem item) -> void; - auto setEdge(Edge edge) -> void; auto setEnabled(bool enabled) -> void override; auto setGeometry(Geometry geometry) -> void override; + auto setNavigation(Navigation navigation) -> void; auto setVisible(bool visible) -> void override; auto onChange() -> void; diff --git a/hiro/windows/widget/text-edit.cpp b/hiro/windows/widget/text-edit.cpp index 81255b06..2ddb830d 100644 --- a/hiro/windows/widget/text-edit.cpp +++ b/hiro/windows/widget/text-edit.cpp @@ -11,9 +11,9 @@ auto pTextEdit::construct() -> void { SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference); pWidget::_setState(); setBackgroundColor(state().backgroundColor); - setCursorPosition(state().cursorPosition); setEditable(state().editable); setText(state().text); + setCursor(state().cursor); } auto pTextEdit::destruct() -> void { @@ -27,9 +27,11 @@ auto pTextEdit::setBackgroundColor(Color color) -> void { backgroundBrush = CreateSolidBrush(color ? CreateRGB(color) : GetSysColor(COLOR_WINDOW)); } -auto pTextEdit::setCursorPosition(unsigned position) -> void { - if(position == ~0) position >>= 1; //Edit_SetSel takes signed type - Edit_SetSel(hwnd, position, position); +auto pTextEdit::setCursor(Cursor cursor) -> void { + signed end = GetWindowTextLength(hwnd); + signed offset = max(0, min(end, cursor.offset())); + signed length = max(0, min(end, cursor.offset() + cursor.length())); + Edit_SetSel(hwnd, offset, length); Edit_ScrollCaret(hwnd); } diff --git a/hiro/windows/widget/text-edit.hpp b/hiro/windows/widget/text-edit.hpp index 061d12af..e3605475 100644 --- a/hiro/windows/widget/text-edit.hpp +++ b/hiro/windows/widget/text-edit.hpp @@ -6,7 +6,7 @@ struct pTextEdit : pWidget { Declare(TextEdit, Widget) auto setBackgroundColor(Color color) -> void; - auto setCursorPosition(unsigned position) -> void; + auto setCursor(Cursor cursor) -> void; auto setEditable(bool editable) -> void; auto setForegroundColor(Color color) -> void; auto setText(string text) -> void; diff --git a/hiro/windows/widget/widget.cpp b/hiro/windows/widget/widget.cpp index 2d2a8afb..5edde98b 100644 --- a/hiro/windows/widget/widget.cpp +++ b/hiro/windows/widget/widget.cpp @@ -35,11 +35,9 @@ auto pWidget::setFocused() -> void { SetFocus(hwnd); } -auto pWidget::setFont(const string&) -> void { - auto font = self().font(true); - if(!font) font = Font::sans(8); +auto pWidget::setFont(const Font&) -> void { if(hfont) DeleteObject(hfont); - hfont = pFont::create(font); + hfont = pFont::create(self().font(true)); SendMessage(hwnd, WM_SETFONT, (WPARAM)hfont, 0); } diff --git a/hiro/windows/widget/widget.hpp b/hiro/windows/widget/widget.hpp index e8350116..2f2926a0 100644 --- a/hiro/windows/widget/widget.hpp +++ b/hiro/windows/widget/widget.hpp @@ -9,7 +9,7 @@ struct pWidget : pSizable { virtual auto minimumSize() -> Size; auto setEnabled(bool enabled) -> void override; auto setFocused() -> void; - auto setFont(const string& font) -> void override; + auto setFont(const Font& font) -> void override; virtual auto setGeometry(Geometry geometry) -> void; auto setVisible(bool visible) -> void override; diff --git a/hiro/windows/window.cpp b/hiro/windows/window.cpp index 92d49c2e..675c5385 100644 --- a/hiro/windows/window.cpp +++ b/hiro/windows/window.cpp @@ -82,7 +82,7 @@ auto pWindow::setFocused() -> void { SetFocus(hwnd); } -auto pWindow::setFont(const string& font) -> void { +auto pWindow::setFont(const Font& font) -> void { if(auto layout = state().layout) { if(auto self = layout->self()) self->setFont(layout->font(true)); } diff --git a/hiro/windows/window.hpp b/hiro/windows/window.hpp index 11f0dd39..e42397ad 100644 --- a/hiro/windows/window.hpp +++ b/hiro/windows/window.hpp @@ -17,7 +17,7 @@ struct pWindow : pObject { auto setDroppable(bool droppable) -> void; auto setEnabled(bool enabled) -> void; auto setFocused() -> void; - auto setFont(const string& font) -> void override; + auto setFont(const Font& font) -> void override; auto setFullScreen(bool fullScreen) -> void; auto setGeometry(Geometry geometry) -> void; auto setModal(bool modal) -> void; diff --git a/nall/image/base.hpp b/nall/image/base.hpp index e7c4c059..42244ee9 100644 --- a/nall/image/base.hpp +++ b/nall/image/base.hpp @@ -80,6 +80,7 @@ struct image { //utility.hpp inline auto crop(unsigned x, unsigned y, unsigned width, unsigned height) -> bool; inline auto alphaBlend(uint64_t alphaColor) -> void; + inline auto alphaMultiply() -> void; inline auto transform(const image& source = {}) -> void; inline auto transform(bool endian, unsigned depth, uint64_t alphaMask, uint64_t redMask, uint64_t greenMask, uint64_t blueMask) -> void; diff --git a/nall/image/utility.hpp b/nall/image/utility.hpp index e70bda2a..bb6e7454 100644 --- a/nall/image/utility.hpp +++ b/nall/image/utility.hpp @@ -56,6 +56,30 @@ auto image::alphaBlend(uint64_t alphaColor) -> void { } } +auto image::alphaMultiply() -> void { + unsigned divisor = (1 << _alpha.depth()) - 1; + + #pragma omp parallel for + for(unsigned y = 0; y < _height; y++) { + uint8_t* dp = _data + pitch() * y; + for(unsigned x = 0; x < _width; x++) { + uint64_t color = read(dp); + + uint64_t colorA = (color & _alpha.mask()) >> _alpha.shift(); + uint64_t colorR = (color & _red.mask() ) >> _red.shift(); + uint64_t colorG = (color & _green.mask()) >> _green.shift(); + uint64_t colorB = (color & _blue.mask() ) >> _blue.shift(); + + colorR = (colorR * colorA) / divisor; + colorG = (colorG * colorA) / divisor; + colorB = (colorB * colorA) / divisor; + + write(dp, (colorA << _alpha.shift()) | (colorR << _red.shift()) | (colorG << _green.shift()) | (colorB << _blue.shift())); + dp += stride(); + } + } +} + auto image::transform(const image& source) -> void { return transform(source._endian, source._depth, source._alpha.mask(), source._red.mask(), source._green.mask(), source._blue.mask()); } diff --git a/target-tomoko/presentation/presentation.cpp b/target-tomoko/presentation/presentation.cpp index 54e5312f..36b80d9d 100644 --- a/target-tomoko/presentation/presentation.cpp +++ b/target-tomoko/presentation/presentation.cpp @@ -97,7 +97,7 @@ Presentation::Presentation() { cheatEditor.setText("Cheat Editor").onActivate([&] { toolsManager->show(0); }); stateManager.setText("State Manager").onActivate([&] { toolsManager->show(1); }); - statusBar.setFont(Font::sans(8, "Bold")); + statusBar.setFont(Font().setBold()); statusBar.setVisible(config->userInterface.showStatusBar); onClose([&] { program->quit(); }); diff --git a/target-tomoko/program/program.cpp b/target-tomoko/program/program.cpp index 3d21d0ba..9617c496 100644 --- a/target-tomoko/program/program.cpp +++ b/target-tomoko/program/program.cpp @@ -68,8 +68,15 @@ Program::Program(lstring args) { updateVideoFilter(); - if(args.size() == 2 && directory::exists(args[1])) { - loadMedia(args[1]); + args.takeFirst(); //ignore program location in argument parsing + for(auto& argument : args) { + if(argument == "--fullscreen") { + presentation->toggleFullScreen(); + } else { + auto location = argument; + if(file::exists(location)) location = dirname(location); + if(directory::exists(location)) loadMedia(location); + } } } diff --git a/target-tomoko/settings/advanced.cpp b/target-tomoko/settings/advanced.cpp index 1a1a385f..1261d0db 100644 --- a/target-tomoko/settings/advanced.cpp +++ b/target-tomoko/settings/advanced.cpp @@ -1,10 +1,10 @@ AdvancedSettings::AdvancedSettings(TabFrame* parent) : TabFrameItem(parent) { - setIcon(Icon::Action::Settings); + setImage(Icon::Action::Settings); setText("Advanced"); layout.setMargin(5); - driverLabel.setText("Driver Selection").setFont(Font::sans(8, "Bold")); + driverLabel.setText("Driver Selection").setFont(Font().setBold()); videoLabel.setText("Video:"); videoDriver.onChange([&] { config->video.driver = videoDriver.selected()->text(); }); for(auto& driver : Video::availableDrivers()) { @@ -30,7 +30,7 @@ AdvancedSettings::AdvancedSettings(TabFrame* parent) : TabFrameItem(parent) { if(config->input.driver == driver) item.setSelected(); } - libraryLabel.setText("Game Library").setFont(Font::sans(8, "Bold")); + libraryLabel.setText("Game Library").setFont(Font().setBold()); libraryPrefix.setText("Location:"); libraryLocation.setEditable(false).setText(config->library.location); libraryChange.setText("Change ...").onActivate([&] { diff --git a/target-tomoko/settings/audio.cpp b/target-tomoko/settings/audio.cpp index fcb4989b..c75be097 100644 --- a/target-tomoko/settings/audio.cpp +++ b/target-tomoko/settings/audio.cpp @@ -1,5 +1,5 @@ AudioSettings::AudioSettings(TabFrame* parent) : TabFrameItem(parent) { - setIcon(Icon::Device::Speaker); + setImage(Icon::Device::Speaker); setText("Audio"); layout.setMargin(5); diff --git a/target-tomoko/settings/hotkeys.cpp b/target-tomoko/settings/hotkeys.cpp index 0e087bb5..0d809879 100644 --- a/target-tomoko/settings/hotkeys.cpp +++ b/target-tomoko/settings/hotkeys.cpp @@ -1,5 +1,5 @@ HotkeySettings::HotkeySettings(TabFrame* parent) : TabFrameItem(parent) { - setIcon(Icon::Device::Keyboard); + setImage(Icon::Device::Keyboard); setText("Hotkeys"); layout.setMargin(5); diff --git a/target-tomoko/settings/input.cpp b/target-tomoko/settings/input.cpp index df168b71..8c4a7f8a 100644 --- a/target-tomoko/settings/input.cpp +++ b/target-tomoko/settings/input.cpp @@ -1,5 +1,5 @@ InputSettings::InputSettings(TabFrame* parent) : TabFrameItem(parent) { - setIcon(Icon::Device::Joypad); + setImage(Icon::Device::Joypad); setText("Input"); layout.setMargin(5); diff --git a/target-tomoko/settings/settings.cpp b/target-tomoko/settings/settings.cpp index ad55c400..de987341 100644 --- a/target-tomoko/settings/settings.cpp +++ b/target-tomoko/settings/settings.cpp @@ -11,7 +11,7 @@ SettingsManager::SettingsManager() { settingsManager = this; layout.setMargin(5); - statusBar.setFont(Font::sans(8, "Bold")); + statusBar.setFont(Font().setBold()); setTitle("Configuration Settings"); setSize({600, 400}); diff --git a/target-tomoko/settings/timing.cpp b/target-tomoko/settings/timing.cpp index b46c49b1..3ef1e8e2 100644 --- a/target-tomoko/settings/timing.cpp +++ b/target-tomoko/settings/timing.cpp @@ -1,5 +1,5 @@ TimingSettings::TimingSettings(TabFrame* parent) : TabFrameItem(parent) { - setIcon(Icon::Device::Clock); + setImage(Icon::Device::Clock); setText("Timing"); layout.setMargin(5); diff --git a/target-tomoko/settings/video.cpp b/target-tomoko/settings/video.cpp index 13491a00..3c12b46e 100644 --- a/target-tomoko/settings/video.cpp +++ b/target-tomoko/settings/video.cpp @@ -1,10 +1,10 @@ VideoSettings::VideoSettings(TabFrame* parent) : TabFrameItem(parent) { - setIcon(Icon::Device::Display); + setImage(Icon::Device::Display); setText("Video"); layout.setMargin(5); - colorAdjustmentLabel.setFont(Font::sans(8, "Bold")).setText("Color Adjustment"); + colorAdjustmentLabel.setFont(Font().setBold()).setText("Color Adjustment"); saturationLabel.setText("Saturation:"); saturationSlider.setLength(201).setPosition(config->video.saturation).onChange([&] { update(); }); gammaLabel.setText("Gamma:"); @@ -12,7 +12,7 @@ VideoSettings::VideoSettings(TabFrame* parent) : TabFrameItem(parent) { luminanceLabel.setText("Luminance:"); luminanceSlider.setLength(101).setPosition(config->video.luminance).onChange([&] { update(); }); - overscanMaskLabel.setFont(Font::sans(8, "Bold")).setText("Overscan Mask"); + overscanMaskLabel.setFont(Font().setBold()).setText("Overscan Mask"); horizontalMaskLabel.setText("Horizontal:"); horizontalMaskSlider.setLength(17).setPosition(config->video.overscan.horizontal).onChange([&] { update(); }); verticalMaskLabel.setText("Vertical:"); diff --git a/target-tomoko/tools/cheat-editor.cpp b/target-tomoko/tools/cheat-editor.cpp index f1b5cb86..4f26d72c 100644 --- a/target-tomoko/tools/cheat-editor.cpp +++ b/target-tomoko/tools/cheat-editor.cpp @@ -1,5 +1,5 @@ CheatEditor::CheatEditor(TabFrame* parent) : TabFrameItem(parent) { - setIcon(Icon::Edit::Replace); + setImage(Icon::Edit::Replace); setText("Cheat Editor"); layout.setMargin(5); diff --git a/target-tomoko/tools/state-manager.cpp b/target-tomoko/tools/state-manager.cpp index 0c8912f8..87b12627 100644 --- a/target-tomoko/tools/state-manager.cpp +++ b/target-tomoko/tools/state-manager.cpp @@ -1,5 +1,5 @@ StateManager::StateManager(TabFrame* parent) : TabFrameItem(parent) { - setIcon(Icon::Application::FileManager); + setImage(Icon::Application::FileManager); setText("State Manager"); layout.setMargin(5);