Update to bsnes v107.1 release.

byuu says:

Don't let the point release fool you, there are many significant changes in this
release. I will be keeping bsnes releases using a point system until the new
higan release is ready.

Changelog:

  - GUI: added high DPI support
  - GUI: fixed the state manager image preview
  - Windows: added a new waveOut driver with support for dynamic rate control
  - Windows: corrected the XAudio 2.1 dynamic rate control support [BearOso]
  - Windows: corrected the Direct3D 9.0 fullscreen exclusive window centering
  - Windows: fixed XInput controller support on Windows 10
  - SFC: added high-level emulation for the DSP1, DSP2, DSP4, ST010, and Cx4
    coprocessors
  - SFC: fixed a slight rendering glitch in the intro to Megalomania

If the coprocessor firmware is missing, bsnes will fallback on HLE where it is
supported, which is everything other than SD Gundam GX and the two Hayazashi
Nidan Morita Shougi games.

The Windows dynamic rate control works best with Direct3D in fullscreen
exclusive mode. I recommend the waveOut driver over the XAudio 2.1 driver, as it
is not possible to target a single XAudio2 version on all Windows OS releases.
The waveOut driver should work everywhere out of the box.

Note that with DRC, the synchronization source is your monitor, so you will
want to be running at 60hz (NTSC) or 50hz (PAL). If you have an adaptive sync
monitor, you should instead use the WASAPI (exclusive) or ASIO audio driver.
This commit is contained in:
Tim Allen
2019-04-09 11:16:30 +10:00
parent 7786206a4f
commit 4d7bb510f2
223 changed files with 9895 additions and 3116 deletions

View File

