mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-09-02 01:02:36 +02:00
Update to v106r69 release.
byuu says: The biggest change was improving WonderSwan emulation. With help from trap15, I tracked down a bug where I was checking the wrong bit for reverse DMA transfers. Then I also emulated VTOTAL to support variable refresh rate. Then I improved HyperVoice emulation which should be unsigned samples in three of four modes. That got Fire Lancer running great. I also rewrote the disassembler. The old one disassembled many instructions completely wrong, and deviated too much from any known x86 syntax. I also emulated some of the quirks of the V30 (two-byte POP into registers fails, SALC is just XLAT mirrored, etc) which probably don't matter unless someone tries to run code to verify it's a NEC CPU and not an Intel CPU, but hey, why not? I also put more work into the MSX skeleton, but it's still just a skeleton with no real emulation yet.
This commit is contained in:
@@ -12,6 +12,9 @@ auto pSizable::minimumSize() const -> Size {
|
||||
return {0, 0};
|
||||
}
|
||||
|
||||
auto pSizable::setCollapsible(bool collapsible) -> void {
|
||||
}
|
||||
|
||||
auto pSizable::setGeometry(Geometry geometry) -> void {
|
||||
self().doSize();
|
||||
}
|
||||
|
@@ -6,6 +6,7 @@ struct pSizable : pObject {
|
||||
Declare(Sizable, Object)
|
||||
|
||||
virtual auto minimumSize() const -> Size;
|
||||
virtual auto setCollapsible(bool collapsible) -> void;
|
||||
virtual auto setGeometry(Geometry geometry) -> void;
|
||||
};
|
||||
|
||||
|
@@ -28,6 +28,8 @@
|
||||
}
|
||||
|
||||
-(void) run:(NSTimer*)instance {
|
||||
if(Application::state().quit) return;
|
||||
|
||||
if(timer->enabled()) {
|
||||
timer->doActivate();
|
||||
}
|
||||
|
@@ -1,5 +1,9 @@
|
||||
#if defined(Hiro_Label)
|
||||
|
||||
//todo:
|
||||
//* Label::onButtonPress()
|
||||
//* Label::onButtonRelease()
|
||||
|
||||
@implementation CocoaLabel : NSTextView
|
||||
|
||||
-(id) initWith:(hiro::mLabel&)labelReference {
|
||||
|
@@ -106,3 +106,7 @@
|
||||
#if defined(Hiro_Button) && defined(Hiro_ComboButton) && defined(Hiro_LineEdit) && defined(Hiro_ListView) && defined(Hiro_MessageDialog)
|
||||
#define Hiro_BrowserDialog
|
||||
#endif
|
||||
|
||||
#if defined(Hiro_Label)
|
||||
#define Hiro_AboutDialog
|
||||
#endif
|
||||
|
@@ -72,6 +72,14 @@ auto Color::setRed(signed red) -> type& {
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto Color::setValue(uint32_t value) -> type& {
|
||||
state.alpha = value >> 24 & 255;
|
||||
state.red = value >> 16 & 255;
|
||||
state.green = value >> 8 & 255;
|
||||
state.blue = value >> 0 & 255;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto Color::value() const -> uint32_t {
|
||||
return state.alpha << 24 | state.red << 16 | state.green << 8 | state.blue << 0;
|
||||
}
|
||||
|
@@ -121,6 +121,7 @@ struct Color {
|
||||
auto setColor(int red, int green, int blue, int alpha = 255) -> type&;
|
||||
auto setGreen(int green) -> type&;
|
||||
auto setRed(int red) -> type&;
|
||||
auto setValue(uint32_t value) -> type&;
|
||||
auto value() const -> uint32_t;
|
||||
|
||||
//private:
|
||||
@@ -180,29 +181,7 @@ struct Alignment {
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(Hiro_Cursor)
|
||||
struct Cursor {
|
||||
using type = Cursor;
|
||||
|
||||
Cursor(int offset = 0, int length = 0);
|
||||
|
||||
explicit operator bool() const;
|
||||
auto operator==(const Cursor& source) const -> bool;
|
||||
auto operator!=(const Cursor& source) const -> bool;
|
||||
|
||||
auto length() const -> int;
|
||||
auto offset() const -> int;
|
||||
auto setCursor(int offset = 0, int length = 0) -> type&;
|
||||
auto setLength(int length = 0) -> type&;
|
||||
auto setOffset(int offset = 0) -> type&;
|
||||
|
||||
//private:
|
||||
struct State {
|
||||
int offset;
|
||||
int length;
|
||||
} state;
|
||||
};
|
||||
#endif
|
||||
#include "cursor.hpp"
|
||||
|
||||
#if defined(Hiro_Position)
|
||||
struct Position {
|
||||
@@ -1063,28 +1042,7 @@ struct mIconViewItem : mObject {
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(Hiro_Label)
|
||||
struct mLabel : mWidget {
|
||||
Declare(Label)
|
||||
|
||||
auto alignment() const -> Alignment;
|
||||
auto backgroundColor() const -> Color;
|
||||
auto foregroundColor() const -> Color;
|
||||
auto setAlignment(Alignment alignment = {}) -> type&;
|
||||
auto setBackgroundColor(Color color = {}) -> type&;
|
||||
auto setForegroundColor(Color color = {}) -> type&;
|
||||
auto setText(const string& text = "") -> type&;
|
||||
auto text() const -> string;
|
||||
|
||||
//private:
|
||||
struct State {
|
||||
Alignment alignment;
|
||||
Color backgroundColor;
|
||||
Color foregroundColor;
|
||||
string text;
|
||||
} state;
|
||||
};
|
||||
#endif
|
||||
#include "widget/label.hpp"
|
||||
|
||||
#if defined(Hiro_LineEdit)
|
||||
struct mLineEdit : mWidget {
|
||||
@@ -1227,91 +1185,8 @@ struct mTextEdit : mWidget {
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(Hiro_TreeView)
|
||||
struct mTreeView : mWidget {
|
||||
Declare(TreeView)
|
||||
using mObject::remove;
|
||||
|
||||
auto append(sTreeViewItem item) -> type&;
|
||||
auto backgroundColor() const -> Color;
|
||||
auto doActivate() const -> void;
|
||||
auto doChange() const -> void;
|
||||
auto doContext() const -> void;
|
||||
auto doToggle(sTreeViewItem item) const -> void;
|
||||
auto foregroundColor() const -> Color;
|
||||
auto item(const string& path) const -> TreeViewItem;
|
||||
auto itemCount() const -> uint;
|
||||
auto items() const -> vector<TreeViewItem>;
|
||||
auto onActivate(const function<void ()>& callback = {}) -> type&;
|
||||
auto onChange(const function<void ()>& callback = {}) -> type&;
|
||||
auto onContext(const function<void ()>& callback = {}) -> type&;
|
||||
auto onToggle(const function<void (sTreeViewItem)>& callback = {}) -> type&;
|
||||
auto remove(sTreeViewItem item) -> type&;
|
||||
auto reset() -> type&;
|
||||
auto selected() const -> TreeViewItem;
|
||||
auto setBackgroundColor(Color color = {}) -> type&;
|
||||
auto setForegroundColor(Color color = {}) -> type&;
|
||||
auto setParent(mObject* parent = nullptr, int offset = -1) -> type&;
|
||||
|
||||
//private:
|
||||
struct State {
|
||||
Color backgroundColor;
|
||||
Color foregroundColor;
|
||||
vector<sTreeViewItem> items;
|
||||
function<void ()> onActivate;
|
||||
function<void ()> onChange;
|
||||
function<void ()> onContext;
|
||||
function<void (sTreeViewItem)> onToggle;
|
||||
string selectedPath;
|
||||
} state;
|
||||
|
||||
auto destruct() -> void override;
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(Hiro_TreeView)
|
||||
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 foregroundColor(bool recursive = false) const -> Color;
|
||||
auto icon() const -> image;
|
||||
auto item(const string& path) const -> TreeViewItem;
|
||||
auto itemCount() const -> uint;
|
||||
auto items() const -> vector<TreeViewItem>;
|
||||
auto path() const -> string;
|
||||
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 setForegroundColor(Color color = {}) -> type&;
|
||||
auto setIcon(const image& icon = {}) -> type&;
|
||||
auto setParent(mObject* parent = nullptr, int 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;
|
||||
Color foregroundColor;
|
||||
image icon;
|
||||
vector<sTreeViewItem> items;
|
||||
string text;
|
||||
} state;
|
||||
|
||||
auto destruct() -> void override;
|
||||
};
|
||||
#endif
|
||||
#include "widget/tree-view.hpp"
|
||||
#include "widget/tree-view-item.hpp"
|
||||
|
||||
#if defined(Hiro_VerticalScrollBar)
|
||||
struct mVerticalScrollBar : mWidget {
|
||||
|
@@ -1,11 +1,11 @@
|
||||
#if defined(Hiro_Cursor)
|
||||
|
||||
Cursor::Cursor(signed offset, signed length) {
|
||||
Cursor::Cursor(int offset, int length) {
|
||||
setCursor(offset, length);
|
||||
}
|
||||
|
||||
Cursor::operator bool() const {
|
||||
return offset() && length();
|
||||
return offset() || length();
|
||||
}
|
||||
|
||||
auto Cursor::operator==(const Cursor& source) const -> bool {
|
||||
@@ -16,27 +16,27 @@ auto Cursor::operator!=(const Cursor& source) const -> bool {
|
||||
return !operator==(source);
|
||||
}
|
||||
|
||||
auto Cursor::length() const -> signed {
|
||||
auto Cursor::length() const -> int {
|
||||
return state.length;
|
||||
}
|
||||
|
||||
auto Cursor::offset() const -> signed {
|
||||
auto Cursor::offset() const -> int {
|
||||
return state.offset;
|
||||
}
|
||||
|
||||
auto Cursor::setCursor(signed offset, signed length) -> type& {
|
||||
auto Cursor::setCursor(int offset, int length) -> type& {
|
||||
state.offset = offset;
|
||||
state.length = length;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto Cursor::setOffset(signed offset) -> type& {
|
||||
state.offset = offset;
|
||||
auto Cursor::setLength(int length) -> type& {
|
||||
state.length = length;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto Cursor::setLength(signed length) -> type& {
|
||||
state.length = length;
|
||||
auto Cursor::setOffset(int offset) -> type& {
|
||||
state.offset = offset;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
23
hiro/core/cursor.hpp
Normal file
23
hiro/core/cursor.hpp
Normal file
@@ -0,0 +1,23 @@
|
||||
#if defined(Hiro_Cursor)
|
||||
struct Cursor {
|
||||
using type = Cursor;
|
||||
|
||||
Cursor(int offset = 0, int length = 0);
|
||||
|
||||
explicit operator bool() const;
|
||||
auto operator==(const Cursor& source) const -> bool;
|
||||
auto operator!=(const Cursor& source) const -> bool;
|
||||
|
||||
auto length() const -> int;
|
||||
auto offset() const -> int;
|
||||
auto setCursor(int offset = 0, int length = 0) -> type&;
|
||||
auto setLength(int length = 0) -> type&;
|
||||
auto setOffset(int offset = 0) -> type&;
|
||||
|
||||
//private:
|
||||
struct State {
|
||||
int offset;
|
||||
int length;
|
||||
} state;
|
||||
};
|
||||
#endif
|
@@ -52,10 +52,12 @@
|
||||
|
||||
#define DeclareSharedSizable(Name) \
|
||||
DeclareSharedObject(Name) \
|
||||
auto collapsible() const { return self().collapsible(); } \
|
||||
auto doSize() const { return self().doSize(); } \
|
||||
auto geometry() const { return self().geometry(); } \
|
||||
auto minimumSize() const { return self().minimumSize(); } \
|
||||
auto onSize(const function<void ()>& callback = {}) { return self().onSize(callback), *this; } \
|
||||
auto setCollapsible(bool collapsible = true) { return self().setCollapsible(collapsible), *this; } \
|
||||
auto setGeometry(Geometry geometry) { return self().setGeometry(geometry), *this; } \
|
||||
|
||||
#define DeclareSharedWidget(Name) \
|
||||
@@ -461,7 +463,11 @@ struct Label : sLabel {
|
||||
|
||||
auto alignment() const { return self().alignment(); }
|
||||
auto backgroundColor() const { return self().backgroundColor(); }
|
||||
auto doMousePress(Mouse::Button button) const { return self().doMousePress(button); }
|
||||
auto doMouseRelease(Mouse::Button button) const { return self().doMouseRelease(button); }
|
||||
auto foregroundColor() const { return self().foregroundColor(); }
|
||||
auto onMousePress(const function<void (Mouse::Button)>& callback = {}) { return self().onMousePress(callback), *this; }
|
||||
auto onMouseRelease(const function<void (Mouse::Button)>& callback = {}) { return self().onMouseRelease(callback), *this; }
|
||||
auto setAlignment(Alignment alignment = {}) { return self().setAlignment(alignment), *this; }
|
||||
auto setBackgroundColor(Color color = {}) { return self().setBackgroundColor(color), *this; }
|
||||
auto setForegroundColor(Color color = {}) { return self().setForegroundColor(color), *this; }
|
||||
@@ -540,10 +546,16 @@ struct SourceEdit : sSourceEdit {
|
||||
auto doChange() const { return self().doChange(); }
|
||||
auto doMove() const { return self().doMove(); }
|
||||
auto editable() const { return self().editable(); }
|
||||
auto language() const { return self().language(); }
|
||||
auto numbered() const { return self().numbered(); }
|
||||
auto onChange(const function<void ()>& callback = {}) { return self().onChange(callback), *this; }
|
||||
auto onMove(const function<void ()>& callback = {}) { return self().onMove(callback), *this; }
|
||||
auto scheme() const { return self().scheme(); }
|
||||
auto setCursor(Cursor cursor = {}) { return self().setCursor(cursor), *this; }
|
||||
auto setEditable(bool editable = true) { return self().setEditable(editable), *this; }
|
||||
auto setLanguage(const string& language = "") { return self().setLanguage(language), *this; }
|
||||
auto setNumbered(bool numbered = true) { return self().setNumbered(numbered), *this; }
|
||||
auto setScheme(const string& scheme = "") { return self().setScheme(scheme), *this; }
|
||||
auto setText(const string& text = "") { return self().setText(text), *this; }
|
||||
auto setWordWrap(bool wordWrap = true) { return self().setWordWrap(wordWrap), *this; }
|
||||
auto text() const { return self().text(); }
|
||||
@@ -749,6 +761,7 @@ struct TreeViewItem : sTreeViewItem {
|
||||
auto backgroundColor() const { return self().backgroundColor(); }
|
||||
auto checkable() const { return self().checkable(); }
|
||||
auto checked() const { return self().checked(); }
|
||||
auto expanded() const { return self().expanded(); }
|
||||
auto foregroundColor() const { return self().foregroundColor(); }
|
||||
auto icon() const { return self().icon(); }
|
||||
auto item(const string& path) const { return self().item(path); }
|
||||
|
@@ -4,6 +4,10 @@ auto mSizable::allocate() -> pObject* {
|
||||
return new pSizable(*this);
|
||||
}
|
||||
|
||||
auto mSizable::collapsible() const -> bool {
|
||||
return state.collapsible;
|
||||
}
|
||||
|
||||
auto mSizable::doSize() const -> void {
|
||||
if(state.onSize) return state.onSize();
|
||||
}
|
||||
@@ -21,6 +25,12 @@ auto mSizable::onSize(const function<void ()>& callback) -> type& {
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto mSizable::setCollapsible(bool collapsible) -> type& {
|
||||
state.collapsible = collapsible;
|
||||
signal(setCollapsible, collapsible);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto mSizable::setGeometry(Geometry geometry) -> type& {
|
||||
state.geometry = geometry;
|
||||
signal(setGeometry, geometry);
|
||||
|
@@ -2,15 +2,17 @@
|
||||
struct mSizable : mObject {
|
||||
Declare(Sizable)
|
||||
|
||||
auto collapsible() const -> bool;
|
||||
auto doSize() const -> void;
|
||||
auto geometry() const -> Geometry;
|
||||
virtual auto minimumSize() const -> Size;
|
||||
auto onSize(const function<void ()>& callback = {}) -> type&;
|
||||
virtual auto setCollapsible(bool collapsible = true) -> type&;
|
||||
virtual auto setGeometry(Geometry geometry) -> type&;
|
||||
|
||||
//private:
|
||||
//sizeof(mSizable) == 24
|
||||
struct State {
|
||||
bool collapsible = false;
|
||||
Geometry geometry;
|
||||
function<void ()> onSize;
|
||||
} state;
|
||||
|
@@ -14,10 +14,28 @@ auto mLabel::backgroundColor() const -> Color {
|
||||
return state.backgroundColor;
|
||||
}
|
||||
|
||||
auto mLabel::doMousePress(Mouse::Button button) const -> void {
|
||||
if(state.onMousePress) return state.onMousePress(button);
|
||||
}
|
||||
|
||||
auto mLabel::doMouseRelease(Mouse::Button button) const -> void {
|
||||
if(state.onMouseRelease) return state.onMouseRelease(button);
|
||||
}
|
||||
|
||||
auto mLabel::foregroundColor() const -> Color {
|
||||
return state.foregroundColor;
|
||||
}
|
||||
|
||||
auto mLabel::onMousePress(const function<void (Mouse::Button)>& callback) -> type& {
|
||||
state.onMousePress = callback;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto mLabel::onMouseRelease(const function<void (Mouse::Button)>& callback) -> type& {
|
||||
state.onMouseRelease = callback;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto mLabel::setAlignment(Alignment alignment) -> type& {
|
||||
state.alignment = alignment;
|
||||
signal(setAlignment, alignment);
|
||||
|
28
hiro/core/widget/label.hpp
Normal file
28
hiro/core/widget/label.hpp
Normal file
@@ -0,0 +1,28 @@
|
||||
#if defined(Hiro_Label)
|
||||
struct mLabel : mWidget {
|
||||
Declare(Label)
|
||||
|
||||
auto alignment() const -> Alignment;
|
||||
auto backgroundColor() const -> Color;
|
||||
auto doMousePress(Mouse::Button button) const -> void;
|
||||
auto doMouseRelease(Mouse::Button button) const -> void;
|
||||
auto foregroundColor() const -> Color;
|
||||
auto onMousePress(const function<void (Mouse::Button)>& callback = {}) -> type&;
|
||||
auto onMouseRelease(const function<void (Mouse::Button)>& callback = {}) -> type&;
|
||||
auto setAlignment(Alignment alignment = {}) -> type&;
|
||||
auto setBackgroundColor(Color color = {}) -> type&;
|
||||
auto setForegroundColor(Color color = {}) -> type&;
|
||||
auto setText(const string& text = "") -> type&;
|
||||
auto text() const -> string;
|
||||
|
||||
//private:
|
||||
struct State {
|
||||
Alignment alignment;
|
||||
Color backgroundColor;
|
||||
Color foregroundColor;
|
||||
function<void (Mouse::Button)> onMousePress;
|
||||
function<void (Mouse::Button)> onMouseRelease;
|
||||
string text;
|
||||
} state;
|
||||
};
|
||||
#endif
|
@@ -7,7 +7,7 @@ auto mSourceEdit::allocate() -> pObject* {
|
||||
//
|
||||
|
||||
auto mSourceEdit::cursor() const -> Cursor {
|
||||
return state.cursor;
|
||||
return signal(cursor);
|
||||
}
|
||||
|
||||
auto mSourceEdit::doChange() const -> void {
|
||||
@@ -22,6 +22,14 @@ auto mSourceEdit::editable() const -> bool {
|
||||
return state.editable;
|
||||
}
|
||||
|
||||
auto mSourceEdit::language() const -> string {
|
||||
return state.language;
|
||||
}
|
||||
|
||||
auto mSourceEdit::numbered() const -> bool {
|
||||
return state.numbered;
|
||||
}
|
||||
|
||||
auto mSourceEdit::onChange(const function<void ()>& callback) -> type& {
|
||||
state.onChange = callback;
|
||||
return *this;
|
||||
@@ -32,8 +40,11 @@ auto mSourceEdit::onMove(const function<void ()>& callback) -> type& {
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto mSourceEdit::scheme() const -> string {
|
||||
return state.scheme;
|
||||
}
|
||||
|
||||
auto mSourceEdit::setCursor(Cursor cursor) -> type& {
|
||||
state.cursor = cursor;
|
||||
signal(setCursor, cursor);
|
||||
return *this;
|
||||
}
|
||||
@@ -44,6 +55,24 @@ auto mSourceEdit::setEditable(bool editable) -> type& {
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto mSourceEdit::setLanguage(const string& language) -> type& {
|
||||
state.language = language;
|
||||
signal(setLanguage, language);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto mSourceEdit::setNumbered(bool numbered) -> type& {
|
||||
state.numbered = numbered;
|
||||
signal(setNumbered, numbered);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto mSourceEdit::setScheme(const string& scheme) -> type& {
|
||||
state.scheme = scheme;
|
||||
signal(setScheme, scheme);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto mSourceEdit::setText(const string& text) -> type& {
|
||||
state.text = text;
|
||||
signal(setText, text);
|
||||
|
@@ -6,10 +6,16 @@ struct mSourceEdit : mWidget {
|
||||
auto doChange() const -> void;
|
||||
auto doMove() const -> void;
|
||||
auto editable() const -> bool;
|
||||
auto language() const -> string;
|
||||
auto numbered() const -> bool;
|
||||
auto onChange(const function<void ()>& callback = {}) -> type&;
|
||||
auto onMove(const function<void ()>& callback = {}) -> type&;
|
||||
auto scheme() const -> string;
|
||||
auto setCursor(Cursor cursor = {}) -> type&;
|
||||
auto setEditable(bool editable) -> type&;
|
||||
auto setLanguage(const string& language = "") -> type&;
|
||||
auto setNumbered(bool numbered = true) -> type&;
|
||||
auto setScheme(const string& scheme = "") -> type&;
|
||||
auto setText(const string& text = "") -> type&;
|
||||
auto setWordWrap(bool wordWrap = true) -> type&;
|
||||
auto text() const -> string;
|
||||
@@ -17,10 +23,12 @@ struct mSourceEdit : mWidget {
|
||||
|
||||
//private:
|
||||
struct State {
|
||||
Cursor cursor;
|
||||
bool editable = true;
|
||||
string language;
|
||||
bool numbered = true;
|
||||
function<void ()> onChange;
|
||||
function<void ()> onMove;
|
||||
string scheme;
|
||||
string text;
|
||||
bool wordWrap = true;
|
||||
} state;
|
||||
|
@@ -39,6 +39,10 @@ auto mTreeViewItem::checked() const -> bool {
|
||||
return state.checked;
|
||||
}
|
||||
|
||||
auto mTreeViewItem::expanded() const -> bool {
|
||||
return state.expanded;
|
||||
}
|
||||
|
||||
auto mTreeViewItem::foregroundColor(bool recursive) const -> Color {
|
||||
if(auto color = state.foregroundColor) return color;
|
||||
if(recursive) {
|
||||
@@ -122,6 +126,7 @@ auto mTreeViewItem::setChecked(bool checked) -> type& {
|
||||
}
|
||||
|
||||
auto mTreeViewItem::setExpanded(bool expanded) -> type& {
|
||||
state.expanded = expanded;
|
||||
signal(setExpanded, expanded);
|
||||
return *this;
|
||||
}
|
||||
|
45
hiro/core/widget/tree-view-item.hpp
Normal file
45
hiro/core/widget/tree-view-item.hpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#if defined(Hiro_TreeView)
|
||||
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 expanded() const -> bool;
|
||||
auto foregroundColor(bool recursive = false) const -> Color;
|
||||
auto icon() const -> image;
|
||||
auto item(const string& path) const -> TreeViewItem;
|
||||
auto itemCount() const -> uint;
|
||||
auto items() const -> vector<TreeViewItem>;
|
||||
auto path() const -> string;
|
||||
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 setForegroundColor(Color color = {}) -> type&;
|
||||
auto setIcon(const image& icon = {}) -> type&;
|
||||
auto setParent(mObject* parent = nullptr, int 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;
|
||||
bool expanded = false;
|
||||
Color foregroundColor;
|
||||
image icon;
|
||||
vector<sTreeViewItem> items;
|
||||
string text;
|
||||
} state;
|
||||
|
||||
auto destruct() -> void override;
|
||||
};
|
||||
#endif
|
41
hiro/core/widget/tree-view.hpp
Normal file
41
hiro/core/widget/tree-view.hpp
Normal file
@@ -0,0 +1,41 @@
|
||||
#if defined(Hiro_TreeView)
|
||||
struct mTreeView : mWidget {
|
||||
Declare(TreeView)
|
||||
using mObject::remove;
|
||||
|
||||
auto append(sTreeViewItem item) -> type&;
|
||||
auto backgroundColor() const -> Color;
|
||||
auto doActivate() const -> void;
|
||||
auto doChange() const -> void;
|
||||
auto doContext() const -> void;
|
||||
auto doToggle(sTreeViewItem item) const -> void;
|
||||
auto foregroundColor() const -> Color;
|
||||
auto item(const string& path) const -> TreeViewItem;
|
||||
auto itemCount() const -> uint;
|
||||
auto items() const -> vector<TreeViewItem>;
|
||||
auto onActivate(const function<void ()>& callback = {}) -> type&;
|
||||
auto onChange(const function<void ()>& callback = {}) -> type&;
|
||||
auto onContext(const function<void ()>& callback = {}) -> type&;
|
||||
auto onToggle(const function<void (sTreeViewItem)>& callback = {}) -> type&;
|
||||
auto remove(sTreeViewItem item) -> type&;
|
||||
auto reset() -> type&;
|
||||
auto selected() const -> TreeViewItem;
|
||||
auto setBackgroundColor(Color color = {}) -> type&;
|
||||
auto setForegroundColor(Color color = {}) -> type&;
|
||||
auto setParent(mObject* parent = nullptr, int offset = -1) -> type&;
|
||||
|
||||
//private:
|
||||
struct State {
|
||||
Color backgroundColor;
|
||||
Color foregroundColor;
|
||||
vector<sTreeViewItem> items;
|
||||
function<void ()> onActivate;
|
||||
function<void ()> onChange;
|
||||
function<void ()> onContext;
|
||||
function<void (sTreeViewItem)> onToggle;
|
||||
string selectedPath;
|
||||
} state;
|
||||
|
||||
auto destruct() -> void override;
|
||||
};
|
||||
#endif
|
141
hiro/extension/about-dialog.cpp
Normal file
141
hiro/extension/about-dialog.cpp
Normal file
@@ -0,0 +1,141 @@
|
||||
#if defined(Hiro_AboutDialog)
|
||||
|
||||
auto AboutDialog::setAuthor(const string& author) -> type& {
|
||||
state.author = author;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto AboutDialog::setDescription(const string& description) -> type& {
|
||||
state.description = description;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto AboutDialog::setLicense(const string& license) -> type& {
|
||||
state.license = license;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto AboutDialog::setLogo(const image& logo) -> type& {
|
||||
state.logo = logo;
|
||||
state.logo.transform();
|
||||
state.logo.alphaBlend(0xfffff0);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto AboutDialog::setName(const string& name) -> type& {
|
||||
state.name = name;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto AboutDialog::setParent(sWindow parent) -> type& {
|
||||
state.parent = parent;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto AboutDialog::setVersion(const string& version) -> type& {
|
||||
state.version = version;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto AboutDialog::setWebsite(const string& website) -> type& {
|
||||
state.website = website;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto AboutDialog::show() -> void {
|
||||
Window window;
|
||||
window.onClose([&] { window.setModal(false); });
|
||||
|
||||
VerticalLayout layout{&window};
|
||||
layout.setPadding(5);
|
||||
|
||||
Label nameLabel{&layout, Size{~0, 0}};
|
||||
nameLabel.setCollapsible();
|
||||
nameLabel.setAlignment(0.5);
|
||||
nameLabel.setForegroundColor({0, 0, 0});
|
||||
nameLabel.setFont(Font().setFamily("Georgia").setBold().setSize(36.0));
|
||||
nameLabel.setText(state.name ? state.name : Application::name());
|
||||
nameLabel.setVisible(!state.logo);
|
||||
|
||||
Canvas logoCanvas{&layout, Size{~0, 0}};
|
||||
logoCanvas.setCollapsible();
|
||||
logoCanvas.setIcon(state.logo);
|
||||
logoCanvas.setVisible((bool)state.logo);
|
||||
|
||||
Label descriptionLabel{&layout, Size{~0, 0}};
|
||||
descriptionLabel.setCollapsible();
|
||||
descriptionLabel.setAlignment(0.5);
|
||||
descriptionLabel.setForegroundColor({0, 0, 0});
|
||||
descriptionLabel.setText(state.description);
|
||||
if(!state.description) descriptionLabel.setVisible(false);
|
||||
|
||||
HorizontalLayout versionLayout{&layout, Size{~0, 0}, 0};
|
||||
versionLayout.setCollapsible();
|
||||
Label versionLabel{&versionLayout, Size{~0, 0}, 3};
|
||||
versionLabel.setAlignment(1.0);
|
||||
versionLabel.setFont(Font().setBold());
|
||||
versionLabel.setForegroundColor({0, 0, 0});
|
||||
versionLabel.setText("Version:");
|
||||
Label versionValue{&versionLayout, Size{~0, 0}};
|
||||
versionValue.setAlignment(0.0);
|
||||
versionValue.setFont(Font().setBold());
|
||||
versionValue.setForegroundColor({0, 0, 0});
|
||||
versionValue.setText(state.version);
|
||||
if(!state.version) versionLayout.setVisible(false);
|
||||
|
||||
HorizontalLayout authorLayout{&layout, Size{~0, 0}, 0};
|
||||
authorLayout.setCollapsible();
|
||||
Label authorLabel{&authorLayout, Size{~0, 0}, 3};
|
||||
authorLabel.setAlignment(1.0);
|
||||
authorLabel.setFont(Font().setBold());
|
||||
authorLabel.setForegroundColor({0, 0, 0});
|
||||
authorLabel.setText("Author:");
|
||||
Label authorValue{&authorLayout, Size{~0, 0}};
|
||||
authorValue.setAlignment(0.0);
|
||||
authorValue.setFont(Font().setBold());
|
||||
authorValue.setForegroundColor({0, 0, 0});
|
||||
authorValue.setText(state.author);
|
||||
if(!state.author) authorLayout.setVisible(false);
|
||||
|
||||
HorizontalLayout licenseLayout{&layout, Size{~0, 0}, 0};
|
||||
licenseLayout.setCollapsible();
|
||||
Label licenseLabel{&licenseLayout, Size{~0, 0}, 3};
|
||||
licenseLabel.setAlignment(1.0);
|
||||
licenseLabel.setFont(Font().setBold());
|
||||
licenseLabel.setForegroundColor({0, 0, 0});
|
||||
licenseLabel.setText("License:");
|
||||
Label licenseValue{&licenseLayout, Size{~0, 0}};
|
||||
licenseValue.setAlignment(0.0);
|
||||
licenseValue.setFont(Font().setBold());
|
||||
licenseValue.setForegroundColor({0, 0, 0});
|
||||
licenseValue.setText(state.license);
|
||||
if(!state.license) licenseLayout.setVisible(false);
|
||||
|
||||
HorizontalLayout websiteLayout{&layout, Size{~0, 0}, 0};
|
||||
websiteLayout.setCollapsible();
|
||||
Label websiteLabel{&websiteLayout, Size{~0, 0}, 3};
|
||||
websiteLabel.setAlignment(1.0);
|
||||
websiteLabel.setFont(Font().setBold());
|
||||
websiteLabel.setForegroundColor({0, 0, 0});
|
||||
websiteLabel.setText("Website:");
|
||||
Label websiteValue{&websiteLayout, Size{~0, 0}};
|
||||
websiteValue.setAlignment(0.0);
|
||||
websiteValue.setFont(Font().setBold());
|
||||
websiteValue.setForegroundColor({0, 0, 240});
|
||||
websiteValue.setText(state.website);
|
||||
websiteValue.onMouseRelease([&](Mouse::Button button) {
|
||||
if(button == Mouse::Button::Left) invoke(state.website);
|
||||
});
|
||||
if(!state.website) websiteLayout.setVisible(false);
|
||||
|
||||
window.setTitle({"About ", state.name ? state.name : Application::name(), " ..."});
|
||||
window.setBackgroundColor({255, 255, 240});
|
||||
window.setSize({max(360, layout.minimumSize().width()), layout.minimumSize().height()});
|
||||
window.setResizable(false);
|
||||
window.setCentered(state.parent);
|
||||
window.setDismissable();
|
||||
window.setVisible();
|
||||
window.setModal();
|
||||
}
|
||||
|
||||
#endif
|
29
hiro/extension/about-dialog.hpp
Normal file
29
hiro/extension/about-dialog.hpp
Normal file
@@ -0,0 +1,29 @@
|
||||
#if defined(Hiro_AboutDialog)
|
||||
|
||||
struct AboutDialog {
|
||||
using type = AboutDialog;
|
||||
|
||||
auto setAuthor(const string& author = "") -> type&;
|
||||
auto setDescription(const string& description = "") -> type&;
|
||||
auto setLicense(const string& license = "") -> type&;
|
||||
auto setLogo(const image& logo = {}) -> type&;
|
||||
auto setName(const string& name = "") -> type&;
|
||||
auto setParent(sWindow parent = {}) -> type&;
|
||||
auto setVersion(const string& version = "") -> type&;
|
||||
auto setWebsite(const string& website = "") -> type&;
|
||||
auto show() -> void;
|
||||
|
||||
private:
|
||||
struct State {
|
||||
string author;
|
||||
string description;
|
||||
string license;
|
||||
image logo;
|
||||
string name;
|
||||
sWindow parent;
|
||||
string version;
|
||||
string website;
|
||||
} state;
|
||||
};
|
||||
|
||||
#endif
|
@@ -162,7 +162,7 @@ auto BrowserDialogWindow::run() -> BrowserDialog::Response {
|
||||
auto part = filter.split("|", 1L);
|
||||
filterList.append(ComboButtonItem().setText(part.left()));
|
||||
}
|
||||
optionList.setVisible((bool)state.options).onChange([&] { response.option = optionList.selected().text(); });
|
||||
optionList.setCollapsible().setVisible((bool)state.options).onChange([&] { response.option = optionList.selected().text(); });
|
||||
for(auto& option : state.options) {
|
||||
optionList.append(ComboButtonItem().setText(option));
|
||||
}
|
||||
|
@@ -10,4 +10,5 @@ namespace hiro {
|
||||
#include "list-view.cpp"
|
||||
#include "browser-dialog.cpp"
|
||||
#include "message-dialog.cpp"
|
||||
#include "about-dialog.cpp"
|
||||
}
|
||||
|
@@ -8,4 +8,5 @@ namespace hiro {
|
||||
#include "shared.hpp"
|
||||
#include "browser-dialog.hpp"
|
||||
#include "message-dialog.hpp"
|
||||
#include "about-dialog.hpp"
|
||||
}
|
||||
|
@@ -40,19 +40,23 @@ auto mHorizontalLayout::destruct() -> void {
|
||||
|
||||
auto mHorizontalLayout::minimumSize() const -> Size {
|
||||
float width = 0;
|
||||
float spacing = 0;
|
||||
for(auto index : range(cellCount())) {
|
||||
auto cell = this->cell(index);
|
||||
if(cell.collapsible()) continue;
|
||||
if(cell.size().width() == Size::Minimum || cell.size().width() == Size::Maximum) {
|
||||
width += cell.sizable().minimumSize().width();
|
||||
} else {
|
||||
width += cell.size().width();
|
||||
}
|
||||
if(index != cellCount() - 1) width += cell.spacing();
|
||||
width += spacing;
|
||||
spacing = cell.spacing();
|
||||
}
|
||||
|
||||
float height = 0;
|
||||
for(auto index : range(cellCount())) {
|
||||
auto cell = this->cell(index);
|
||||
if(cell.collapsible()) continue;
|
||||
if(cell.size().height() == Size::Minimum || cell.size().height() == Size::Maximum) {
|
||||
height = max(height, cell.sizable().minimumSize().height());
|
||||
continue;
|
||||
@@ -108,10 +112,10 @@ auto mHorizontalLayout::setFont(const Font& font) -> type& {
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto mHorizontalLayout::setGeometry(Geometry geometry) -> type& {
|
||||
mSizable::setGeometry(geometry);
|
||||
if(!visible(true)) return *this;
|
||||
auto mHorizontalLayout::setGeometry(Geometry requestedGeometry) -> type& {
|
||||
if(!visible(true)) return mSizable::setGeometry(requestedGeometry), *this;
|
||||
|
||||
auto geometry = requestedGeometry;
|
||||
geometry.setX(geometry.x() + padding().x());
|
||||
geometry.setY(geometry.y() + padding().y());
|
||||
geometry.setWidth (geometry.width() - padding().x() - padding().width());
|
||||
@@ -120,8 +124,9 @@ auto mHorizontalLayout::setGeometry(Geometry geometry) -> type& {
|
||||
vector<float> widths;
|
||||
widths.resize(cellCount());
|
||||
uint maximumWidths = 0;
|
||||
for(auto index : range(cellCount())) {
|
||||
for(uint index : range(cellCount())) {
|
||||
auto cell = this->cell(index);
|
||||
if(cell.collapsible()) continue;
|
||||
float width = 0;
|
||||
if(cell.size().width() == Size::Maximum) {
|
||||
width = Size::Maximum;
|
||||
@@ -135,9 +140,13 @@ auto mHorizontalLayout::setGeometry(Geometry geometry) -> type& {
|
||||
}
|
||||
|
||||
float fixedWidth = 0;
|
||||
for(uint index : range(state.cells.size())) {
|
||||
float spacing = 0;
|
||||
for(uint index : range(cellCount())) {
|
||||
auto cell = this->cell(index);
|
||||
if(cell.collapsible()) continue;
|
||||
if(widths[index] != Size::Maximum) fixedWidth += widths[index];
|
||||
if(index != cellCount() - 1) fixedWidth += cell(index).spacing();
|
||||
fixedWidth += spacing;
|
||||
spacing = cell.spacing();
|
||||
}
|
||||
|
||||
float maximumWidth = (geometry.width() - fixedWidth) / maximumWidths;
|
||||
@@ -146,8 +155,9 @@ auto mHorizontalLayout::setGeometry(Geometry geometry) -> type& {
|
||||
}
|
||||
|
||||
float height = 0;
|
||||
for(auto index : range(cellCount())) {
|
||||
for(uint index : range(cellCount())) {
|
||||
auto cell = this->cell(index);
|
||||
if(cell.collapsible()) continue;
|
||||
if(cell.size().height() == Size::Maximum) {
|
||||
height = geometry.height();
|
||||
break;
|
||||
@@ -160,10 +170,11 @@ auto mHorizontalLayout::setGeometry(Geometry geometry) -> type& {
|
||||
|
||||
float geometryX = geometry.x();
|
||||
float geometryY = geometry.y();
|
||||
for(auto index : range(cellCount())) {
|
||||
for(uint index : range(cellCount())) {
|
||||
auto cell = this->cell(index);
|
||||
if(cell.collapsible()) continue;
|
||||
float geometryWidth = widths[index];
|
||||
float geometryHeight = height;
|
||||
auto cell = this->cell(index);
|
||||
auto alignment = cell.alignment();
|
||||
if(!alignment) alignment = this->alignment();
|
||||
if(!alignment) alignment = 0.5;
|
||||
@@ -177,6 +188,7 @@ auto mHorizontalLayout::setGeometry(Geometry geometry) -> type& {
|
||||
geometryX += geometryWidth + cell.spacing();
|
||||
}
|
||||
|
||||
mSizable::setGeometry(requestedGeometry);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -218,6 +230,11 @@ auto mHorizontalLayoutCell::alignment() const -> maybe<float> {
|
||||
return state.alignment;
|
||||
}
|
||||
|
||||
auto mHorizontalLayoutCell::collapsible() const -> bool {
|
||||
if(state.sizable) return state.sizable->collapsible() && !state.sizable->visible();
|
||||
return false;
|
||||
}
|
||||
|
||||
auto mHorizontalLayoutCell::destruct() -> void {
|
||||
if(auto& sizable = state.sizable) sizable->destruct();
|
||||
mObject::destruct();
|
||||
|
@@ -49,6 +49,7 @@ struct mHorizontalLayoutCell : mObject {
|
||||
using type = mHorizontalLayoutCell;
|
||||
|
||||
auto alignment() const -> maybe<float>;
|
||||
auto collapsible() const -> bool;
|
||||
auto setAlignment(maybe<float> alignment) -> type&;
|
||||
auto setEnabled(bool enabled) -> type& override;
|
||||
auto setFont(const Font& font) -> type& override;
|
||||
|
@@ -26,6 +26,7 @@ struct HorizontalLayoutCell : sHorizontalLayoutCell {
|
||||
DeclareSharedObject(HorizontalLayoutCell)
|
||||
|
||||
auto alignment() const { return self().alignment(); }
|
||||
auto collapsible() const { return self().collapsible(); }
|
||||
auto setAlignment(maybe<float> alignment = {}) { return self().setAlignment(alignment), *this; }
|
||||
auto setSizable(sSizable sizable) { return self().setSizable(sizable), *this; }
|
||||
auto setSize(Size size) { return self().setSize(size), *this; }
|
||||
@@ -58,6 +59,7 @@ struct VerticalLayoutCell : sVerticalLayoutCell {
|
||||
DeclareSharedObject(VerticalLayoutCell)
|
||||
|
||||
auto alignment() const { return self().alignment(); }
|
||||
auto collapsible() const { return self().collapsible(); }
|
||||
auto setAlignment(maybe<float> alignment = {}) { return self().setAlignment(alignment), *this; }
|
||||
auto setSizable(sSizable sizable) { return self().setSizable(sizable), *this; }
|
||||
auto setSize(Size size) { return self().setSize(size), *this; }
|
||||
|
@@ -143,10 +143,10 @@ auto mTableLayout::setFont(const Font& font) -> type& {
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto mTableLayout::setGeometry(Geometry geometry) -> type& {
|
||||
mSizable::setGeometry(geometry);
|
||||
if(!visible(true)) return *this;
|
||||
auto mTableLayout::setGeometry(Geometry requestedGeometry) -> type& {
|
||||
if(!visible(true)) return mSizable::setGeometry(requestedGeometry), *this;
|
||||
|
||||
auto geometry = requestedGeometry;
|
||||
geometry.setX(geometry.x() + padding().x());
|
||||
geometry.setY(geometry.y() + padding().y());
|
||||
geometry.setWidth (geometry.width() - padding().x() - padding().width());
|
||||
@@ -250,6 +250,7 @@ auto mTableLayout::setGeometry(Geometry geometry) -> type& {
|
||||
geometryY += heights[y] + row.spacing();
|
||||
}
|
||||
|
||||
mSizable::setGeometry(requestedGeometry);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
@@ -42,6 +42,7 @@ auto mVerticalLayout::minimumSize() const -> Size {
|
||||
float width = 0;
|
||||
for(auto index : range(cellCount())) {
|
||||
auto cell = this->cell(index);
|
||||
if(cell.collapsible()) continue;
|
||||
if(cell.size().width() == Size::Minimum || cell.size().width() == Size::Maximum) {
|
||||
width = max(width, cell.sizable().minimumSize().width());
|
||||
continue;
|
||||
@@ -50,14 +51,17 @@ auto mVerticalLayout::minimumSize() const -> Size {
|
||||
}
|
||||
|
||||
float height = 0;
|
||||
float spacing = 0;
|
||||
for(auto index : range(cellCount())) {
|
||||
auto cell = this->cell(index);
|
||||
if(cell.collapsible()) continue;
|
||||
if(cell.size().height() == Size::Minimum || cell.size().height() == Size::Maximum) {
|
||||
height += cell.sizable().minimumSize().height();
|
||||
} else {
|
||||
height += cell.size().height();
|
||||
}
|
||||
if(index != cellCount() - 1) height += cell.spacing();
|
||||
height += spacing;
|
||||
spacing = cell.spacing();
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -108,18 +112,19 @@ auto mVerticalLayout::setFont(const Font& font) -> type& {
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto mVerticalLayout::setGeometry(Geometry geometry) -> type& {
|
||||
mSizable::setGeometry(geometry);
|
||||
if(!visible(true)) return *this;
|
||||
auto mVerticalLayout::setGeometry(Geometry requestedGeometry) -> type& {
|
||||
if(!visible(true)) return mSizable::setGeometry(requestedGeometry), *this;
|
||||
|
||||
auto geometry = requestedGeometry;
|
||||
geometry.setX(geometry.x() + padding().x());
|
||||
geometry.setY(geometry.y() + padding().y());
|
||||
geometry.setWidth (geometry.width() - padding().x() - padding().width());
|
||||
geometry.setHeight(geometry.height() - padding().y() - padding().height());
|
||||
|
||||
float width = 0;
|
||||
for(auto index : range(cellCount())) {
|
||||
for(uint index : range(cellCount())) {
|
||||
auto cell = this->cell(index);
|
||||
if(cell.collapsible()) continue;
|
||||
if(cell.size().width() == Size::Maximum) {
|
||||
width = geometry.width();
|
||||
break;
|
||||
@@ -133,8 +138,9 @@ auto mVerticalLayout::setGeometry(Geometry geometry) -> type& {
|
||||
vector<float> heights;
|
||||
heights.resize(cellCount());
|
||||
uint maximumHeights = 0;
|
||||
for(auto index : range(cellCount())) {
|
||||
for(uint index : range(cellCount())) {
|
||||
auto cell = this->cell(index);
|
||||
if(cell.collapsible()) continue;
|
||||
float height = 0;
|
||||
if(cell.size().height() == Size::Maximum) {
|
||||
height = Size::Maximum;
|
||||
@@ -148,9 +154,13 @@ auto mVerticalLayout::setGeometry(Geometry geometry) -> type& {
|
||||
}
|
||||
|
||||
float fixedHeight = 0;
|
||||
for(uint index : range(state.cells.size())) {
|
||||
float spacing = 0;
|
||||
for(uint index : range(cellCount())) {
|
||||
auto cell = this->cell(index);
|
||||
if(cell.collapsible()) continue;
|
||||
if(heights[index] != Size::Maximum) fixedHeight += heights[index];
|
||||
if(index != cellCount() - 1) fixedHeight += cell(index).spacing();
|
||||
fixedHeight += spacing;
|
||||
spacing = cell.spacing();
|
||||
}
|
||||
|
||||
float maximumHeight = (geometry.height() - fixedHeight) / maximumHeights;
|
||||
@@ -160,10 +170,11 @@ auto mVerticalLayout::setGeometry(Geometry geometry) -> type& {
|
||||
|
||||
float geometryX = geometry.x();
|
||||
float geometryY = geometry.y();
|
||||
for(auto index : range(cellCount())) {
|
||||
for(uint index : range(cellCount())) {
|
||||
auto cell = this->cell(index);
|
||||
if(cell.collapsible()) continue;
|
||||
float geometryWidth = width;
|
||||
float geometryHeight = heights[index];
|
||||
auto cell = this->cell(index);
|
||||
auto alignment = cell.alignment();
|
||||
if(!alignment) alignment = this->alignment();
|
||||
if(!alignment) alignment = 0.0;
|
||||
@@ -177,6 +188,7 @@ auto mVerticalLayout::setGeometry(Geometry geometry) -> type& {
|
||||
geometryY += geometryHeight + cell.spacing();
|
||||
}
|
||||
|
||||
mSizable::setGeometry(requestedGeometry);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -218,6 +230,11 @@ auto mVerticalLayoutCell::alignment() const -> maybe<float> {
|
||||
return state.alignment;
|
||||
}
|
||||
|
||||
auto mVerticalLayoutCell::collapsible() const -> bool {
|
||||
if(state.sizable) return state.sizable->collapsible() && !state.sizable->visible();
|
||||
return false;
|
||||
}
|
||||
|
||||
auto mVerticalLayoutCell::destruct() -> void {
|
||||
if(auto& sizable = state.sizable) sizable->destruct();
|
||||
mObject::destruct();
|
||||
|
@@ -49,6 +49,7 @@ struct mVerticalLayoutCell : mObject {
|
||||
using type = mVerticalLayoutCell;
|
||||
|
||||
auto alignment() const -> maybe<float>;
|
||||
auto collapsible() const -> bool;
|
||||
auto setAlignment(maybe<float> alignment) -> type&;
|
||||
auto setEnabled(bool enabled) -> type& override;
|
||||
auto setFont(const Font& font) -> type& override;
|
||||
|
@@ -10,6 +10,9 @@ auto pApplication::run() -> void {
|
||||
while(!Application::state().quit) {
|
||||
Application::doMain();
|
||||
processEvents();
|
||||
//avoid spinlooping the thread when there is no main loop ...
|
||||
//when there is one, Application::onMain() is expected to sleep when possible instead
|
||||
if(!Application::state().onMain) usleep(2000);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +21,13 @@ auto pApplication::pendingEvents() -> bool {
|
||||
}
|
||||
|
||||
auto pApplication::processEvents() -> void {
|
||||
while(pendingEvents()) gtk_main_iteration_do(false);
|
||||
//GTK can sometimes return gtk_pending_events() == true forever,
|
||||
//no matter how many times gtk_main_iteration_do() is called.
|
||||
//implement a timeout to prevent hiro from hanging forever in this case.
|
||||
auto time = chrono::millisecond();
|
||||
while(pendingEvents() && chrono::millisecond() - time < 50) {
|
||||
gtk_main_iteration_do(false);
|
||||
}
|
||||
for(auto& window : state().windows) window->_synchronizeGeometry();
|
||||
}
|
||||
|
||||
|
@@ -16,7 +16,8 @@ auto pFont::size(PangoFontDescription* font, const string& text) -> Size {
|
||||
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);
|
||||
g_object_unref(layout);
|
||||
g_object_unref(context);
|
||||
return {width, height};
|
||||
}
|
||||
|
||||
@@ -29,7 +30,7 @@ auto pFont::family(const string& family) -> string {
|
||||
#elif defined(DISPLAY_XORG)
|
||||
if(family == Font::Sans ) return "Sans";
|
||||
if(family == Font::Serif) return "Serif";
|
||||
if(family == Font::Mono ) return "Liberation Mono";
|
||||
if(family == Font::Mono ) return "Monospace";
|
||||
return family ? family : "Sans";
|
||||
#else
|
||||
return family;
|
||||
|
@@ -12,6 +12,9 @@ auto pSizable::minimumSize() const -> Size {
|
||||
return {0, 0};
|
||||
}
|
||||
|
||||
auto pSizable::setCollapsible(bool collapsible) -> void {
|
||||
}
|
||||
|
||||
auto pSizable::setGeometry(Geometry geometry) -> void {
|
||||
self().doSize();
|
||||
}
|
||||
|
@@ -6,6 +6,7 @@ struct pSizable : pObject {
|
||||
Declare(Sizable, Object)
|
||||
|
||||
virtual auto minimumSize() const -> Size;
|
||||
virtual auto setCollapsible(bool collapsible) -> void;
|
||||
virtual auto setGeometry(Geometry geometry) -> void;
|
||||
};
|
||||
|
||||
|
@@ -3,6 +3,9 @@
|
||||
namespace hiro {
|
||||
|
||||
static auto Timer_trigger(pTimer* p) -> signed {
|
||||
//prevent all timers from firing once the program has been terminated
|
||||
if(Application::state().quit) return false;
|
||||
|
||||
//timer may have been disabled prior to triggering, so check state
|
||||
if(p->self().enabled(true)) p->self().doActivate();
|
||||
|
||||
|
@@ -46,6 +46,24 @@ static auto Label_expose(GtkWidget* widget, GdkEvent* event, pLabel* p) -> int {
|
||||
return false;
|
||||
}
|
||||
|
||||
static auto Label_mousePress(GtkWidget* widget, GdkEventButton* event, pLabel* p) -> int {
|
||||
switch(event->button) {
|
||||
case 1: p->self().doMousePress(Mouse::Button::Left); break;
|
||||
case 2: p->self().doMousePress(Mouse::Button::Middle); break;
|
||||
case 3: p->self().doMousePress(Mouse::Button::Right); break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static auto Label_mouseRelease(GtkWidget* widget, GdkEventButton* event, pLabel* p) -> int {
|
||||
switch(event->button) {
|
||||
case 1: p->self().doMouseRelease(Mouse::Button::Left); break;
|
||||
case 2: p->self().doMouseRelease(Mouse::Button::Middle); break;
|
||||
case 3: p->self().doMouseRelease(Mouse::Button::Right); break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
auto pLabel::construct() -> void {
|
||||
gtkWidget = gtk_event_box_new();
|
||||
subWidget = gtk_label_new("");
|
||||
@@ -57,6 +75,8 @@ auto pLabel::construct() -> void {
|
||||
setForegroundColor(state().foregroundColor);
|
||||
setText(state().text);
|
||||
|
||||
g_signal_connect(G_OBJECT(gtkWidget), "button-press-event", G_CALLBACK(Label_mousePress), (gpointer)this);
|
||||
g_signal_connect(G_OBJECT(gtkWidget), "button-release-event", G_CALLBACK(Label_mouseRelease), (gpointer)this);
|
||||
#if HIRO_GTK==2
|
||||
g_signal_connect(G_OBJECT(subWidget), "expose-event", G_CALLBACK(Label_expose), (gpointer)this);
|
||||
#elif HIRO_GTK==3
|
||||
|
@@ -7,13 +7,7 @@ static auto SourceEdit_change(GtkTextBuffer*, pSourceEdit* p) -> void {
|
||||
}
|
||||
|
||||
static auto SourceEdit_move(GObject*, GParamSpec*, pSourceEdit* p) -> void {
|
||||
signed offset = 0;
|
||||
g_object_get(G_OBJECT(p->gtkSourceBuffer), "cursor-position", &offset, nullptr);
|
||||
|
||||
if(p->state().cursor.offset() != offset) {
|
||||
p->state().cursor.setOffset(offset);
|
||||
if(!p->locked()) p->self().doMove();
|
||||
}
|
||||
if(!p->locked()) p->self().doMove();
|
||||
}
|
||||
|
||||
auto pSourceEdit::construct() -> void {
|
||||
@@ -24,16 +18,16 @@ auto pSourceEdit::construct() -> void {
|
||||
gtk_scrolled_window_set_shadow_type(gtkScrolledWindow, GTK_SHADOW_ETCHED_IN);
|
||||
|
||||
gtkSourceLanguageManager = gtk_source_language_manager_get_default();
|
||||
gtkSourceLanguage = gtk_source_language_manager_get_language(gtkSourceLanguageManager, "cpp");
|
||||
gtkSourceLanguage = gtk_source_language_manager_get_language(gtkSourceLanguageManager, "");
|
||||
|
||||
gtkSourceStyleSchemeManager = gtk_source_style_scheme_manager_get_default();
|
||||
gtkSourceStyleScheme = gtk_source_style_scheme_manager_get_scheme(gtkSourceStyleSchemeManager, "oblivion");
|
||||
gtkSourceStyleScheme = gtk_source_style_scheme_manager_get_scheme(gtkSourceStyleSchemeManager, "classic");
|
||||
|
||||
gtkSourceBuffer = gtk_source_buffer_new(nullptr);
|
||||
gtkTextBuffer = GTK_TEXT_BUFFER(gtkSourceBuffer);
|
||||
gtk_source_buffer_set_highlight_matching_brackets(gtkSourceBuffer, true);
|
||||
gtk_source_buffer_set_highlight_syntax(gtkSourceBuffer, true);
|
||||
//gtk_source_buffer_set_language(gtkSourceBuffer, gtkSourceLanguage);
|
||||
gtk_source_buffer_set_language(gtkSourceBuffer, gtkSourceLanguage);
|
||||
gtk_source_buffer_set_style_scheme(gtkSourceBuffer, gtkSourceStyleScheme);
|
||||
|
||||
gtkSourceView = (GtkSourceView*)gtk_source_view_new_with_buffer(gtkSourceBuffer);
|
||||
@@ -55,6 +49,9 @@ auto pSourceEdit::construct() -> void {
|
||||
gtk_widget_show(gtkWidgetSourceView);
|
||||
|
||||
setEditable(state().editable);
|
||||
setLanguage(state().language);
|
||||
setNumbered(state().numbered);
|
||||
setScheme(state().scheme);
|
||||
setText(state().text);
|
||||
setWordWrap(state().wordWrap);
|
||||
|
||||
@@ -70,6 +67,23 @@ auto pSourceEdit::destruct() -> void {
|
||||
gtk_widget_destroy(gtkWidget);
|
||||
}
|
||||
|
||||
auto pSourceEdit::cursor() const -> Cursor {
|
||||
Cursor cursor;
|
||||
int offset = 0;
|
||||
g_object_get(G_OBJECT(gtkSourceBuffer), "cursor-position", &offset, nullptr);
|
||||
cursor.setOffset(offset);
|
||||
GtkTextIter start, end;
|
||||
if(gtk_text_buffer_get_selection_bounds(gtkTextBuffer, &start, &end)) {
|
||||
//if selecting text from left to right, the cursor may be ahead of the selection start ...
|
||||
//since hiro combines selection bounds (end-start) into length, move the offset to the start
|
||||
int origin = gtk_text_iter_get_offset(&start);
|
||||
cursor.setOffset(origin);
|
||||
int length = gtk_text_iter_get_offset(&end) - origin;
|
||||
cursor.setLength(length);
|
||||
}
|
||||
return cursor;
|
||||
}
|
||||
|
||||
auto pSourceEdit::setCursor(Cursor cursor) -> void {
|
||||
lock();
|
||||
GtkTextIter offset, length;
|
||||
@@ -92,45 +106,33 @@ auto pSourceEdit::setFocused() -> void {
|
||||
gtk_widget_grab_focus(gtkWidgetSourceView);
|
||||
}
|
||||
|
||||
/*
|
||||
auto pSourceEdit::setPosition(signed position) -> void {
|
||||
lock();
|
||||
GtkTextIter iter;
|
||||
//note: iterators must be initialized via get_iter() before calling set_offset()
|
||||
gtk_text_buffer_get_end_iter(gtkTextBuffer, &iter);
|
||||
if(position >= 0) {
|
||||
gtk_text_iter_set_offset(&iter, position);
|
||||
} else {
|
||||
state().position = gtk_text_iter_get_offset(&iter);
|
||||
}
|
||||
gtk_text_buffer_place_cursor(gtkTextBuffer, &iter);
|
||||
auto mark = gtk_text_buffer_get_mark(gtkTextBuffer, "insert");
|
||||
gtk_text_view_scroll_mark_onscreen(gtkTextView, mark);
|
||||
unlock();
|
||||
auto pSourceEdit::setLanguage(const string& language) -> void {
|
||||
string name;
|
||||
if(language == "C") name = "c";
|
||||
if(language == "C++") name = "cpp";
|
||||
if(language == "Make") name = "makefile";
|
||||
gtkSourceLanguage = gtk_source_language_manager_get_language(gtkSourceLanguageManager, name);
|
||||
gtk_source_buffer_set_language(gtkSourceBuffer, gtkSourceLanguage);
|
||||
}
|
||||
|
||||
auto pSourceEdit::setSelected(Position selected) -> void {
|
||||
lock();
|
||||
GtkTextIter iter;
|
||||
gtk_text_buffer_get_end_iter(gtkTextBuffer, &iter);
|
||||
signed offset = gtk_text_iter_get_offset(&iter);
|
||||
if(selected.x() < 0 || selected.x() > offset) selected.setX(offset);
|
||||
if(selected.y() < 0 || selected.y() > offset) selected.setY(offset);
|
||||
state().selected = selected;
|
||||
GtkTextIter startIter;
|
||||
gtk_text_buffer_get_start_iter(gtkTextBuffer, &startIter);
|
||||
gtk_text_iter_set_offset(&startIter, selected.x());
|
||||
GtkTextIter endIter;
|
||||
gtk_text_buffer_get_end_iter(gtkTextBuffer, &endIter);
|
||||
gtk_text_iter_set_offset(&endIter, selected.y());
|
||||
gtk_text_buffer_select_range(gtkTextBuffer, &startIter, &endIter);
|
||||
unlock();
|
||||
auto pSourceEdit::setNumbered(bool numbered) -> void {
|
||||
gtk_source_view_set_show_line_numbers(gtkSourceView, numbered);
|
||||
}
|
||||
|
||||
auto pSourceEdit::setScheme(const string& requestedScheme) -> void {
|
||||
auto scheme = requestedScheme ? requestedScheme : "classic";
|
||||
gtkSourceStyleScheme = gtk_source_style_scheme_manager_get_scheme(gtkSourceStyleSchemeManager, scheme.downcase());
|
||||
if(!gtkSourceStyleScheme) gtkSourceStyleScheme = gtk_source_style_scheme_manager_get_scheme(gtkSourceStyleSchemeManager, "classic");
|
||||
gtk_source_buffer_set_style_scheme(gtkSourceBuffer, gtkSourceStyleScheme);
|
||||
}
|
||||
*/
|
||||
|
||||
auto pSourceEdit::setText(const string& text) -> void {
|
||||
lock();
|
||||
//prevent Ctrl+Z from undoing the newly assigned text ...
|
||||
//for instance, a text editor widget setting the initial document here
|
||||
gtk_source_buffer_begin_not_undoable_action(gtkSourceBuffer);
|
||||
gtk_text_buffer_set_text(gtkTextBuffer, text, -1);
|
||||
gtk_source_buffer_end_not_undoable_action(gtkSourceBuffer);
|
||||
unlock();
|
||||
}
|
||||
|
||||
|
@@ -5,9 +5,13 @@ namespace hiro {
|
||||
struct pSourceEdit : pWidget {
|
||||
Declare(SourceEdit, Widget)
|
||||
|
||||
auto cursor() const -> Cursor;
|
||||
auto setCursor(Cursor cursor) -> void;
|
||||
auto setEditable(bool editable) -> void;
|
||||
auto setFocused() -> void override;
|
||||
auto setLanguage(const string& language) -> void;
|
||||
auto setNumbered(bool numbered) -> void;
|
||||
auto setScheme(const string& scheme) -> void;
|
||||
auto setText(const string& text) -> void;
|
||||
auto setWordWrap(bool wordWrap) -> void;
|
||||
auto text() const -> string;
|
||||
|
@@ -294,30 +294,40 @@ auto pTableView::_doEdit(GtkCellRendererText* gtkCellRendererText, const char* p
|
||||
}
|
||||
}
|
||||
|
||||
auto pTableView::_doEvent(GdkEventButton* event) -> signed {
|
||||
GtkTreePath* path = nullptr;
|
||||
gtk_tree_view_get_path_at_pos(gtkTreeView, event->x, event->y, &path, nullptr, nullptr, nullptr);
|
||||
auto pTableView::_doEvent(GdkEventButton* gdkEvent) -> signed {
|
||||
if(gdkEvent->type == GDK_BUTTON_PRESS) {
|
||||
//detect when the empty space of the GtkTreeView is clicked; and clear the selection
|
||||
GtkTreePath* gtkPath = nullptr;
|
||||
gtk_tree_view_get_path_at_pos(gtkTreeView, gdkEvent->x, gdkEvent->y, >kPath, nullptr, nullptr, nullptr);
|
||||
if(!gtkPath) {
|
||||
//the first time a GtkTreeView widget is clicked, even if the empty space of the widget is clicked,
|
||||
//a "changed" signal will be sent after the "button-press-event", to activate the first item in the tree
|
||||
//this is undesirable, so set a flag to undo the next selection change during the "changed" signal
|
||||
suppressChange = true;
|
||||
if(gtk_tree_selection_count_selected_rows(gtkTreeSelection) > 0) {
|
||||
gtk_tree_selection_unselect_all(gtkTreeSelection);
|
||||
for(auto& item : state().items) item->setSelected(false);
|
||||
self().doChange();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if(event->type == GDK_BUTTON_PRESS) {
|
||||
//when clicking in empty space below the last table view item; GTK+ does not deselect all items;
|
||||
//below code enables this functionality, to match behavior with all other UI toolkits (and because it's very convenient to have)
|
||||
if(path == nullptr && gtk_tree_selection_count_selected_rows(gtkTreeSelection) > 0) {
|
||||
for(auto& item : state().items) item->setSelected(false);
|
||||
self().doChange();
|
||||
return true;
|
||||
if(gdkEvent->button == 3) {
|
||||
//multi-selection mode:
|
||||
//if multiple items are selected, and one item is right-clicked on (for a context menu), GTK clears selection on all other items
|
||||
//block this behavior so that onContext() handler can work on more than one selected item at a time
|
||||
if(gtkPath && gtk_tree_selection_path_is_selected(gtkTreeSelection, gtkPath)) return true;
|
||||
}
|
||||
}
|
||||
|
||||
if(event->type == GDK_BUTTON_PRESS && event->button == 3) {
|
||||
//this check prevents the loss of selection on other items if the item under the mouse cursor is currently selected
|
||||
if(path && gtk_tree_selection_path_is_selected(gtkTreeSelection, path)) return true;
|
||||
}
|
||||
|
||||
if(event->type == GDK_BUTTON_RELEASE && event->button == 3) {
|
||||
//handle action during right-click release; as button-press-event is sent prior to selection update
|
||||
//without this, the callback handler would see the previous selection state instead
|
||||
self().doContext();
|
||||
return false;
|
||||
if(gdkEvent->type == GDK_BUTTON_RELEASE) {
|
||||
suppressChange = false;
|
||||
if(gdkEvent->button == 3) {
|
||||
//handle action during right-click release; as button-press-event is sent prior to selection update
|
||||
//without this, the callback handler would see the previous selection state instead
|
||||
self().doContext();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -365,6 +375,12 @@ auto pTableView::_doToggle(GtkCellRendererToggle* gtkCellRendererToggle, const c
|
||||
//this prevents firing an onChange event when the actual selection has not changed
|
||||
//this is particularly important for the motion-notify-event binding
|
||||
auto pTableView::_updateSelected() -> void {
|
||||
if(suppressChange) {
|
||||
suppressChange = false;
|
||||
gtk_tree_selection_unselect_all(gtkTreeSelection);
|
||||
return;
|
||||
}
|
||||
|
||||
vector<unsigned> selected;
|
||||
|
||||
GList* list = gtk_tree_selection_get_selected_rows(gtkTreeSelection, >kTreeModel);
|
||||
|
@@ -44,6 +44,7 @@ struct pTableView : pWidget {
|
||||
GtkListStore* gtkListStore = nullptr;
|
||||
GtkTreeModel* gtkTreeModel = nullptr;
|
||||
vector<uint> currentSelection;
|
||||
bool suppressChange = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
@@ -25,6 +25,8 @@ auto pTreeView::construct() -> void {
|
||||
gtkTreeView = GTK_TREE_VIEW(gtkWidgetChild);
|
||||
gtkTreeSelection = gtk_tree_view_get_selection(gtkTreeView);
|
||||
gtk_tree_view_set_headers_visible(gtkTreeView, false);
|
||||
gtk_tree_view_set_show_expanders(gtkTreeView, false);
|
||||
gtk_tree_view_set_level_indentation(gtkTreeView, 20);
|
||||
gtk_container_add(GTK_CONTAINER(gtkWidget), gtkWidgetChild);
|
||||
gtk_widget_show(gtkWidgetChild);
|
||||
|
||||
@@ -58,10 +60,19 @@ auto pTreeView::construct() -> void {
|
||||
g_signal_connect(G_OBJECT(gtkTreeSelection), "changed", G_CALLBACK(TreeView_change), (gpointer)this);
|
||||
g_signal_connect(G_OBJECT(gtkCellToggle), "toggled", G_CALLBACK(TreeView_toggle), (gpointer)this);
|
||||
|
||||
//Ctrl+F triggers a small popup window at the bottom of the GtkTreeView, which clears the currently selected item(s)
|
||||
//this is undesirable for amethyst, which uses the active item to display a document to edit, and binds Ctrl+F to a document find function
|
||||
//for now, disable GtkTreeView's interactive search: longer term, more thought will need to go into if this is ever desirable or not
|
||||
//gtk_tree_view_set_enable_search(gtkTreeView, false) does not work
|
||||
//gtk_tree_view_set_search_column(gtkTreeView, -1) does not work
|
||||
gtkEntry = (GtkEntry*)gtk_entry_new();
|
||||
gtk_tree_view_set_search_entry(gtkTreeView, gtkEntry);
|
||||
|
||||
pWidget::construct();
|
||||
}
|
||||
|
||||
auto pTreeView::destruct() -> void {
|
||||
gtk_widget_destroy(GTK_WIDGET(gtkEntry));
|
||||
gtk_widget_destroy(gtkWidgetChild);
|
||||
gtk_widget_destroy(gtkWidget);
|
||||
}
|
||||
@@ -105,24 +116,39 @@ auto pTreeView::_activatePath(GtkTreePath* gtkPath) -> void {
|
||||
}
|
||||
|
||||
auto pTreeView::_buttonEvent(GdkEventButton* gdkEvent) -> signed {
|
||||
GtkTreePath* gtkPath = nullptr;
|
||||
gtk_tree_view_get_path_at_pos(gtkTreeView, gdkEvent->x, gdkEvent->y, >kPath, nullptr, nullptr, nullptr);
|
||||
|
||||
if(gdkEvent->type == GDK_BUTTON_PRESS) {
|
||||
//detect when the empty space of the GtkTreeView is clicked; and clear the selection
|
||||
if(gtkPath == nullptr && gtk_tree_selection_count_selected_rows(gtkTreeSelection) > 0) {
|
||||
gtk_tree_selection_unselect_all(gtkTreeSelection);
|
||||
state().selectedPath.reset();
|
||||
self().doChange();
|
||||
return true;
|
||||
GtkTreePath* gtkPath = nullptr;
|
||||
gtk_tree_view_get_path_at_pos(gtkTreeView, gdkEvent->x, gdkEvent->y, >kPath, nullptr, nullptr, nullptr);
|
||||
if(!gtkPath) {
|
||||
//the first time a GtkTreeView widget is clicked, even if the empty space of the widget is clicked,
|
||||
//a "changed" signal will be sent after the "button-press-event", to activate the first item in the tree
|
||||
//this is undesirable, so set a flag to undo the next selection change during the "changed" signal
|
||||
suppressChange = true;
|
||||
if(gtk_tree_selection_count_selected_rows(gtkTreeSelection) > 0) {
|
||||
gtk_tree_selection_unselect_all(gtkTreeSelection);
|
||||
state().selectedPath.reset();
|
||||
self().doChange();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if(gdkEvent->button == 3) {
|
||||
//multi-selection mode: (not implemented in TreeView yet ... but code is here anyway for future use)
|
||||
//if multiple items are selected, and one item is right-clicked on (for a context menu), GTK clears selection on all other items
|
||||
//block this behavior so that onContext() handler can work on more than one selected item at a time
|
||||
if(gtkPath && gtk_tree_selection_path_is_selected(gtkTreeSelection, gtkPath)) return true;
|
||||
}
|
||||
}
|
||||
|
||||
if(gdkEvent->type == GDK_BUTTON_RELEASE && gdkEvent->button == 3) {
|
||||
//handle right-click context menu
|
||||
//have to detect on button release instead of press; as GTK+ does not update new selection prior to press event
|
||||
self().doContext();
|
||||
return false;
|
||||
if(gdkEvent->type == GDK_BUTTON_RELEASE) {
|
||||
suppressChange = false;
|
||||
if(gdkEvent->button == 3) {
|
||||
//handle action during right-click release; as button-press-event is sent prior to selection update
|
||||
//without this, the callback handler would see the previous selection state instead
|
||||
self().doContext();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -173,6 +199,12 @@ auto pTreeView::_togglePath(string path) -> void {
|
||||
}
|
||||
|
||||
auto pTreeView::_updateSelected() -> void {
|
||||
if(suppressChange) {
|
||||
suppressChange = false;
|
||||
gtk_tree_selection_unselect_all(gtkTreeSelection);
|
||||
return;
|
||||
}
|
||||
|
||||
GtkTreeIter iter;
|
||||
if(gtk_tree_selection_get_selected(gtkTreeSelection, >kTreeModel, &iter)) {
|
||||
char* gtkPath = gtk_tree_model_get_string_from_iter(gtkTreeModel, &iter);
|
||||
|
@@ -27,6 +27,8 @@ struct pTreeView : pWidget {
|
||||
GtkCellRenderer* gtkCellToggle = nullptr;
|
||||
GtkCellRenderer* gtkCellPixbuf = nullptr;
|
||||
GtkCellRenderer* gtkCellText = nullptr;
|
||||
GtkEntry* gtkEntry = nullptr;
|
||||
bool suppressChange = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
@@ -329,7 +329,9 @@ auto pWindow::setGeometry(Geometry geometry) -> void {
|
||||
setMinimumSize(state().minimumSize);
|
||||
|
||||
auto time1 = chrono::millisecond();
|
||||
while(chrono::millisecond() - time1 < 20) Application::processEvents();
|
||||
while(chrono::millisecond() - time1 < 20) {
|
||||
Application::processEvents();
|
||||
}
|
||||
|
||||
gtk_window_resize(GTK_WINDOW(widget), geometry.width(), geometry.height() + _menuHeight() + _statusHeight());
|
||||
|
||||
@@ -627,7 +629,7 @@ auto pWindow::_synchronizeState() -> void {
|
||||
if(!gtk_widget_get_realized(widget)) return;
|
||||
|
||||
#if defined(DISPLAY_WINDOWS)
|
||||
auto window = GDK_WINDOW_HWND(gtk_widget_get_window(widget));
|
||||
auto window = (HWND)GDK_WINDOW_HWND(gtk_widget_get_window(widget));
|
||||
|
||||
bool maximized = IsZoomed(window);
|
||||
bool minimized = IsIconic(window);
|
||||
|
@@ -189,6 +189,8 @@ struct QtLabel : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
QtLabel(pLabel& p) : p(p) {}
|
||||
auto mousePressEvent(QMouseEvent*) -> void;
|
||||
auto mouseReleaseEvent(QMouseEvent*) -> void;
|
||||
auto paintEvent(QPaintEvent*) -> void;
|
||||
pLabel& p;
|
||||
};
|
||||
|
1379
hiro/qt/qt.moc
1379
hiro/qt/qt.moc
File diff suppressed because it is too large
Load Diff
@@ -12,6 +12,9 @@ auto pSizable::minimumSize() const -> Size {
|
||||
return {0, 0};
|
||||
}
|
||||
|
||||
auto pSizable::setCollapsible(bool collapsible) -> void {
|
||||
}
|
||||
|
||||
auto pSizable::setGeometry(Geometry geometry) -> void {
|
||||
self().doSize();
|
||||
}
|
||||
|
@@ -6,6 +6,7 @@ struct pSizable : pObject {
|
||||
Declare(Sizable, Object)
|
||||
|
||||
virtual auto minimumSize() const -> Size;
|
||||
virtual auto setCollapsible(bool collapsible) -> void;
|
||||
virtual auto setGeometry(Geometry geometry) -> void;
|
||||
};
|
||||
|
||||
|
@@ -25,6 +25,7 @@ auto pTimer::setInterval(unsigned interval) -> void {
|
||||
}
|
||||
|
||||
auto QtTimer::onActivate() -> void {
|
||||
if(Application::state().quit) return;
|
||||
p.self().doActivate();
|
||||
}
|
||||
|
||||
|
@@ -40,6 +40,22 @@ auto pLabel::setText(const string& text) -> void {
|
||||
qtLabel->update();
|
||||
}
|
||||
|
||||
auto QtLabel::mousePressEvent(QMouseEvent* event) -> void {
|
||||
switch(event->button()) {
|
||||
case Qt::LeftButton: p.self().doMousePress(Mouse::Button::Left); break;
|
||||
case Qt::MidButton: p.self().doMousePress(Mouse::Button::Middle); break;
|
||||
case Qt::RightButton: p.self().doMousePress(Mouse::Button::Right); break;
|
||||
}
|
||||
}
|
||||
|
||||
auto QtLabel::mouseReleaseEvent(QMouseEvent* event) -> void {
|
||||
switch(event->button()) {
|
||||
case Qt::LeftButton: p.self().doMouseRelease(Mouse::Button::Left); break;
|
||||
case Qt::MidButton: p.self().doMouseRelease(Mouse::Button::Middle); break;
|
||||
case Qt::RightButton: p.self().doMouseRelease(Mouse::Button::Right); break;
|
||||
}
|
||||
}
|
||||
|
||||
//QLabel ignores QPalette ... so we have to implement our own Label class atop QWidget ...
|
||||
auto QtLabel::paintEvent(QPaintEvent* event) -> void {
|
||||
QPainter painter(p.qtLabel);
|
||||
|
BIN
hiro/resource/icon/action/close.png
Normal file
BIN
hiro/resource/icon/action/close.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 586 B |
BIN
hiro/resource/icon/emblem/folder-open.png
Normal file
BIN
hiro/resource/icon/emblem/folder-open.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 625 B |
@@ -3,6 +3,7 @@ namespace name=Icon
|
||||
binary name=Add file=icon/action/add.png
|
||||
binary name=Attach file=icon/action/attach.png
|
||||
binary name=Bookmark file=icon/action/bookmark.png
|
||||
binary name=Close file=icon/action/close.png
|
||||
binary name=FullScreen file=icon/action/full-screen.png
|
||||
binary name=Mute file=icon/action/mute.png
|
||||
binary name=New file=icon/action/new.png
|
||||
@@ -53,6 +54,7 @@ namespace name=Icon
|
||||
binary name=Binary file=icon/emblem/binary.png
|
||||
binary name=File file=icon/emblem/file.png
|
||||
binary name=Folder file=icon/emblem/folder.png
|
||||
binary name=FolderOpen file=icon/emblem/folder-open.png
|
||||
binary name=Font file=icon/emblem/font.png
|
||||
binary name=Image file=icon/emblem/image.png
|
||||
binary name=Markup file=icon/emblem/markup.png
|
||||
|
@@ -62,6 +62,27 @@ const unsigned char Bookmark[686] = {
|
||||
221,234,14,26,128,72,36,194,78,163,215,90,127,221,211,211,211,189,147,194,104,52,218,7,240,7,70,86,184,198,50,151,
|
||||
228,191,0,0,0,0,73,69,78,68,174,66,96,130,
|
||||
};
|
||||
const unsigned char Close[586] = {
|
||||
137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
|
||||
97,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,9,112,72,89,115,0,0,13,215,0,0,13,
|
||||
215,1,66,40,155,120,0,0,0,25,116,69,88,116,83,111,102,116,119,97,114,101,0,119,119,119,46,105,110,107,115,99,
|
||||
97,112,101,46,111,114,103,155,238,60,26,0,0,1,199,73,68,65,84,56,141,165,147,205,106,26,97,20,134,31,51,14,
|
||||
5,77,71,218,136,100,83,20,131,67,118,198,149,76,93,185,51,5,23,221,100,153,69,46,32,189,130,118,81,122,3,189,
|
||||
130,118,147,149,12,130,139,226,118,50,232,106,4,133,4,162,208,162,20,132,128,197,159,90,181,240,165,167,27,45,147,161,
|
||||
41,20,15,156,205,57,231,121,57,63,223,23,18,17,182,177,157,173,232,7,4,18,64,26,136,250,98,81,224,96,157,251,
|
||||
167,192,126,62,159,63,214,117,253,163,109,219,230,26,140,218,182,109,234,186,254,33,159,207,31,3,251,247,8,17,217,120,
|
||||
194,178,172,83,160,11,92,105,154,118,89,173,86,143,170,213,234,145,166,105,151,192,21,208,181,44,235,84,68,18,27,46,
|
||||
228,91,98,58,149,74,189,31,12,6,79,0,1,30,133,195,225,57,128,82,106,23,248,9,132,146,201,228,184,223,239,191,
|
||||
2,190,4,71,184,245,60,239,34,147,201,92,3,11,96,172,148,210,148,82,26,48,6,22,153,76,230,218,243,188,11,224,
|
||||
118,3,133,2,103,52,70,163,209,139,66,161,240,188,215,235,61,243,39,76,211,252,218,104,52,154,241,120,252,19,48,123,
|
||||
104,137,119,173,86,171,183,92,46,247,214,93,252,241,213,106,245,180,221,110,119,129,59,63,224,239,32,90,175,215,15,202,
|
||||
229,242,107,165,148,2,230,1,241,93,93,215,181,90,173,246,174,84,42,125,6,126,4,5,210,177,88,236,205,108,54,251,
|
||||
5,124,7,200,102,179,223,0,58,157,206,222,186,230,177,97,24,59,211,233,244,237,223,150,56,175,84,42,205,72,36,50,
|
||||
4,22,185,92,110,232,56,206,141,227,56,55,185,92,110,8,44,34,145,200,176,82,169,52,239,117,231,123,7,136,200,161,
|
||||
235,186,103,197,98,241,124,50,153,156,136,136,33,34,198,100,50,57,41,22,139,231,174,235,158,137,200,161,159,9,10,32,
|
||||
34,166,136,188,92,195,155,152,177,142,153,193,250,224,25,255,219,182,254,141,191,1,124,82,8,56,142,215,29,200,0,0,
|
||||
0,0,73,69,78,68,174,66,96,130,
|
||||
};
|
||||
const unsigned char FullScreen[650] = {
|
||||
137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
|
||||
97,0,0,0,6,98,75,71,68,0,255,0,255,0,255,160,189,167,147,0,0,0,9,112,72,89,115,0,0,11,19,0,
|
||||
@@ -1116,6 +1137,28 @@ const unsigned char Folder[581] = {
|
||||
119,199,119,159,130,243,227,251,248,159,1,34,140,69,194,139,223,71,22,33,158,28,99,167,49,0,0,0,0,73,69,78,
|
||||
68,174,66,96,130,
|
||||
};
|
||||
const unsigned char FolderOpen[625] = {
|
||||
137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
|
||||
97,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,9,112,72,89,115,0,0,13,215,0,0,13,
|
||||
215,1,66,40,155,120,0,0,0,25,116,69,88,116,83,111,102,116,119,97,114,101,0,119,119,119,46,105,110,107,115,99,
|
||||
97,112,101,46,111,114,103,155,238,60,26,0,0,1,238,73,68,65,84,56,141,165,147,59,79,148,65,20,134,159,153,239,
|
||||
219,93,195,71,178,33,94,18,27,45,181,48,33,86,212,118,22,90,108,225,63,48,177,163,38,196,206,146,248,11,44,164,
|
||||
129,127,161,198,80,24,45,76,136,196,196,74,140,72,34,176,11,24,216,203,156,51,231,88,44,139,44,40,20,158,100,170,
|
||||
57,239,115,46,239,76,112,119,254,39,194,252,252,252,75,119,127,248,151,187,174,136,60,90,88,88,120,127,46,96,110,110,
|
||||
110,175,213,106,53,205,12,51,99,212,81,167,211,97,101,101,229,85,85,85,15,70,201,235,235,235,0,44,46,46,14,252,
|
||||
40,177,20,17,87,85,182,183,183,201,57,143,65,106,181,218,76,187,221,62,112,119,220,157,170,170,112,119,102,103,103,63,
|
||||
1,119,79,0,4,85,197,204,198,32,211,211,211,147,83,83,83,84,85,69,81,20,196,24,233,118,187,44,45,45,221,25,
|
||||
117,85,170,170,169,102,114,254,115,70,0,17,161,219,237,18,66,32,132,64,140,17,128,148,210,241,230,75,17,241,148,210,
|
||||
24,32,231,204,168,109,119,63,3,80,213,227,37,150,41,37,83,85,114,206,44,127,140,236,245,34,80,59,215,58,171,238,
|
||||
215,102,158,44,247,66,140,95,75,85,69,68,200,57,179,219,139,60,125,124,143,34,134,35,143,128,127,188,147,164,118,233,
|
||||
217,139,183,183,75,17,49,17,193,204,8,56,19,141,130,181,239,191,112,31,234,9,167,124,7,98,12,220,184,60,65,8,
|
||||
88,41,34,166,58,4,20,49,80,196,64,12,225,140,240,68,101,250,253,76,188,10,128,31,217,56,92,90,89,20,20,49,
|
||||
208,40,227,25,225,64,140,94,202,100,115,26,69,28,141,105,165,170,90,74,137,131,1,52,39,235,108,237,15,232,167,140,
|
||||
251,176,218,64,51,73,109,108,21,101,17,216,218,235,15,71,8,33,152,153,241,101,167,206,245,107,77,118,15,133,190,24,
|
||||
189,164,199,162,211,35,197,16,216,216,57,36,134,184,85,186,195,234,38,172,254,172,115,171,89,231,205,234,15,178,93,252,
|
||||
67,59,237,125,178,219,235,50,55,174,188,251,176,89,221,4,103,237,243,183,11,133,39,98,3,179,231,191,1,223,162,81,
|
||||
163,208,43,119,152,0,0,0,0,73,69,78,68,174,66,96,130,
|
||||
};
|
||||
const unsigned char Font[627] = {
|
||||
137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
|
||||
97,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,25,116,69,88,116,83,111,102,116,119,97,114,
|
||||
|
@@ -3,6 +3,7 @@ namespace Action {
|
||||
extern const unsigned char Add[323];
|
||||
extern const unsigned char Attach[649];
|
||||
extern const unsigned char Bookmark[686];
|
||||
extern const unsigned char Close[586];
|
||||
extern const unsigned char FullScreen[650];
|
||||
extern const unsigned char Mute[632];
|
||||
extern const unsigned char New[477];
|
||||
@@ -57,6 +58,7 @@ extern const unsigned char Audio[688];
|
||||
extern const unsigned char Binary[560];
|
||||
extern const unsigned char File[741];
|
||||
extern const unsigned char Folder[581];
|
||||
extern const unsigned char FolderOpen[625];
|
||||
extern const unsigned char Font[627];
|
||||
extern const unsigned char Image[558];
|
||||
extern const unsigned char Markup[709];
|
||||
|
@@ -12,6 +12,9 @@ auto pSizable::minimumSize() const -> Size {
|
||||
return {0, 0};
|
||||
}
|
||||
|
||||
auto pSizable::setCollapsible(bool collapsible) -> void {
|
||||
}
|
||||
|
||||
auto pSizable::setGeometry(Geometry geometry) -> void {
|
||||
self().doSize();
|
||||
}
|
||||
|
@@ -6,6 +6,7 @@ struct pSizable : pObject {
|
||||
Declare(Sizable, Object)
|
||||
|
||||
virtual auto minimumSize() const -> Size;
|
||||
virtual auto setCollapsible(bool collapsible) -> void;
|
||||
virtual auto setGeometry(Geometry geometry) -> void;
|
||||
};
|
||||
|
||||
|
@@ -8,6 +8,8 @@ namespace hiro {
|
||||
static vector<pTimer*> timers;
|
||||
|
||||
static auto CALLBACK Timer_timeoutProc(HWND hwnd, UINT msg, UINT_PTR timerID, DWORD time) -> void {
|
||||
if(Application::state().quit) return;
|
||||
|
||||
for(auto& timer : timers) {
|
||||
if(timer->htimer == timerID) return timer->self().doActivate();
|
||||
}
|
||||
|
@@ -95,6 +95,22 @@ auto pLabel::windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> ma
|
||||
return msg == WM_ERASEBKGND;
|
||||
}
|
||||
|
||||
if(msg == WM_LBUTTONDOWN || msg == WM_MBUTTONDOWN || msg == WM_RBUTTONDOWN) {
|
||||
switch(msg) {
|
||||
case WM_LBUTTONDOWN: self().doMousePress(Mouse::Button::Left); break;
|
||||
case WM_MBUTTONDOWN: self().doMousePress(Mouse::Button::Middle); break;
|
||||
case WM_RBUTTONDOWN: self().doMousePress(Mouse::Button::Right); break;
|
||||
}
|
||||
}
|
||||
|
||||
if(msg == WM_LBUTTONUP || msg == WM_MBUTTONUP || msg == WM_RBUTTONUP) {
|
||||
switch(msg) {
|
||||
case WM_LBUTTONUP: self().doMouseRelease(Mouse::Button::Left); break;
|
||||
case WM_MBUTTONUP: self().doMouseRelease(Mouse::Button::Middle); break;
|
||||
case WM_RBUTTONUP: self().doMouseRelease(Mouse::Button::Right); break;
|
||||
}
|
||||
}
|
||||
|
||||
return pWidget::windowProc(hwnd, msg, wparam, lparam);
|
||||
}
|
||||
|
||||
|
@@ -51,10 +51,9 @@ auto pLineEdit::onChange() -> void {
|
||||
}
|
||||
|
||||
auto pLineEdit::windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> maybe<LRESULT> {
|
||||
if(msg == WM_KEYDOWN) {
|
||||
if(msg == WM_KEYDOWN && wparam == VK_RETURN) {
|
||||
self().doActivate();
|
||||
}
|
||||
|
||||
return pWidget::windowProc(hwnd, msg, wparam, lparam);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user