@@ -1,5 +1,17 @@
#if defined(Hiro_AboutDialog)
auto AboutDialog::setAlignment(Alignment alignment) -> type& {
state.alignment = alignment;
state.relativeTo = {};
return *this;
}
auto AboutDialog::setAlignment(sWindow relativeTo, Alignment alignment) -> type& {
state.alignment = alignment;
state.relativeTo = relativeTo;
return *this;
}
auto AboutDialog::setAuthor(const string& author) -> type& {
state.author = author;
return *this;
@@ -27,11 +39,6 @@ auto AboutDialog::setName(const string& name) -> type& {
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;
@@ -47,7 +54,7 @@ auto AboutDialog::show() -> void {
window.onClose([&] { window.setModal(false); });
VerticalLayout layout{&window};
layout.setPadding(5);
layout.setPadding(5_sx, 5_sy);
Label nameLabel{&layout, Size{~0, 0}};
nameLabel.setCollapsible();
@@ -59,8 +66,13 @@ auto AboutDialog::show() -> void {
Canvas logoCanvas{&layout, Size{~0, 0}};
logoCanvas.setCollapsible();
logoCanvas.setIcon(state.logo);
logoCanvas.setVisible((bool)state.logo);
if(state.logo) {
image logo{state.logo};
logo.scale(sx(logo.width()), sy(logo.height()));
logoCanvas.setIcon(logo);
} else {
logoCanvas.setVisible(false);
}
Label descriptionLabel{&layout, Size{~0, 0}};
descriptionLabel.setCollapsible();
@@ -71,7 +83,7 @@ auto AboutDialog::show() -> void {
HorizontalLayout versionLayout{&layout, Size{~0, 0}, 0};
versionLayout.setCollapsible();
Label versionLabel{&versionLayout, Size{~0, 0}, 3};
Label versionLabel{&versionLayout, Size{~0, 0}, 3_sx};
versionLabel.setAlignment(1.0);
versionLabel.setFont(Font().setBold());
versionLabel.setForegroundColor({0, 0, 0});
@@ -85,7 +97,7 @@ auto AboutDialog::show() -> void {
HorizontalLayout authorLayout{&layout, Size{~0, 0}, 0};
authorLayout.setCollapsible();
Label authorLabel{&authorLayout, Size{~0, 0}, 3};
Label authorLabel{&authorLayout, Size{~0, 0}, 3_sx};
authorLabel.setAlignment(1.0);
authorLabel.setFont(Font().setBold());
authorLabel.setForegroundColor({0, 0, 0});
@@ -99,7 +111,7 @@ auto AboutDialog::show() -> void {
HorizontalLayout licenseLayout{&layout, Size{~0, 0}, 0};
licenseLayout.setCollapsible();
Label licenseLabel{&licenseLayout, Size{~0, 0}, 3};
Label licenseLabel{&licenseLayout, Size{~0, 0}, 3_sx};
licenseLabel.setAlignment(1.0);
licenseLabel.setFont(Font().setBold());
licenseLabel.setForegroundColor({0, 0, 0});
@@ -113,7 +125,7 @@ auto AboutDialog::show() -> void {
HorizontalLayout websiteLayout{&layout, Size{~0, 0}, 0};
websiteLayout.setCollapsible();
Label websiteLabel{&websiteLayout, Size{~0, 0}, 3};
Label websiteLabel{&websiteLayout, Size{~0, 0}, 3_sx};
websiteLabel.setAlignment(1.0);
websiteLabel.setFont(Font().setBold());
websiteLabel.setForegroundColor({0, 0, 0});
@@ -123,16 +135,16 @@ auto AboutDialog::show() -> void {
websiteValue.setFont(Font().setBold());
websiteValue.setForegroundColor({0, 0, 240});
websiteValue.setText(state.website);
websiteValue.onMouseRelease([&](Mouse::Button button) {
websiteValue.onMouseRelease([&](auto 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.setSize({max(360_sx, layout.minimumSize().width()), layout.minimumSize().height()});
window.setResizable(false);
window.setCentered(state.parent);
window.setAlignment(state.relativeTo, state.alignment);
window.setDismissable();
window.setVisible();
window.setModal();

View File

@@ -3,24 +3,26 @@
struct AboutDialog {
using type = AboutDialog;
auto setAlignment(Alignment = Alignment::Center) -> type&;
auto setAlignment(sWindow relativeTo, Alignment = Alignment::Center) -> type&;
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 {
Alignment alignment = Alignment::Center;
string author;
string description;
string license;
image logo;
string name;
sWindow parent;
sWindow relativeTo;
string version;
string website;
} state;

View File

@@ -7,6 +7,7 @@ struct BrowserDialogWindow {
auto accept() -> void;
auto activate() -> void;
auto change() -> void;
auto context() -> void;
auto isFolder(const string& name) -> bool;
auto isMatch(const string& name) -> bool;
auto run() -> BrowserDialog::Response;
@@ -15,18 +16,26 @@ struct BrowserDialogWindow {
private:
Window window;
VerticalLayout layout{&window};
HorizontalLayout pathLayout{&layout, Size{~0, 0}, 5};
HorizontalLayout pathLayout{&layout, Size{~0, 0}, 5_sx};
LineEdit pathName{&pathLayout, Size{~0, 0}, 0};
Button pathRefresh{&pathLayout, Size{0, 0}, 0};
Button pathNew{&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};
ListView view{&layout, Size{~0, ~0}, 5_sx};
HorizontalLayout controlLayout{&layout, Size{~0, 0}};
ComboButton filterList{&controlLayout, Size{0, 0}, 5};
LineEdit fileName{&controlLayout, Size{~0, 0}, 5};
ComboButton optionList{&controlLayout, Size{0, 0}, 5};
Button acceptButton{&controlLayout, Size{80, 0}, 5};
Button cancelButton{&controlLayout, Size{80, 0}, 5};
ComboButton filterList{&controlLayout, Size{0, 0}, 5_sx};
LineEdit fileName{&controlLayout, Size{~0, 0}, 5_sx};
ComboButton optionList{&controlLayout, Size{0, 0}, 5_sx};
Button acceptButton{&controlLayout, Size{80_sx, 0}, 5_sx};
Button cancelButton{&controlLayout, Size{80_sx, 0}, 5_sx};
PopupMenu contextMenu;
MenuItem createAction{&contextMenu};
MenuItem renameAction{&contextMenu};
MenuItem removeAction{&contextMenu};
MenuSeparator contextSeparator{&contextMenu};
MenuCheckItem showHiddenOption{&contextMenu};
BrowserDialog::State& state;
BrowserDialog::Response response;
@@ -45,6 +54,8 @@ auto BrowserDialogWindow::accept() -> void {
}
if(state.action == "openFiles" && batched) {
string name = batched[0].text();
if(isFolder(name) && batched.size() == 1) return setPath({state.path, name});
for(auto item : batched) {
string name = item.text();
if(isFolder(name)) {
@@ -135,6 +146,24 @@ auto BrowserDialogWindow::change() -> void {
}
}
auto BrowserDialogWindow::context() -> void {
auto batched = view.batched();
if(!batched) {
createAction.setVisible(true);
renameAction.setVisible(false);
removeAction.setVisible(false);
} else if(batched.size() == 1) {
createAction.setVisible(false);
renameAction.setVisible(true);
removeAction.setVisible(true);
} else {
createAction.setVisible(false);
renameAction.setVisible(false);
removeAction.setVisible(true);
}
contextMenu.setVisible();
}
auto BrowserDialogWindow::isFolder(const string& name) -> bool {
return directory::exists({state.path, name});
}
@@ -151,12 +180,28 @@ auto BrowserDialogWindow::isMatch(const string& name) -> bool {
auto BrowserDialogWindow::run() -> BrowserDialog::Response {
response = {};
layout.setPadding(5);
auto document = BML::unserialize(file::read({Path::userSettings(), "hiro/browser-dialog.bml"}));
struct Settings {
bool showHidden = true;
} settings;
if(auto node = document["BrowserDialog/ShowHidden"]) settings.showHidden = node.boolean();
layout.setPadding(5_sx, 5_sy);
pathName.onActivate([&] { setPath(pathName.text()); });
pathRefresh.setBordered(false).setIcon(Icon::Action::Refresh).onActivate([&] { setPath(state.path); });
pathHome.setBordered(false).setIcon(Icon::Go::Home).onActivate([&] { setPath(Path::user()); });
pathUp.setBordered(false).setIcon(Icon::Go::Up).onActivate([&] { setPath(Location::dir(state.path)); });
image iconRefresh{Icon::Action::Refresh};
iconRefresh.scale(16_sx, 16_sy);
pathRefresh.setBordered(false).setIcon(iconRefresh).onActivate([&] { setPath(state.path); });
image iconNew{Icon::Action::NewFolder};
iconNew.scale(16_sx, 16_sy);
pathNew.setBordered(false).setIcon(iconNew).onActivate([&] { createAction.doActivate(); });
image iconHome{Icon::Go::Home};
iconHome.scale(16_sx, 16_sy);
pathHome.setBordered(false).setIcon(iconHome).onActivate([&] { setPath(Path::user()); });
image iconUp{Icon::Go::Up};
iconUp.scale(16_sx, 16_sy);
pathUp.setBordered(false).setIcon(iconUp).onActivate([&] { setPath(Location::dir(state.path)); });
view.setBatchable(state.action == "openFiles").onActivate([&] { activate(); }).onChange([&] { change(); });
view.onContext([&] { context(); });
filterList.setVisible(state.action != "selectFolder").onChange([&] { setPath(state.path); });
for(auto& filter : state.filters) {
auto part = filter.split("|", 1L);
@@ -184,12 +229,101 @@ auto BrowserDialogWindow::run() -> BrowserDialog::Response {
filters.append(part.right().split(":"));
}
createAction.setIcon(Icon::Action::NewFolder).setText("Create Folder ...").onActivate([&] {
if(auto name = NameDialog()
.setTitle("Create Folder")
.setText("Enter a new folder name:")
.setIcon(Icon::Emblem::Folder)
.setAlignment(window)
.create()
) {
directory::create({state.path, name});
pathRefresh.doActivate();
}
});
renameAction.setIcon(Icon::Application::TextEditor).setText("Rename ...").onActivate([&] {
auto batched = view.batched();
if(batched.size() != 1) return;
auto name = batched[0].text();
if(directory::exists({state.path, name})) {
if(auto rename = NameDialog()
.setTitle({"Rename ", name})
.setText("Enter the new folder name:")
.setIcon(Icon::Emblem::Folder)
.setAlignment(window)
.rename(name)
) {
if(name == rename) return;
if(!directory::rename({state.path, name}, {state.path, rename})) return (void)MessageDialog()
.setTitle("Error")
.setText("Failed to rename folder.")
.setAlignment(window)
.error();
pathRefresh.doActivate();
}
} else if(file::exists({state.path, name})) {
if(auto rename = NameDialog()
.setTitle({"Rename ", name})
.setText("Enter the new file name:")
.setIcon(Icon::Emblem::File)
.setAlignment(window)
.rename(name)
) {
if(name == rename) return;
if(!file::rename({state.path, name}, {state.path, rename})) return (void)MessageDialog()
.setTitle("Error")
.setText("Failed to rename file.")
.setAlignment(window)
.error();
pathRefresh.doActivate();
}
}
});
removeAction.setIcon(Icon::Action::Remove).setText("Delete ...").onActivate([&] {
auto batched = view.batched();
if(!batched) return;
if(MessageDialog()
.setTitle("Remove Selected")
.setText({"Are you sure you want to permanently delete the selected item", batched.size() == 1 ? "" : "s", "?"})
.setAlignment(window)
.question() == "No") return;
for(auto& item : batched) {
auto name = item.text();
if(directory::exists({state.path, name})) {
if(!directory::remove({state.path, name})) {
if(MessageDialog()
.setTitle("Warning")
.setText({"Failed to remove ", name, "\n\nContinue trying to remove remaining items?"})
.question() == "No") break;
}
} else if(file::exists({state.path, name})) {
if(!file::remove({state.path, name})) {
if(MessageDialog()
.setTitle("Warning")
.setText({"Failed to remove ", name, "\n\nContinue trying to remove remaining items?"})
.question() == "No") break;
}
}
}
pathRefresh.doActivate();
});
showHiddenOption.setChecked(settings.showHidden).setText("Show Hidden").onToggle([&] {
auto document = BML::unserialize(file::read({Path::userSettings(), "hiro/browser-dialog.bml"}));
document("BrowserDialog/ShowHidden").setValue(showHiddenOption.checked());
directory::create({Path::userSettings(), "hiro/"});
file::write({Path::userSettings(), "hiro/browser-dialog.bml"}, BML::serialize(document));
pathRefresh.doActivate();
});
setPath(state.path);
window.onClose([&] { window.setModal(false); });
window.setTitle(state.title);
window.setSize({640, 480});
window.setCentered(state.parent);
window.setSize({640_sx, 480_sy});
window.setAlignment(state.relativeTo, state.alignment);
window.setDismissable();
window.setVisible();
view.setFocused();
@@ -218,6 +352,7 @@ auto BrowserDialogWindow::setPath(string path) -> void {
} else {
continue;
}
if(!showHiddenOption.checked() && directory::hidden({state.path, content})) continue;
view.append(ListViewItem().setText(content).setIcon(Icon::Emblem::Folder));
}
@@ -230,6 +365,7 @@ auto BrowserDialogWindow::setPath(string path) -> void {
if(state.action == "openFolder") continue;
}
if(!isMatch(content)) continue;
if(!showHiddenOption.checked() && file::hidden({state.path, content})) continue;
view.append(ListViewItem().setText(content).setIcon(isFolder ? (image)Icon::Action::Open : (image)Icon::Emblem::File));
}
@@ -293,6 +429,18 @@ auto BrowserDialog::selectFolder() -> string {
return {};
}
auto BrowserDialog::setAlignment(Alignment alignment) -> type& {
state.alignment = alignment;
state.relativeTo = {};
return *this;
}
auto BrowserDialog::setAlignment(sWindow relativeTo, Alignment alignment) -> type& {
state.alignment = alignment;
state.relativeTo = relativeTo;
return *this;
}
auto BrowserDialog::setFilters(const vector<string>& filters) -> type& {
state.filters = filters;
return *this;
@@ -303,11 +451,6 @@ auto BrowserDialog::setOptions(const vector<string>& options) -> type& {
return *this;
}
auto BrowserDialog::setParent(const sWindow& parent) -> type& {
state.parent = parent;
return *this;
}
auto BrowserDialog::setPath(const string& path) -> type& {
state.path = path;
return *this;

View File

@@ -14,19 +14,21 @@ struct BrowserDialog {
auto saveFile() -> string; //one file
auto selected() -> vector<string>;
auto selectFolder() -> string; //one existing folder
auto setAlignment(Alignment = Alignment::Center) -> type&;
auto setAlignment(sWindow relativeTo, Alignment = Alignment::Center) -> type&;
auto setFilters(const vector<string>& filters = {}) -> type&;
auto setOptions(const vector<string>& options = {}) -> type&;
auto setParent(const sWindow& parent) -> type&;
auto setPath(const string& path = "") -> type&;
auto setTitle(const string& title = "") -> type&;
private:
struct State {
string action;
Alignment alignment = Alignment::Center;
vector<string> filters = {"*"};
vector<string> options;
sWindow parent;
string path;
sWindow relativeTo;
string title;
} state;

View File

@@ -7,8 +7,11 @@ namespace hiro {
#include "horizontal-layout.cpp"
#include "vertical-layout.cpp"
#include "table-layout.cpp"
#include "horizontal-resize-grip.cpp"
#include "vertical-resize-grip.cpp"
#include "list-view.cpp"
#include "browser-dialog.cpp"
#include "message-dialog.cpp"
#include "name-dialog.cpp"
#include "browser-dialog.cpp"
#include "about-dialog.cpp"
}

View File

@@ -4,9 +4,12 @@ namespace hiro {
#include "horizontal-layout.hpp"
#include "vertical-layout.hpp"
#include "table-layout.hpp"
#include "horizontal-resize-grip.hpp"
#include "vertical-resize-grip.hpp"
#include "list-view.hpp"
#include "shared.hpp"
#include "browser-dialog.hpp"
#include "message-dialog.hpp"
#include "name-dialog.hpp"
#include "browser-dialog.hpp"
#include "about-dialog.hpp"
}

View File

@@ -24,6 +24,10 @@ auto mFixedLayout::cell(sSizable sizable) const -> FixedLayoutCell {
return {};
}
auto mFixedLayout::cells() const -> vector<FixedLayoutCell> {
return state.cells;
}
auto mFixedLayout::cellCount() const -> uint {
return state.cells.size();
}
@@ -63,6 +67,11 @@ auto mFixedLayout::reset() -> type& {
return synchronize();
}
auto mFixedLayout::resize() -> type& {
setGeometry(geometry());
return *this;
}
auto mFixedLayout::setEnabled(bool enabled) -> type& {
mSizable::setEnabled(enabled);
for(auto& cell : state.cells) cell.sizable().setEnabled(cell.sizable().enabled());

View File

@@ -16,11 +16,13 @@ struct mFixedLayout : mSizable {
auto append(sSizable sizable, Geometry geometry) -> type&;
auto cell(uint position) const -> FixedLayoutCell;
auto cell(sSizable sizable) const -> FixedLayoutCell;
auto cells() const -> vector<FixedLayoutCell>;
auto cellCount() const -> uint;
auto minimumSize() const -> Size override;
auto remove(sSizable sizable) -> type&;
auto remove(sFixedLayoutCell cell) -> type&;
auto reset() -> type& override;
auto resize() -> type&;
auto setEnabled(bool enabled) -> type& override;
auto setFont(const Font& font) -> type& override;
auto setParent(mObject* parent = nullptr, int offset = -1) -> type& override;

View File

@@ -29,6 +29,10 @@ auto mHorizontalLayout::cell(sSizable sizable) const -> HorizontalLayoutCell {
return {};
}
auto mHorizontalLayout::cells() const -> vector<HorizontalLayoutCell> {
return state.cells;
}
auto mHorizontalLayout::cellCount() const -> uint {
return state.cells.size();
}
@@ -95,6 +99,11 @@ auto mHorizontalLayout::reset() -> type& {
return synchronize();
}
auto mHorizontalLayout::resize() -> type& {
setGeometry(geometry());
return *this;
}
auto mHorizontalLayout::setAlignment(maybe<float> alignment) -> type& {
state.alignment = alignment;
return synchronize();
@@ -113,7 +122,7 @@ auto mHorizontalLayout::setFont(const Font& font) -> type& {
}
auto mHorizontalLayout::setGeometry(Geometry requestedGeometry) -> type& {
if(!visible(true)) return mSizable::setGeometry(requestedGeometry), *this;
//if(!visible(true)) return mSizable::setGeometry(requestedGeometry), *this;
auto geometry = requestedGeometry;
geometry.setX(geometry.x() + padding().x());

View File

@@ -14,15 +14,17 @@ struct mHorizontalLayout : mSizable {
using mSizable::remove;
auto alignment() const -> maybe<float>;
auto append(sSizable sizable, Size size, float spacing = 5) -> type&;
auto append(sSizable sizable, Size size, float spacing = 5_sy) -> type&;
auto cell(uint position) const -> HorizontalLayoutCell;
auto cell(sSizable sizable) const -> HorizontalLayoutCell;
auto cells() const -> vector<HorizontalLayoutCell>;
auto cellCount() const -> uint;
auto minimumSize() const -> Size override;
auto padding() const -> Geometry;
auto remove(sSizable sizable) -> type&;
auto remove(sHorizontalLayoutCell cell) -> type&;
auto reset() -> type& override;
auto resize() -> type&;
auto setAlignment(maybe<float> alignment) -> type&;
auto setEnabled(bool enabled) -> type& override;
auto setFont(const Font& font) -> type& override;
@@ -41,7 +43,7 @@ private:
maybe<float> alignment;
vector<HorizontalLayoutCell> cells;
Geometry padding;
float spacing = 5;
float spacing = 5_sx;
} state;
};
@@ -70,7 +72,7 @@ private:
maybe<float> alignment;
sSizable sizable;
Size size;
float spacing = 5;
float spacing = 5_sx;
} state;
friend class mHorizontalLayout;

View File

@@ -0,0 +1,49 @@
#if defined(Hiro_HorizontalResizeGrip)
mHorizontalResizeGrip::mHorizontalResizeGrip() {
image icon;
icon.allocate(5, 15);
for(uint y : range(icon.height())) {
auto data = icon.data() + y * icon.pitch();
icon.write(data, 0x00000000); data += icon.stride();
icon.write(data, 0xff9f9f9f); data += icon.stride();
icon.write(data, 0x00000000); data += icon.stride();
icon.write(data, 0xff9f9f9f); data += icon.stride();
icon.write(data, 0x00000000); data += icon.stride();
}
mCanvas::setIcon(icon);
mCanvas::onMousePress([&](auto button) {
if(button == Mouse::Button::Left && !state.timer.enabled()) {
doActivate();
state.offset = 0;
state.origin = Mouse::position();
state.timer.setEnabled();
}
});
state.timer.setInterval(10).onActivate([&] {
if(!Mouse::pressed(Mouse::Button::Left)) return (void)state.timer.setEnabled(false);
auto position = Mouse::position();
auto offset = position.x() - state.origin.x();
if(offset != state.offset) doResize(offset), offset = state.offset;
});
}
auto mHorizontalResizeGrip::doActivate() const -> void {
if(state.onActivate) state.onActivate();
}
auto mHorizontalResizeGrip::doResize(int offset) const -> void {
if(state.onResize) state.onResize(offset);
}
auto mHorizontalResizeGrip::onActivate(const function<void ()>& callback) -> type& {
state.onActivate = callback;
return *this;
}
auto mHorizontalResizeGrip::onResize(const function<void (int)>& callback) -> type& {
state.onResize = callback;
return *this;
}
#endif

View File

@@ -0,0 +1,26 @@
#if defined(Hiro_HorizontalResizeGrip)
struct HorizontalResizeGrip;
struct mHorizontalResizeGrip;
using sHorizontalResizeGrip = shared_pointer<mHorizontalResizeGrip>;
struct mHorizontalResizeGrip : mCanvas {
using type = mHorizontalResizeGrip;
mHorizontalResizeGrip();
auto doActivate() const -> void;
auto doResize(int offset) const -> void;
auto onActivate(const function<void ()>& callback) -> type&;
auto onResize(const function<void (int)>& callback) -> type&;
//private:
struct State {
function<void ()> onActivate;
function<void (int)> onResize;
int offset = 0;
Position origin;
Timer timer;
} state;
};
#endif

View File

@@ -1,12 +1,9 @@
#if defined(Hiro_ListView)
mListView::mListView() {
mTableView::onActivate([&] {
doActivate();
});
mTableView::onChange([&] {
doChange();
});
mTableView::onActivate([&] { doActivate(); });
mTableView::onChange([&] { doChange(); });
mTableView::onContext([&] { doContext(); });
mTableView::onToggle([&](TableViewCell cell) {
if(auto item = cell->parentTableViewItem()) {
if(auto shared = item->instance.acquire()) {
@@ -32,6 +29,10 @@ auto mListView::doChange() const -> void {
if(state.onChange) state.onChange();
}
auto mListView::doContext() const -> void {
if(state.onContext) state.onContext();
}
auto mListView::doToggle(ListViewItem item) const -> void {
if(state.onToggle) state.onToggle(item);
}
@@ -57,6 +58,11 @@ auto mListView::onChange(const function<void ()>& callback) -> type& {
return *this;
}
auto mListView::onContext(const function<void ()>& callback) -> type& {
state.onContext = callback;
return *this;
}
auto mListView::onToggle(const function<void (ListViewItem)>& callback) -> type& {
state.onToggle = callback;
return *this;
@@ -68,10 +74,26 @@ auto mListView::reset() -> type& {
return *this;
}
auto mListView::resize() -> type& {
mTableView::resizeColumns();
return *this;
}
auto mListView::selected() const -> ListViewItem {
return ListViewItem{mTableView::selected()};
}
auto mListView::setVisible(bool visible) -> type& {
mTableView::setVisible(visible);
#if 0
if(visible) {
Application::processEvents(); //heavy-handed, but necessary for proper Widget geometry
mTableView::resizeColumns();
}
#endif
return *this;
}
//
mListViewItem::mListViewItem() {

View File

@@ -18,19 +18,24 @@ struct mListView : mTableView {
auto batched() const -> vector<ListViewItem>;
auto doActivate() const -> void;
auto doChange() const -> void;
auto doContext() const -> void;
auto doToggle(ListViewItem) const -> void;
auto item(uint position) const -> ListViewItem;
auto items() const -> vector<ListViewItem>;
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 (ListViewItem)>& callback) -> type&;
auto reset() -> type& override;
auto resize() -> type&;
auto selected() const -> ListViewItem;
auto setVisible(bool visible = true) -> type&;
//private:
struct State {
function<void ()> onActivate;
function<void ()> onChange;
function<void ()> onContext;
function<void (ListViewItem)> onToggle;
} state;
};

View File

@@ -4,6 +4,10 @@ MessageDialog::MessageDialog(const string& text) {
state.text = text;
}
auto MessageDialog::checked() const -> bool {
return state.checked;
}
auto MessageDialog::error(const vector<string>& buttons) -> string {
state.buttons = buttons;
state.icon = Icon::Prompt::Error;
@@ -22,8 +26,25 @@ auto MessageDialog::question(const vector<string>& buttons) -> string {
return _run();
}
auto MessageDialog::setParent(shared_pointer<mWindow> parent) -> type& {
state.parent = parent;
auto MessageDialog::setAlignment(Alignment alignment) -> type& {
state.alignment = alignment;
state.relativeTo = {};
return *this;
}
auto MessageDialog::setAlignment(sWindow relativeTo, Alignment alignment) -> type& {
state.alignment = alignment;
state.relativeTo = relativeTo;
return *this;
}
auto MessageDialog::setChecked(bool checked) -> type& {
state.checked = checked;
return *this;
}
auto MessageDialog::setOption(const string& option) -> type& {
state.option = option;
return *this;
}
@@ -44,23 +65,32 @@ auto MessageDialog::warning(const vector<string>& buttons) -> string {
}
auto MessageDialog::_run() -> string {
if(!state.buttons) return {}; //nothing to do
Application::Namespace tr{"MessageDialog"};
Window window;
VerticalLayout layout{&window};
HorizontalLayout messageLayout{&layout, Size{~0, 0}, 5};
VerticalLayout messageIconLayout{&messageLayout, Size{16, ~0}, 5};
Canvas messageIcon{&messageIconLayout, Size{16, 16}, 0};
Widget messageIconSpacer{&messageIconLayout, Size{16, ~0}};
HorizontalLayout messageLayout{&layout, Size{~0, 0}, 5_sy};
VerticalLayout messageIconLayout{&messageLayout, Size{16_sx, ~0}, 5_sx};
Canvas messageIcon{&messageIconLayout, Size{16_sx, 16_sy}, 0};
Widget messageIconSpacer{&messageIconLayout, Size{16_sx, ~0}};
Label messageText{&messageLayout, Size{~0, 0}};
Widget optionSpacer{&layout, Size{0, 0}};
CheckLabel optionText{&layout, Size{~0, 0}};
HorizontalLayout controlLayout{&layout, Size{~0, 0}};
Widget controlSpacer{&controlLayout, Size{~0, 0}};
layout.setPadding(5);
messageIcon.setIcon(state.icon);
layout.setPadding(5_sx, 5_sy);
image icon{state.icon};
icon.scale(16_sx, 16_sy);
messageIcon.setIcon(icon);
messageText.setText(state.text);
optionSpacer.setCollapsible().setVisible((bool)state.option);
optionText.setCollapsible().setChecked(state.checked).setText(state.option).setVisible((bool)state.option).onToggle([&] {
state.checked = optionText.checked();
});
for(auto n : range(state.buttons.size())) {
Button button{&controlLayout, Size{80, 0}, 5};
Button button{&controlLayout, Size{80_sx, 0}, 5_sx};
button.onActivate([&, n] {
state.response = state.buttons[n];
window.setModal(false);
@@ -69,15 +99,26 @@ auto MessageDialog::_run() -> string {
button.setFocused(); //the last button will have effective focus
}
int widthMessage = 5 + 16 + 5 + Font().size(state.text).width() + 5;
int widthButtons = 5 + state.buttons.size() * 85;
int width = max(320, widthMessage, widthButtons);
int widthMessage = 5_sx + 16 + 5_sx + Font().size(state.text).width() + 5_sx;
int widthButtons = 5_sx + state.buttons.size() * 85_sx;
int width = max(320_sx, widthMessage, widthButtons);
window.onClose([&] {
//if the dialog is dismissed (escape pressed, or window manager close button clicked),
//set the response to the last button shown (which is also the default selected button),
//and set a flag to indicate that the dialog was dismissed to the caller.
//note that the safest option should always be the last option in the buttons list.
if(!state.response) {
state.response = state.buttons.last();
state.dismissed = true;
}
window.setModal(false);
});
window.onClose([&] { window.setModal(false); });
window.setTitle(state.title);
window.setResizable(false);
window.setSize({width, layout.minimumSize().height()});
window.setCentered(state.parent);
window.setAlignment(state.relativeTo, state.alignment);
window.setDismissable();
window.setVisible();
window.setModal();

View File

@@ -4,19 +4,28 @@ struct MessageDialog {
using type = MessageDialog;
MessageDialog(const string& text = "");
auto checked() const -> bool;
auto dismissed() const -> bool;
auto error(const vector<string>& buttons = {"Ok"}) -> string;
auto information(const vector<string>& buttons = {"Ok"}) -> string;
auto question(const vector<string>& buttons = {"Yes", "No"}) -> string;
auto setParent(sWindow parent = {}) -> type&;
auto setAlignment(Alignment = Alignment::Center) -> type&;
auto setAlignment(sWindow relativeTo, Alignment = Alignment::Center) -> type&;
auto setChecked(bool checked = true) -> type&;
auto setOption(const string& option = "") -> type&;
auto setText(const string& text = "") -> type&;
auto setTitle(const string& title = "") -> type&;
auto warning(const vector<string>& buttons = {"Ok"}) -> string;
private:
struct State {
Alignment alignment = Alignment::Center;
vector<string> buttons;
bool checked = false;
bool dismissed = false;
image icon;
sWindow parent;
string option;
sWindow relativeTo;
string response;
string text;
string title;

View File

@@ -0,0 +1,80 @@
#if defined(Hiro_NameDialog)
NameDialog::NameDialog() {
layout.setPadding(5_sx, 5_sy);
typeIcon.setCollapsible();
nameValue.onActivate([&] { acceptButton.doActivate(); });
acceptButton.onActivate([&] {
response = nameValue.text();
window.doClose();
});
cancelButton.setText("Cancel").onActivate([&] { window.doClose(); });
window.onClose([&] {
window.setModal(false);
window.setVisible(false);
});
window.setDismissable();
}
auto NameDialog::create(string name) -> string {
return show("Create", name);
}
auto NameDialog::rename(string name) -> string {
return show("Rename", name);
}
auto NameDialog::setAlignment(Alignment alignment) -> type& {
state.alignment = alignment;
state.relativeTo = {};
return *this;
}
auto NameDialog::setAlignment(sWindow relativeTo, Alignment alignment) -> type& {
state.alignment = alignment;
state.relativeTo = relativeTo;
return *this;
}
auto NameDialog::setIcon(const image& icon) -> type& {
state.icon = icon;
return *this;
}
auto NameDialog::setText(const string& text) -> type& {
state.text = text;
return *this;
}
auto NameDialog::setTitle(const string& title) -> type& {
state.title = title;
return *this;
}
auto NameDialog::show(string mode, string name) -> string {
response = {};
setTitle(state.title);
if(!state.title && mode == "Create") setTitle("Create");
if(!state.title && mode == "Rename") setTitle({"Rename ", name});
textLabel.setText(state.text ? state.text : "Enter a name:");
if(state.icon) {
image icon{state.icon};
icon.scale(16_sx, 16_sy);
typeIcon.setIcon(icon);
} else {
typeIcon.setVisible(false);
}
nameValue.setText(name);
acceptButton.setText(mode);
window.setTitle(state.title);
window.setSize({400_sx, layout.minimumSize().height()});
window.setAlignment(state.relativeTo, state.alignment);
window.setVisible();
nameValue.setFocused();
window.setModal();
return response;
}
#endif

View File

@@ -0,0 +1,40 @@
#if defined(Hiro_NameDialog)
struct NameDialog {
using type = NameDialog;
NameDialog();
auto create(string name = "") -> string;
auto rename(string name) -> string;
auto setIcon(const image& icon = {}) -> type&;
auto setAlignment(Alignment = Alignment::Center) -> type&;
auto setAlignment(sWindow relativeTo, Alignment = Alignment::Center) -> type&;
auto setText(const string& text = {}) -> type&;
auto setTitle(const string& title = {}) -> type&;
private:
auto show(string mode, string name) -> string;
Window window;
VerticalLayout layout{&window};
Label textLabel{&layout, Size{~0, 0}};
HorizontalLayout nameLayout{&layout, Size{~0, 0}};
Canvas typeIcon{&nameLayout, Size{16_sx, 16_sy}};
LineEdit nameValue{&nameLayout, Size{~0, 0}};
HorizontalLayout controlLayout{&layout, Size{~0, 0}};
Widget controlSpacer{&controlLayout, Size{~0, 0}};
Button acceptButton{&controlLayout, Size{80, 0}};
Button cancelButton{&controlLayout, Size{80, 0}};
struct State {
Alignment alignment = Alignment::Center;
image icon;
sWindow relativeTo;
string text;
string title;
} state;
string response;
};
#endif

View File

@@ -14,10 +14,12 @@ struct FixedLayout : sFixedLayout {
auto append(sSizable sizable, Geometry geometry) { return self().append(sizable, geometry), *this; }
auto cell(uint position) const { return self().cell(position); }
auto cell(sSizable sizable) const { return self().cell(sizable); }
auto cells() const { return self().cells(); }
auto cellCount() const { return self().cellCount(); }
auto remove(sSizable sizable) { return self().remove(sizable), *this; }
auto remove(sFixedLayoutCell cell) { return self().remove(cell), *this; }
auto reset() { return self().reset(), *this; }
auto resize() { return self().resize(), *this; }
};
#endif
@@ -30,7 +32,7 @@ struct HorizontalLayoutCell : sHorizontalLayoutCell {
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; }
auto setSpacing(float spacing = 5) { return self().setSpacing(spacing), *this; }
auto setSpacing(float spacing = 5_sx) { return self().setSpacing(spacing), *this; }
auto sizable() const { return self().sizable(); }
auto size() const { return self().size(); }
auto spacing() const { return self().spacing(); }
@@ -40,17 +42,20 @@ struct HorizontalLayout : sHorizontalLayout {
DeclareSharedSizable(HorizontalLayout)
auto alignment() const { return self().alignment(); }
auto append(sSizable sizable, Size size, float spacing = 5) { return self().append(sizable, size, spacing), *this; }
auto append(sSizable sizable, Size size, float spacing = 5_sx) { return self().append(sizable, size, spacing), *this; }
auto cell(uint position) const { return self().cell(position); }
auto cell(sSizable sizable) const { return self().cell(sizable); }
auto cells() const { return self().cells(); }
auto cellCount() const { return self().cellCount(); }
auto remove(sSizable sizable) { return self().remove(sizable), *this; }
auto remove(sHorizontalLayoutCell cell) { return self().remove(cell), *this; }
auto reset() { return self().reset(), *this; }
auto resize() { return self().resize(), *this; }
auto setAlignment(maybe<float> alignment = {}) { return self().setAlignment(alignment), *this; }
auto setPadding(float padding) { return self().setPadding({padding, padding, padding, padding}), *this; }
auto setPadding(float x, float y) { return self().setPadding({x, y, x, y}), *this; }
auto setPadding(Geometry padding = {}) { return self().setPadding(padding), *this; }
auto setSpacing(float spacing = 5) { return self().setSpacing(spacing), *this; }
auto setSpacing(float spacing = 5_sx) { return self().setSpacing(spacing), *this; }
};
#endif
@@ -63,7 +68,7 @@ struct VerticalLayoutCell : sVerticalLayoutCell {
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; }
auto setSpacing(float spacing = 5) { return self().setSpacing(spacing), *this; }
auto setSpacing(float spacing = 5_sy) { return self().setSpacing(spacing), *this; }
auto sizable() const { return self().sizable(); }
auto size() const { return self().size(); }
auto spacing() const { return self().spacing(); }
@@ -73,17 +78,20 @@ struct VerticalLayout : sVerticalLayout {
DeclareSharedSizable(VerticalLayout)
auto alignment() const { return self().alignment(); }
auto append(sSizable sizable, Size size, float spacing = 5) { return self().append(sizable, size, spacing), *this; }
auto append(sSizable sizable, Size size, float spacing = 5_sy) { return self().append(sizable, size, spacing), *this; }
auto cell(uint position) const { return self().cell(position); }
auto cell(sSizable sizable) const { return self().cell(sizable); }
auto cells() const { return self().cells(); }
auto cellCount() const { return self().cellCount(); }
auto remove(sSizable sizable) { return self().remove(sizable), *this; }
auto remove(sVerticalLayoutCell cell) { return self().remove(cell), *this; }
auto reset() { return self().reset(), *this; }
auto resize() { return self().resize(), *this; }
auto setAlignment(maybe<float> alignment = {}) { return self().setAlignment(alignment), *this; }
auto setPadding(float padding) { return self().setPadding({padding, padding, padding, padding}), *this; }
auto setPadding(float x, float y) { return self().setPadding({x, y, x, y}), *this; }
auto setPadding(Geometry padding = {}) { return self().setPadding(padding), *this; }
auto setSpacing(float spacing = 5) { return self().setSpacing(spacing), *this; }
auto setSpacing(float spacing = 5_sy) { return self().setSpacing(spacing), *this; }
};
#endif
@@ -104,7 +112,7 @@ struct TableLayoutColumn : sTableLayoutColumn {
auto alignment() const { return self().alignment(); }
auto setAlignment(Alignment alignment = {}) { return self().setAlignment(alignment), *this; }
auto setSpacing(float spacing = 5) { return self().setSpacing(spacing), *this; }
auto setSpacing(float spacing = 5_sx) { return self().setSpacing(spacing), *this; }
auto spacing() const { return self().spacing(); }
};
@@ -113,7 +121,7 @@ struct TableLayoutRow : sTableLayoutRow {
auto alignment() const { return self().alignment(); }
auto setAlignment(Alignment alignment = {}) { return self().setAlignment(alignment), *this; }
auto setSpacing(float spacing = 5) { return self().setSpacing(spacing), *this; }
auto setSpacing(float spacing = 5_sy) { return self().setSpacing(spacing), *this; }
auto spacing() const { return self().spacing(); }
};
@@ -125,23 +133,50 @@ struct TableLayout : sTableLayout {
auto cell(uint position) const { return self().cell(position); }
auto cell(uint x, uint y) const { return self().cell(x, y); }
auto cell(sSizable sizable) const { return self().cell(sizable); }
auto cells() const { return self().cells(); }
auto cellCount() const { return self().cellCount(); }
auto column(uint position) const { return self().column(position); }
auto columns() const { return self().columns(); }
auto columnCount() const { return self().columnCount(); }
auto padding() const { return self().padding(); }
auto remove(sSizable sizable) { return self().remove(sizable), *this; }
auto remove(sTableLayoutCell cell) { return self().remove(cell), *this; }
auto reset() { return self().reset(), *this; }
auto resize() { return self().resize(), *this; }
auto row(uint position) const { return self().row(position); }
auto rows() const { return self().rows(); }
auto rowCount() const { return self().rowCount(); }
auto setAlignment(Alignment alignment = {}) { return self().setAlignment(alignment), *this; }
auto setPadding(float padding) { return self().setPadding({padding, padding, padding, padding}), *this; }
auto setPadding(float x, float y) { return self().setPadding({x, y, x, y}), *this; }
auto setPadding(Geometry padding = {}) { return self().setPadding(padding), *this; }
auto setSize(Size size) { return self().setSize(size), *this; }
auto size() const { return self().size(); }
};
#endif
#if defined(Hiro_HorizontalResizeGrip)
struct HorizontalResizeGrip : sHorizontalResizeGrip {
DeclareSharedWidget(HorizontalResizeGrip)
auto doActivate() const { return self().doActivate(); }
auto doResize(int offset) const { return self().doResize(offset); }
auto onActivate(const function<void ()>& callback) { return self().onActivate(callback), *this; }
auto onResize(const function<void (int)>& callback) { return self().onResize(callback), *this; }
};
#endif
#if defined(Hiro_VerticalResizeGrip)
struct VerticalResizeGrip : sVerticalResizeGrip {
DeclareSharedWidget(VerticalResizeGrip)
auto doActivate() const { return self().doActivate(); }
auto doResize(int offset) const { return self().doResize(offset); }
auto onActivate(const function<void ()>& callback) { return self().onActivate(callback), *this; }
auto onResize(const function<void (int)>& callback) { return self().onResize(callback), *this; }
};
#endif
#if defined(Hiro_ListView)
struct ListViewItem : sListViewItem {
DeclareSharedObject(ListViewItem)
@@ -153,12 +188,14 @@ struct ListViewItem : sListViewItem {
auto foregroundColor() const { return self().foregroundColor(); }
auto icon() const { return self().icon(); }
auto reset() { return self().reset(), *this; }
auto selected() const { return self().selected(); }
auto setAlignment(Alignment alignment = {}) { return self().setAlignment(alignment), *this; }
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 setForegroundColor(Color color = {}) { return self().setForegroundColor(color), *this; }
auto setIcon(const image& icon = {}) { return self().setIcon(icon), *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(); }
};
@@ -185,6 +222,7 @@ struct ListView : sListView {
auto onToggle(const function<void (ListViewItem)>& callback = {}) { return self().onToggle(callback), *this; }
auto remove(sListViewItem item) { return self().remove(item), *this; }
auto reset() { return self().reset(), *this; }
auto resize() { return self().resize(), *this; }
auto selected() { return self().selected(); }
auto setAlignment(Alignment alignment = {}) { return self().setAlignment(alignment), *this; }
auto setBackgroundColor(Color color = {}) { return self().setBackgroundColor(color), *this; }

View File

@@ -33,6 +33,10 @@ auto mTableLayout::cell(sSizable sizable) const -> TableLayoutCell {
return {};
}
auto mTableLayout::cells() const -> vector<TableLayoutCell> {
return state.cells;
}
auto mTableLayout::cellCount() const -> uint {
return state.cells.size();
}
@@ -41,6 +45,10 @@ auto mTableLayout::column(uint position) const -> TableLayoutColumn {
return state.columns(position, {});
}
auto mTableLayout::columns() const -> vector<TableLayoutColumn> {
return state.columns;
}
auto mTableLayout::columnCount() const -> uint {
return state.columns.size();
}
@@ -118,10 +126,19 @@ auto mTableLayout::reset() -> type& {
return synchronize();
}
auto mTableLayout::resize() -> type& {
setGeometry(geometry());
return *this;
}
auto mTableLayout::row(uint position) const -> TableLayoutRow {
return state.rows(position, {});
}
auto mTableLayout::rows() const -> vector<TableLayoutRow> {
return state.rows;
}
auto mTableLayout::rowCount() const -> uint {
return state.rows.size();
}
@@ -144,7 +161,7 @@ auto mTableLayout::setFont(const Font& font) -> type& {
}
auto mTableLayout::setGeometry(Geometry requestedGeometry) -> type& {
if(!visible(true)) return mSizable::setGeometry(requestedGeometry), *this;
//if(!visible(true)) return mSizable::setGeometry(requestedGeometry), *this;
auto geometry = requestedGeometry;
geometry.setX(geometry.x() + padding().x());

View File

@@ -24,15 +24,19 @@ struct mTableLayout : mSizable {
auto cell(uint position) const -> TableLayoutCell;
auto cell(uint x, uint y) const -> TableLayoutCell;
auto cell(sSizable sizable) const -> TableLayoutCell;
auto cells() const -> vector<TableLayoutCell>;
auto cellCount() const -> uint;
auto column(uint position) const -> TableLayoutColumn;
auto columns() const -> vector<TableLayoutColumn>;
auto columnCount() const -> uint;
auto minimumSize() const -> Size override;
auto padding() const -> Geometry;
auto remove(sSizable sizable) -> type&;
auto remove(sTableLayoutCell cell) -> type&;
auto reset() -> type&;
auto reset() -> type& override;
auto resize() -> type&;
auto row(uint position) const -> TableLayoutRow;
auto rows() const -> vector<TableLayoutRow>;
auto rowCount() const -> uint;
auto setAlignment(Alignment alignment) -> type&;
auto setEnabled(bool enabled) -> type& override;
@@ -70,7 +74,7 @@ struct mTableLayoutColumn : mObject {
private:
struct State {
Alignment alignment;
float spacing = 5;
float spacing = 5_sx;
} state;
friend class mTableLayout;
@@ -88,7 +92,7 @@ struct mTableLayoutRow : mObject {
private:
struct State {
Alignment alignment;
float spacing = 5;
float spacing = 5_sy;
} state;
friend class mTableLayout;

View File

@@ -29,6 +29,10 @@ auto mVerticalLayout::cell(sSizable sizable) const -> VerticalLayoutCell {
return {};
}
auto mVerticalLayout::cells() const -> vector<VerticalLayoutCell> {
return state.cells;
}
auto mVerticalLayout::cellCount() const -> uint {
return state.cells.size();
}
@@ -95,6 +99,11 @@ auto mVerticalLayout::reset() -> type& {
return synchronize();
}
auto mVerticalLayout::resize() -> type& {
setGeometry(geometry());
return *this;
}
auto mVerticalLayout::setAlignment(maybe<float> alignment) -> type& {
state.alignment = alignment;
return synchronize();
@@ -113,7 +122,7 @@ auto mVerticalLayout::setFont(const Font& font) -> type& {
}
auto mVerticalLayout::setGeometry(Geometry requestedGeometry) -> type& {
if(!visible(true)) return mSizable::setGeometry(requestedGeometry), *this;
//if(!visible(true)) return mSizable::setGeometry(requestedGeometry), *this;
auto geometry = requestedGeometry;
geometry.setX(geometry.x() + padding().x());

View File

@@ -14,15 +14,17 @@ struct mVerticalLayout : mSizable {
using mSizable::remove;
auto alignment() const -> maybe<float>;
auto append(sSizable sizable, Size size, float spacing = 5) -> type&;
auto append(sSizable sizable, Size size, float spacing = 5_sy) -> type&;
auto cell(uint position) const -> VerticalLayoutCell;
auto cell(sSizable sizable) const -> VerticalLayoutCell;
auto cells() const -> vector<VerticalLayoutCell>;
auto cellCount() const -> uint;
auto minimumSize() const -> Size override;
auto padding() const -> Geometry;
auto remove(sSizable sizable) -> type&;
auto remove(sVerticalLayoutCell cell) -> type&;
auto reset() -> type& override;
auto resize() -> type&;
auto setAlignment(maybe<float> alignment) -> type&;
auto setEnabled(bool enabled) -> type& override;
auto setFont(const Font& font) -> type& override;
@@ -41,7 +43,7 @@ private:
maybe<float> alignment;
vector<VerticalLayoutCell> cells;
Geometry padding;
float spacing = 5;
float spacing = 5_sy;
} state;
};
@@ -70,7 +72,7 @@ private:
maybe<float> alignment;
sSizable sizable;
Size size;
float spacing = 5;
float spacing = 5_sy;
} state;
friend class mVerticalLayout;

View File

@@ -0,0 +1,49 @@
#if defined(Hiro_VerticalResizeGrip)
mVerticalResizeGrip::mVerticalResizeGrip() {
image icon;
icon.allocate(15, 5);
for(uint x : range(icon.width())) {
auto data = icon.data() + x * icon.stride();
icon.write(data, 0x00000000); data += icon.pitch();
icon.write(data, 0xff9f9f9f); data += icon.pitch();
icon.write(data, 0x00000000); data += icon.pitch();
icon.write(data, 0xff9f9f9f); data += icon.pitch();
icon.write(data, 0x00000000); data += icon.pitch();
}
mCanvas::setIcon(icon);
mCanvas::onMousePress([&](auto button) {
if(button == Mouse::Button::Left && !state.timer.enabled()) {
doActivate();
state.offset = 0;
state.origin = Mouse::position();
state.timer.setEnabled();
}
});
state.timer.setInterval(10).onActivate([&] {
if(!Mouse::pressed(Mouse::Button::Left)) return (void)state.timer.setEnabled(false);
auto position = Mouse::position();
auto offset = position.y() - state.origin.y();
if(offset != state.offset) doResize(offset), offset = state.offset;
});
}
auto mVerticalResizeGrip::doActivate() const -> void {
if(state.onActivate) state.onActivate();
}
auto mVerticalResizeGrip::doResize(int offset) const -> void {
if(state.onResize) state.onResize(offset);
}
auto mVerticalResizeGrip::onActivate(const function<void ()>& callback) -> type& {
state.onActivate = callback;
return *this;
}
auto mVerticalResizeGrip::onResize(const function<void (int)>& callback) -> type& {
state.onResize = callback;
return *this;
}
#endif

View File

@@ -0,0 +1,26 @@
#if defined(Hiro_VerticalResizeGrip)
struct VerticalResizeGrip;
struct mVerticalResizeGrip;
using sVerticalResizeGrip = shared_pointer<mVerticalResizeGrip>;
struct mVerticalResizeGrip : mCanvas {
using type = mVerticalResizeGrip;
mVerticalResizeGrip();
auto doActivate() const -> void;
auto doResize(int offset) const -> void;
auto onActivate(const function<void ()>& callback) -> type&;
auto onResize(const function<void (int)>& callback) -> type&;
//private:
struct State {
function<void ()> onActivate;
function<void (int)> onResize;
int offset = 0;
Position origin;
Timer timer;
} state;
};
#endif