Update to v106r47 release.

byuu says:

This is probably the largest code-change diff I've done in years.

I spent four days working 10-16 hours a day reworking layouts in hiro
completely.

The result is we now have TableLayout, which will allow for better
horizontal+vertical combined alignment.

Windows, GTK2, and now GTK3 are fully supported.

Windows is getting the initial window geometry wrong by a bit.

GTK2 and GTK3 work perfectly. I basically abandoned trying to detect
resize signals, and instead keep a list of all hiro windows that are
allocated, and every time the main loop runs, it will query all of them
to see if they've been resized. I'm disgusted that I have to do this,
but after fighting with GTK for years, I'm about sick of it. GTK was
doing this crazy thing where it would trigger another size-allocate
inside of a previous size-allocate, and so my layouts would be halfway
through resizing all the widgets, and then the size-allocate would kick
off another one. That would end up leaving the rest of the first layout
loop with bad widget sizes. And if I detected a second re-entry and
blocked it, then the entire window would end up with the older geometry.
I started trying to build a message queue system to allow the second
layout resize to occur after the first one completed, but this was just
too much madness, so I went with the simpler solution.

Qt4 has some geometry problems, and doesn't show tab frame layouts
properly yet.

Qt5 causes an ICE error and tanks my entire Xorg display server, so ...
something is seriously wrong there, and it's not hiro's fault. Creating
a dummy Qt5 application without even using hiro, just int main() {
TestObject object; } with object performing a dynamic\_cast to a derived
type segfaults. Memory is getting corrupted where GCC allocates the
vtables for classes, just by linking in Qt. Could be somehow related to
the -fPIC requirement that only Qt5 has ... could just be that FreeBSD
10.1 has a buggy implementation of Qt5. I don't know. It's beyond my
ability to debug, so this one's going to stay broken.

The Cocoa port is busted. I'll fix it up to compile again, but that's
about all I'm going to do.

Many optimizations mean bsnes and higan open faster. GTK2 and GTK3 both
resize windows very quickly now.

higan crashes when you load a game, so that's not good. bsnes works
though.

bsnes also has the start of a localization engine now. Still a long way
to go.

The makefiles received a rather substantial restructuring. Including the
ruby and hiro makefiles will add the necessary compilation rules for
you, which also means that moc will run for the qt4 and qt5 targets, and
windres will run for the Windows targets.
This commit is contained in:
Tim Allen
2018-07-14 13:59:29 +10:00
parent 0c55796060
commit 6090c63958
207 changed files with 2864 additions and 1667 deletions

View File

@@ -149,7 +149,7 @@ auto BrowserDialogWindow::isMatch(const string& name) -> bool {
auto BrowserDialogWindow::run() -> BrowserDialog::Response {
response = {};
layout.setMargin(5);
layout.setPadding(5);
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()); });

View File

@@ -6,7 +6,7 @@ namespace hiro {
#include "fixed-layout.cpp"
#include "horizontal-layout.cpp"
#include "vertical-layout.cpp"
#include "list-view-item.cpp"
#include "table-layout.cpp"
#include "list-view.cpp"
#include "browser-dialog.cpp"
#include "message-dialog.cpp"

View File

@@ -3,7 +3,7 @@ namespace hiro {
#include "fixed-layout.hpp"
#include "horizontal-layout.hpp"
#include "vertical-layout.hpp"
#include "list-view-item.hpp"
#include "table-layout.hpp"
#include "list-view.hpp"
#include "shared.hpp"
#include "browser-dialog.hpp"

View File

@@ -1,61 +1,142 @@
#if defined(Hiro_FixedLayout)
auto mFixedLayout::append(sSizable sizable, Geometry geometry) -> type& {
properties.append({geometry});
mLayout::append(sizable);
sizable->setGeometry(geometry);
return *this;
FixedLayoutCell cell;
cell->setSizable(sizable);
cell->setGeometry(geometry);
cell->setParent(this, cellCount());
state.cells.append(cell);
return synchronize();
}
auto mFixedLayout::modify(sSizable sizable, Geometry geometry) -> type& {
if(sizable && this->sizable(sizable->offset()) == sizable) {
auto& properties = this->properties[sizable->offset()];
properties.geometry = geometry;
auto mFixedLayout::cell(uint position) const -> FixedLayoutCell {
return state.cells(position, {});
}
auto mFixedLayout::cell(sSizable sizable) const -> FixedLayoutCell {
for(auto& cell : state.cells) {
if(cell->state.sizable == sizable) return cell;
}
return *this;
return {};
}
auto mFixedLayout::cellCount() const -> uint {
return state.cells.size();
}
auto mFixedLayout::destruct() -> void {
for(auto& cell : state.cells) cell->destruct();
return mSizable::destruct();
}
auto mFixedLayout::minimumSize() const -> Size {
float width = Size::Minimum, height = Size::Minimum;
for(auto n : range(sizableCount())) {
width = max(width, sizable(n)->minimumSize().width());
height = max(height, sizable(n)->minimumSize().height());
float width = 0, height = 0;
for(auto& cell : state.cells) {
width = max(width, cell.sizable().minimumSize().width());
height = max(height, cell.sizable().minimumSize().height());
}
return {width, height};
}
auto mFixedLayout::remove(sSizable sizable) -> type& {
properties.remove(sizable->offset());
mLayout::remove(sizable);
return *this;
auto mFixedLayout::remove(sFixedLayoutCell cell) -> type& {
if(cell->parent() != this) return *this;
auto offset = cell->offset();
cell->setParent();
state.cells.remove(offset);
for(uint n : range(offset, cellCount())) state.cells[n]->adjustOffset(-1);
return synchronize();
}
auto mFixedLayout::reset() -> type& {
mLayout::reset();
properties.reset();
return *this;
while(state.cells) remove(state.cells.right());
return synchronize();
}
auto mFixedLayout::setEnabled(bool enabled) -> type& {
mLayout::setEnabled(enabled);
for(auto n : range(sizableCount())) {
sizable(n)->setEnabled(sizable(n)->enabled());
}
mSizable::setEnabled(enabled);
for(auto& cell : state.cells) cell.sizable().setEnabled(cell.sizable().enabled());
return *this;
}
auto mFixedLayout::setFont(const Font& font) -> type& {
mLayout::setFont(font);
for(auto n : range(sizableCount())) {
sizable(n)->setFont(sizable(n)->font());
}
mSizable::setFont(font);
for(auto& cell : state.cells) cell.sizable().setFont(cell.sizable().font());
return *this;
}
auto mFixedLayout::setParent(mObject* parent, int offset) -> type& {
for(auto& cell : reverse(state.cells)) cell->destruct();
mSizable::setParent(parent, offset);
for(auto& cell : state.cells) cell->setParent(this, cell->offset());
return *this;
}
auto mFixedLayout::setVisible(bool visible) -> type& {
mLayout::setVisible(visible);
for(auto n : range(sizableCount())) {
sizable(n)->setVisible(sizable(n)->visible());
mSizable::setVisible(visible);
for(auto& cell : state.cells) cell.sizable().setVisible(cell.sizable().visible());
return synchronize();
}
auto mFixedLayout::synchronize() -> type& {
setGeometry(geometry());
return *this;
}
//
auto mFixedLayoutCell::destruct() -> void {
if(auto& sizable = state.sizable) sizable->destruct();
mObject::destruct();
}
auto mFixedLayoutCell::geometry() const -> Geometry {
return state.geometry;
}
auto mFixedLayoutCell::setEnabled(bool enabled) -> type& {
mObject::setEnabled(enabled);
state.sizable->setEnabled(state.sizable->enabled());
return *this;
}
auto mFixedLayoutCell::setFont(const Font& font) -> type& {
mObject::setFont(font);
state.sizable->setFont(state.sizable->font());
return *this;
}
auto mFixedLayoutCell::setGeometry(Geometry geometry) -> type& {
state.geometry = geometry;
return synchronize();
}
auto mFixedLayoutCell::setParent(mObject* parent, int offset) -> type& {
state.sizable->destruct();
mObject::setParent(parent, offset);
state.sizable->setParent(this, 0);
return *this;
}
auto mFixedLayoutCell::setSizable(sSizable sizable) -> type& {
state.sizable = sizable;
return synchronize();
}
auto mFixedLayoutCell::setVisible(bool visible) -> type& {
mObject::setVisible(visible);
state.sizable->setVisible(state.sizable->visible());
return *this;
}
auto mFixedLayoutCell::sizable() const -> Sizable {
return state.sizable;
}
auto mFixedLayoutCell::synchronize() -> type& {
if(auto parent = this->parent()) {
if(auto fixedLayout = dynamic_cast<mFixedLayout*>(parent)) {
fixedLayout->synchronize();
}
}
return *this;
}

View File

@@ -1,23 +1,61 @@
#if defined(Hiro_FixedLayout)
struct mFixedLayout : mLayout {
struct FixedLayout;
struct FixedLayoutCell;
struct mFixedLayout;
struct mFixedLayoutCell;
using sFixedLayout = shared_pointer<mFixedLayout>;
using sFixedLayoutCell = shared_pointer<mFixedLayoutCell>;
struct mFixedLayout : mSizable {
using type = mFixedLayout;
using mLayout::append;
using mLayout::remove;
using mSizable::remove;
auto append(sSizable sizable, Geometry geometry) -> type&;
auto modify(sSizable sizable, Geometry geometry) -> type&;
auto cell(uint position) const -> FixedLayoutCell;
auto cell(sSizable sizable) const -> FixedLayoutCell;
auto cellCount() const -> uint;
auto minimumSize() const -> Size override;
auto remove(sSizable sizable) -> type& override;
auto remove(sFixedLayoutCell cell) -> type&;
auto reset() -> type& override;
auto setEnabled(bool enabled = true) -> type& override;
auto setFont(const Font& font = {}) -> type& override;
auto setVisible(bool visible = true) ->type& override;
auto setEnabled(bool enabled) -> type& override;
auto setFont(const Font& font) -> type& override;
auto setParent(mObject* parent = nullptr, int offset = -1) -> type& override;
auto setVisible(bool visible) ->type& override;
auto synchronize() -> type&;
struct Property {
private:
auto destruct() -> void override;
struct State {
vector<FixedLayoutCell> cells;
} state;
};
struct mFixedLayoutCell : mObject {
using type = mFixedLayoutCell;
auto geometry() const -> Geometry;
auto setEnabled(bool enabled) -> type& override;
auto setFont(const Font& font) -> type& override;
auto setGeometry(Geometry geometry) -> type&;
auto setParent(mObject* parent = nullptr, int offset = -1) -> type& override;
auto setSizable(sSizable sizable) -> type&;
auto setVisible(bool visible) -> type& override;
auto sizable() const -> Sizable;
auto synchronize() -> type&;
private:
auto destruct() -> void override;
struct State {
Geometry geometry;
};
vector<Property> properties;
sSizable sizable;
} state;
friend class mFixedLayout;
};
#endif

View File

@@ -1,149 +1,280 @@
#if defined(Hiro_HorizontalLayout)
auto mHorizontalLayout::append(sSizable sizable, Size size, float spacing) -> type& {
properties.append({size.width(), size.height(), spacing < 0 ? settings.spacing : spacing});
mLayout::append(sizable);
return *this;
auto mHorizontalLayout::alignment() const -> maybe<float> {
return state.alignment;
}
auto mHorizontalLayout::modify(sSizable sizable, Size size, float spacing) -> type& {
if(sizable && this->sizable(sizable->offset()) == sizable) {
auto& properties = this->properties[sizable->offset()];
properties.setWidth(size.width());
properties.setHeight(size.height());
properties.setSpacing(spacing);
auto mHorizontalLayout::append(sSizable sizable, Size size, float spacing) -> type& {
HorizontalLayoutCell cell;
cell->setSizable(sizable);
cell->setSize(size);
cell->setSpacing(spacing);
cell->setParent(this, cellCount());
state.cells.append(cell);
return synchronize();
}
auto mHorizontalLayout::cell(uint position) const -> HorizontalLayoutCell {
return state.cells(position, {});
}
auto mHorizontalLayout::cell(sSizable sizable) const -> HorizontalLayoutCell {
for(auto& cell : state.cells) {
if(cell->state.sizable == sizable) return cell;
}
return *this;
return {};
}
auto mHorizontalLayout::cellCount() const -> uint {
return state.cells.size();
}
auto mHorizontalLayout::destruct() -> void {
for(auto& cell : state.cells) cell->destruct();
mSizable::destruct();
}
auto mHorizontalLayout::minimumSize() const -> Size {
float width = 0, height = 0;
for(auto n : range(sizableCount())) {
auto& child = properties[sizable(n)->offset()];
if(child.width() == Size::Minimum || child.width() == Size::Maximum) {
width += sizable(n)->minimumSize().width();
float width = 0;
for(auto index : range(cellCount())) {
auto cell = this->cell(index);
if(cell.size().width() == Size::Minimum || cell.size().width() == Size::Maximum) {
width += cell.sizable().minimumSize().width();
} else {
width += child.width();
width += cell.size().width();
}
if(&child != &properties.right()) width += child.spacing();
if(index != cellCount() - 1) width += cell.spacing();
}
for(auto n : range(sizableCount())) {
auto& child = properties[sizable(n)->offset()];
if(child.height() == Size::Minimum || child.height() == Size::Maximum) {
height = max(height, sizable(n)->minimumSize().height());
float height = 0;
for(auto index : range(cellCount())) {
auto cell = this->cell(index);
if(cell.size().height() == Size::Minimum || cell.size().height() == Size::Maximum) {
height = max(height, cell.sizable().minimumSize().height());
continue;
}
height = max(height, child.height());
height = max(height, cell.size().height());
}
return {
settings.padding.x() + width + settings.padding.width(),
settings.padding.y() + height + settings.padding.height()
padding().x() + width + padding().width(),
padding().y() + height + padding().height()
};
}
auto mHorizontalLayout::remove(sSizable sizable) -> type& {
properties.remove(sizable->offset());
mLayout::remove(sizable);
return *this;
auto mHorizontalLayout::padding() const -> Geometry {
return state.padding;
}
auto mHorizontalLayout::remove(sHorizontalLayoutCell cell) -> type& {
if(cell->parent() != this) return *this;
auto offset = cell->offset();
cell->setParent();
state.cells.remove(offset);
for(uint n : range(offset, cellCount())) state.cells[n]->adjustOffset(-1);
return synchronize();
}
auto mHorizontalLayout::reset() -> type& {
mLayout::reset();
properties.reset();
return *this;
while(state.cells) remove(state.cells.right());
return synchronize();
}
auto mHorizontalLayout::setAlignment(float alignment) -> type& {
settings.alignment = max(0.0, min(1.0, alignment));
return *this;
auto mHorizontalLayout::setAlignment(maybe<float> alignment) -> type& {
state.alignment = alignment;
return synchronize();
}
auto mHorizontalLayout::setEnabled(bool enabled) -> type& {
mLayout::setEnabled(enabled);
for(auto n : range(sizableCount())) {
sizable(n)->setEnabled(sizable(n)->enabled());
}
mSizable::setEnabled(enabled);
for(auto& cell : state.cells) cell.sizable().setEnabled(cell.sizable().enabled());
return *this;
}
auto mHorizontalLayout::setFont(const Font& font) -> type& {
mLayout::setFont(font);
for(auto n : range(sizableCount())) {
sizable(n)->setFont(sizable(n)->font());
}
mSizable::setFont(font);
for(auto& cell : state.cells) cell.sizable().setFont(cell.sizable().font());
return *this;
}
auto mHorizontalLayout::setGeometry(Geometry containerGeometry) -> type& {
mLayout::setGeometry(containerGeometry);
auto mHorizontalLayout::setGeometry(Geometry geometry) -> type& {
mSizable::setGeometry(geometry);
auto window = parentWindow(true);
if(!window || !window->visible()) return *this;
auto properties = this->properties;
for(auto n : range(sizableCount())) {
auto& child = properties[sizable(n)->offset()];
if(child.width() == Size::Minimum) child.setWidth(sizable(n)->minimumSize().width());
if(child.height() == Size::Minimum) child.setHeight(sizable(n)->minimumSize().height());
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());
vector<float> widths;
widths.resize(cellCount());
uint maximumWidths = 0;
for(auto index : range(cellCount())) {
auto cell = this->cell(index);
float width = 0;
if(cell.size().width() == Size::Maximum) {
width = Size::Maximum;
maximumWidths++;
} else if(cell.size().width() == Size::Minimum) {
width = cell.sizable().minimumSize().width();
} else {
width = cell.size().width();
}
widths[index] = width;
}
Geometry geometry = containerGeometry;
geometry.setX (geometry.x() + settings.padding.x());
geometry.setY (geometry.y() + settings.padding.y());
geometry.setWidth (geometry.width() - settings.padding.x() - settings.padding.width());
geometry.setHeight(geometry.height() - settings.padding.y() - settings.padding.height());
float minimumWidth = 0, maximumWidthCounter = 0;
for(auto& child : properties) {
if(child.width() == Size::Maximum) maximumWidthCounter++;
if(child.width() != Size::Maximum) minimumWidth += child.width();
if(&child != &properties.right()) minimumWidth += child.spacing();
float fixedWidth = 0;
for(uint index : range(state.cells.size())) {
if(widths[index] != Size::Maximum) fixedWidth += widths[index];
if(index != cellCount() - 1) fixedWidth += cell(index).spacing();
}
for(auto& child : properties) {
if(child.width() == Size::Maximum) child.setWidth((geometry.width() - minimumWidth) / maximumWidthCounter);
if(child.height() == Size::Maximum) child.setHeight(geometry.height());
float maximumWidth = (geometry.width() - fixedWidth) / maximumWidths;
for(auto& width : widths) {
if(width == Size::Maximum) width = maximumWidth;
}
float maximumHeight = 0;
for(auto& child : properties) maximumHeight = max(maximumHeight, child.height());
for(auto n : range(sizableCount())) {
auto& child = properties[sizable(n)->offset()];
float pivot = (maximumHeight - child.height()) * settings.alignment;
Geometry childGeometry = {geometry.x(), geometry.y() + pivot, child.width(), child.height()};
if(childGeometry.width() < 1) childGeometry.setWidth (1);
if(childGeometry.height() < 1) childGeometry.setHeight(1);
sizable(n)->setGeometry(childGeometry);
geometry.setX (geometry.x() + child.width() + child.spacing());
geometry.setWidth(geometry.width() - child.width() + child.spacing());
float height = 0;
for(auto index : range(cellCount())) {
auto cell = this->cell(index);
if(cell.size().height() == Size::Maximum) {
height = geometry.height();
break;
} else if(cell.size().height() == Size::Minimum) {
height = max(height, cell.sizable().minimumSize().height());
} else {
height = max(height, cell.size().height());
}
}
return *this;
}
float geometryX = geometry.x();
float geometryY = geometry.y();
for(auto index : range(cellCount())) {
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;
float cellWidth = geometryWidth;
float cellHeight = cell.size().height();
if(cellHeight == Size::Minimum) cellHeight = cell.sizable()->minimumSize().height();
if(cellHeight == Size::Maximum) cellHeight = geometryHeight;
float cellX = geometryX;
float cellY = geometryY + alignment() * (geometryHeight - cellHeight);
cell.sizable().setGeometry({cellX, cellY, cellWidth, cellHeight});
geometryX += geometryWidth + cell.spacing();
}
auto mHorizontalLayout::setMargin(float margin) -> type& {
setPadding({margin, margin, margin, margin});
return *this;
}
auto mHorizontalLayout::setPadding(Geometry padding) -> type& {
settings.padding = padding;
setGeometry(geometry());
state.padding = padding;
return synchronize();
}
auto mHorizontalLayout::setParent(mObject* parent, int offset) -> type& {
for(auto& cell : reverse(state.cells)) cell->destruct();
mSizable::setParent(parent, offset);
for(auto& cell : state.cells) cell->setParent(this, cell->offset());
return *this;
}
auto mHorizontalLayout::setSpacing(float spacing) -> type& {
settings.spacing = spacing;
state.spacing = spacing;
return synchronize();
}
auto mHorizontalLayout::setVisible(bool visible) -> type& {
mSizable::setVisible(visible);
for(auto& cell : state.cells) cell.sizable().setVisible(cell.sizable().visible());
return synchronize();
}
auto mHorizontalLayout::spacing() const -> float {
return state.spacing;
}
auto mHorizontalLayout::synchronize() -> type& {
setGeometry(geometry());
return *this;
}
auto mHorizontalLayout::setVisible(bool visible) -> type& {
mLayout::setVisible(visible);
for(auto n : range(sizableCount())) {
sizable(n)->setVisible(sizable(n)->visible());
//
auto mHorizontalLayoutCell::alignment() const -> maybe<float> {
return state.alignment;
}
auto mHorizontalLayoutCell::destruct() -> void {
if(auto& sizable = state.sizable) sizable->destruct();
mObject::destruct();
}
auto mHorizontalLayoutCell::setAlignment(maybe<float> alignment) -> type& {
state.alignment = alignment;
return synchronize();
}
auto mHorizontalLayoutCell::setEnabled(bool enabled) -> type& {
mObject::setEnabled(enabled);
state.sizable->setEnabled(state.sizable->enabled());
return *this;
}
auto mHorizontalLayoutCell::setFont(const Font& font) -> type& {
mObject::setFont(font);
state.sizable->setFont(state.sizable->font());
return *this;
}
auto mHorizontalLayoutCell::setParent(mObject* parent, int offset) -> type& {
state.sizable->destruct();
mObject::setParent(parent, offset);
state.sizable->setParent(this, 0);
return *this;
}
auto mHorizontalLayoutCell::setSizable(sSizable sizable) -> type& {
state.sizable = sizable;
return synchronize();
}
auto mHorizontalLayoutCell::setSize(Size size) -> type& {
state.size = size;
return synchronize();
}
auto mHorizontalLayoutCell::setSpacing(float spacing) -> type& {
state.spacing = spacing;
return synchronize();
}
auto mHorizontalLayoutCell::setVisible(bool visible) -> type& {
mObject::setVisible(visible);
state.sizable->setVisible(state.sizable->visible());
return *this;
}
auto mHorizontalLayoutCell::sizable() const -> Sizable {
return state.sizable;
}
auto mHorizontalLayoutCell::size() const -> Size {
return state.size;
}
auto mHorizontalLayoutCell::spacing() const -> float {
return state.spacing;
}
auto mHorizontalLayoutCell::synchronize() -> type& {
if(auto parent = this->parent()) {
if(auto horizontalLayout = dynamic_cast<mHorizontalLayout*>(parent)) {
horizontalLayout->synchronize();
}
}
return *this;
}

View File

@@ -1,38 +1,77 @@
#if defined(Hiro_HorizontalLayout)
struct mHorizontalLayout : mLayout {
struct HorizontalLayout;
struct HorizontalLayoutCell;
struct mHorizontalLayout;
struct mHorizontalLayoutCell;
using sHorizontalLayout = shared_pointer<mHorizontalLayout>;
using sHorizontalLayoutCell = shared_pointer<mHorizontalLayoutCell>;
struct mHorizontalLayout : mSizable {
using type = mHorizontalLayout;
using mLayout::append;
using mLayout::remove;
using mSizable::remove;
auto alignment() const -> maybe<float>;
auto append(sSizable sizable, Size size, float spacing = 5) -> type&;
auto cell(uint position) const -> HorizontalLayoutCell;
auto cell(sSizable sizable) const -> HorizontalLayoutCell;
auto cellCount() const -> uint;
auto minimumSize() const -> Size override;
auto modify(sSizable sizable, Size size, float spacing = 5) -> type&;
auto remove(sSizable sizable) -> type& override;
auto padding() const -> Geometry;
auto remove(sHorizontalLayoutCell cell) -> type&;
auto reset() -> type& override;
auto setAlignment(float alignment = 0.5) -> type&;
auto setEnabled(bool enabled = true) -> type& override;
auto setFont(const Font& font = {}) -> type& override;
auto setAlignment(maybe<float> alignment) -> type&;
auto setEnabled(bool enabled) -> type& override;
auto setFont(const Font& font) -> type& override;
auto setGeometry(Geometry geometry) -> type& override;
auto setMargin(float margin = 0) -> type&;
auto setPadding(Geometry padding = {}) -> type&;
auto setSpacing(float spacing = 5) -> type&;
auto setVisible(bool visible = true) -> type&;
auto setPadding(Geometry padding) -> type&;
auto setParent(mObject* parent = nullptr, int offset = -1) -> type&;
auto setSpacing(float spacing) -> type&;
auto setVisible(bool visible) -> type& override;
auto spacing() const -> float;
auto synchronize() -> type&;
struct Settings {
float alignment = 0.5;
private:
auto destruct() -> void override;
struct State {
maybe<float> alignment;
vector<HorizontalLayoutCell> cells;
Geometry padding;
float spacing = 5;
} settings;
} state;
};
struct Property : Size {
Property() = default;
Property(float width, float height, float spacing) : Size(width, height), _spacing(spacing) {}
auto setSpacing(float spacing) -> Property& { return _spacing = spacing, *this; }
auto spacing() const -> float { return _spacing; }
float _spacing = 0;
};
vector<Property> properties;
struct mHorizontalLayoutCell : mObject {
using type = mHorizontalLayoutCell;
auto alignment() const -> maybe<float>;
auto setAlignment(maybe<float> alignment) -> type&;
auto setEnabled(bool enabled) -> type& override;
auto setFont(const Font& font) -> type& override;
auto setParent(mObject* parent = nullptr, int offset = -1) -> type& override;
auto setSizable(sSizable sizable) -> type&;
auto setSize(Size size) -> type&;
auto setSpacing(float spacing) -> type&;
auto setVisible(bool visible) -> type& override;
auto sizable() const -> Sizable;
auto size() const -> Size;
auto spacing() const -> float;
auto synchronize() -> type&;
private:
auto destruct() -> void override;
struct State {
maybe<float> alignment;
sSizable sizable;
Size size;
float spacing = 5;
} state;
friend class mHorizontalLayout;
};
#endif

View File

@@ -1,49 +0,0 @@
#if defined(Hiro_ListView)
mListViewItem::mListViewItem() {
append(TableViewCell());
}
auto mListViewItem::checkable() const -> bool {
return cell(0).checkable();
}
auto mListViewItem::checked() const -> bool {
return cell(0).checked();
}
auto mListViewItem::icon() const -> image {
return cell(0).icon();
}
auto mListViewItem::reset() -> type& {
mTableViewItem::reset();
append(TableViewCell());
return *this;
}
auto mListViewItem::setCheckable(bool checkable) -> type& {
cell(0).setCheckable(checkable);
return *this;
}
auto mListViewItem::setChecked(bool checked) -> type& {
cell(0).setChecked(checked);
return *this;
}
auto mListViewItem::setIcon(const image& icon) -> type& {
cell(0).setIcon(icon);
return *this;
}
auto mListViewItem::setText(const string& text) -> type& {
cell(0).setText(text);
return *this;
}
auto mListViewItem::text() const -> string {
return cell(0).text();
}
#endif

View File

@@ -1,24 +0,0 @@
#if defined(Hiro_ListView)
struct ListViewItem;
struct mListViewItem;
using sListViewItem = shared_pointer<mListViewItem>;
struct mListViewItem : mTableViewItem {
using type = mListViewItem;
using mTableViewItem::append;
using mTableViewItem::remove;
mListViewItem();
auto checkable() const -> bool;
auto checked() const -> bool;
auto icon() const -> image;
auto reset() -> type&;
auto setCheckable(bool checkable) -> type&;
auto setChecked(bool checked) -> type&;
auto setIcon(const image& icon = {}) -> type&;
auto setText(const string& text) -> type&;
auto text() const -> string;
};
#endif

View File

@@ -72,4 +72,52 @@ auto mListView::selected() const -> ListViewItem {
return ListViewItem{mTableView::selected()};
}
//
mListViewItem::mListViewItem() {
append(TableViewCell());
}
auto mListViewItem::checkable() const -> bool {
return cell(0).checkable();
}
auto mListViewItem::checked() const -> bool {
return cell(0).checked();
}
auto mListViewItem::icon() const -> image {
return cell(0).icon();
}
auto mListViewItem::reset() -> type& {
mTableViewItem::reset();
append(TableViewCell());
return *this;
}
auto mListViewItem::setCheckable(bool checkable) -> type& {
cell(0).setCheckable(checkable);
return *this;
}
auto mListViewItem::setChecked(bool checked) -> type& {
cell(0).setChecked(checked);
return *this;
}
auto mListViewItem::setIcon(const image& icon) -> type& {
cell(0).setIcon(icon);
return *this;
}
auto mListViewItem::setText(const string& text) -> type& {
cell(0).setText(text);
return *this;
}
auto mListViewItem::text() const -> string {
return cell(0).text();
}
#endif

View File

@@ -1,8 +1,13 @@
#if defined(Hiro_ListView)
struct ListView;
struct ListViewItem;
struct mListView;
struct mListViewItem;
using sListView = shared_pointer<mListView>;
using sListViewItem = shared_pointer<mListViewItem>;
struct mListView : mTableView {
using type = mListView;
@@ -30,4 +35,21 @@ struct mListView : mTableView {
} state;
};
struct mListViewItem : mTableViewItem {
using type = mListViewItem;
using mTableViewItem::append;
using mTableViewItem::remove;
mListViewItem();
auto checkable() const -> bool;
auto checked() const -> bool;
auto icon() const -> image;
auto reset() -> type&;
auto setCheckable(bool checkable) -> type&;
auto setChecked(bool checked) -> type&;
auto setIcon(const image& icon = {}) -> type&;
auto setText(const string& text) -> type&;
auto text() const -> string;
};
#endif

View File

@@ -52,10 +52,10 @@ auto MessageDialog::_run() -> string {
HorizontalLayout controlLayout{&layout, Size{~0, 0}};
Widget controlSpacer{&controlLayout, Size{~0, 0}};
layout.setMargin(5);
layout.setPadding(5);
messageIcon.setIcon(state.icon);
messageText.setText(state.text);
for(auto n : range(state.buttons)) {
for(auto n : range(state.buttons.size())) {
Button button{&controlLayout, Size{80, 0}, 5};
button.onActivate([&, n] { state.response = state.buttons[n]; window.setModal(false); });
button.setText(state.buttons[n]);

View File

@@ -1,41 +1,142 @@
#if defined(Hiro_FixedLayout)
using sFixedLayout = shared_pointer<mFixedLayout>;
struct FixedLayoutCell : sFixedLayoutCell {
DeclareSharedObject(FixedLayoutCell)
auto geometry() const { return self().geometry(); }
auto setGeometry(Geometry geometry) { return self().setGeometry(geometry), *this; }
auto setSizable(sSizable sizable) { return self().setSizable(sizable), *this; }
auto sizable() const { return self().sizable(); }
};
struct FixedLayout : sFixedLayout {
DeclareSharedLayout(FixedLayout)
DeclareSharedSizable(FixedLayout)
auto append(sSizable sizable, Geometry geometry) { return self().append(sizable, geometry), *this; }
auto modify(sSizable sizable, Geometry geometry) { return self().modify(sizable, geometry), *this; }
auto cell(uint position) const { return self().cell(position); }
auto cell(sSizable sizable) const { return self().cell(sizable); }
auto cellCount() const { return self().cellCount(); }
auto remove(sFixedLayoutCell cell) { return self().remove(cell), *this; }
auto reset() { return self().reset(), *this; }
};
#endif
#if defined(Hiro_HorizontalLayout)
using sHorizontalLayout = shared_pointer<mHorizontalLayout>;
struct HorizontalLayout : sHorizontalLayout {
DeclareSharedLayout(HorizontalLayout)
struct HorizontalLayoutCell : sHorizontalLayoutCell {
DeclareSharedObject(HorizontalLayoutCell)
auto alignment() const { return self().alignment(); }
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 sizable() const { return self().sizable(); }
auto size() const { return self().size(); }
auto spacing() const { return self().spacing(); }
};
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 modify(sSizable sizable, Size size, float spacing = 5) { return self().modify(sizable, size, spacing), *this; }
auto setAlignment(float alignment = 0.5) { return self().setAlignment(alignment), *this; }
auto setMargin(float margin = 0) { return self().setMargin(margin), *this; }
auto cell(uint position) const { return self().cell(position); }
auto cell(sSizable sizable) const { return self().cell(sizable); }
auto cellCount() const { return self().cellCount(); }
auto remove(sHorizontalLayoutCell cell) { return self().remove(cell), *this; }
auto reset() { return self().reset(), *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(Geometry padding = {}) { return self().setPadding(padding), *this; }
auto setSpacing(float spacing = 5) { return self().setSpacing(spacing), *this; }
};
#endif
#if defined(Hiro_VerticalLayout)
using sVerticalLayout = shared_pointer<mVerticalLayout>;
struct VerticalLayout : sVerticalLayout {
DeclareSharedLayout(VerticalLayout)
struct VerticalLayoutCell : sVerticalLayoutCell {
DeclareSharedObject(VerticalLayoutCell)
auto alignment() const { return self().alignment(); }
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 sizable() const { return self().sizable(); }
auto size() const { return self().size(); }
auto spacing() const { return self().spacing(); }
};
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 modify(sSizable sizable, Size size, float spacing = 5) { return self().modify(sizable, size, spacing), *this; }
auto setAlignment(float alignment = 0.0) { return self().setAlignment(alignment), *this; }
auto setMargin(float margin = 0) { return self().setMargin(margin), *this; }
auto cell(uint position) const { return self().cell(position); }
auto cell(sSizable sizable) const { return self().cell(sizable); }
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 setAlignment(maybe<float> alignment = {}) { return self().setAlignment(alignment), *this; }
auto setPadding(float padding) { return self().setPadding({padding, padding, padding, padding}), *this; }
auto setPadding(Geometry padding = {}) { return self().setPadding(padding), *this; }
auto setSpacing(float spacing = 5) { return self().setSpacing(spacing), *this; }
};
#endif
#if defined(Hiro_TableLayout)
struct TableLayoutCell : sTableLayoutCell {
DeclareSharedObject(TableLayoutCell)
auto alignment() const { return self().alignment(); }
auto setAlignment(Alignment 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 sizable() const { return self().sizable(); }
auto size() const { return self().size(); }
};
struct TableLayoutColumn : sTableLayoutColumn {
DeclareSharedObject(TableLayoutColumn)
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 spacing() const { return self().spacing(); }
};
struct TableLayoutRow : sTableLayoutRow {
DeclareSharedObject(TableLayoutRow)
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 spacing() const { return self().spacing(); }
};
struct TableLayout : sTableLayout {
DeclareSharedSizable(TableLayout)
auto alignment() const { return self().alignment(); }
auto append(sSizable sizable, Size size) { return self().append(sizable, size), *this; }
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 cellCount() const { return self().cellCount(); }
auto column(uint position) const { return self().column(position); }
auto columnCount() const { return self().columnCount(); }
auto padding() const { return self().padding(); }
auto remove(sTableLayoutCell cell) { return self().remove(cell), *this; }
auto reset() { return self().reset(), *this; }
auto row(uint position) const { return self().row(position); }
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(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_ListView)
struct ListViewItem : sListViewItem {
DeclareSharedObject(ListViewItem)
@@ -56,9 +157,7 @@ struct ListViewItem : sListViewItem {
auto setText(const string& text = "") { return self().setText(text), *this; }
auto text() const { return self().text(); }
};
#endif
#if defined(Hiro_ListView)
struct ListView : sListView {
DeclareSharedWidget(ListView)

View File

@@ -0,0 +1,413 @@
#if defined(Hiro_TableLayout)
auto mTableLayout::alignment() const -> Alignment {
return state.alignment;
}
auto mTableLayout::append(sSizable sizable, Size size) -> type& {
TableLayoutCell cell;
cell->setSizable(sizable);
cell->setSize(size);
cell->setParent(this, cellCount());
state.cells.append(cell);
return *this;
}
auto mTableLayout::cell(uint position) const -> TableLayoutCell {
return state.cells(position, {});
}
auto mTableLayout::cell(uint x, uint y) const -> TableLayoutCell {
if(auto cell = state.cells(y * columnCount() + x, {})) return cell;
return {};
}
auto mTableLayout::cell(sSizable sizable) const -> TableLayoutCell {
for(auto& cell : state.cells) {
if(cell->state.sizable == sizable) return cell;
}
return {};
}
auto mTableLayout::cellCount() const -> uint {
return state.cells.size();
}
auto mTableLayout::column(uint position) const -> TableLayoutColumn {
return state.columns(position, {});
}
auto mTableLayout::columnCount() const -> uint {
return state.columns.size();
}
auto mTableLayout::destruct() -> void {
for(auto& cell : state.cells) cell->destruct();
for(auto& column : state.columns) column->destruct();
for(auto& row : state.rows) row->destruct();
mSizable::destruct();
}
auto mTableLayout::minimumSize() const -> Size {
float minimumWidth = 0;
for(uint x : range(columnCount())) {
float width = 0;
auto column = this->column(x);
for(uint y : range(rowCount())) {
auto row = this->row(y);
auto cell = this->cell(x, y);
if(cell.size().width() == Size::Minimum || cell.size().width() == Size::Maximum) {
width = max(width, cell.sizable()->minimumSize().width());
} else {
width = max(width, cell.size().width());
}
}
minimumWidth += width;
if(x != columnCount() - 1) minimumWidth += column.spacing();
}
float minimumHeight = 0;
for(uint y : range(rowCount())) {
float height = 0;
auto row = this->row(y);
for(uint x : range(columnCount())) {
auto column = this->column(x);
auto cell = this->cell(x, y);
if(cell.size().height() == Size::Minimum || cell.size().height() == Size::Maximum) {
height = max(height, cell.sizable()->minimumSize().height());
} else {
height = max(height, cell.size().height());
}
}
minimumHeight += height;
if(y != rowCount() - 1) minimumHeight += row.spacing();
}
return {
padding().x() + minimumWidth + padding().width(),
padding().y() + minimumHeight + padding().height()
};
}
auto mTableLayout::padding() const -> Geometry {
return state.padding;
}
auto mTableLayout::remove(sTableLayoutCell cell) -> type& {
if(cell->parent() != this) return *this;
auto offset = cell->offset();
cell->setParent();
state.cells.remove(offset);
for(uint n : range(offset, cellCount())) state.cells[n]->adjustOffset(-1);
return synchronize();
}
auto mTableLayout::reset() -> type& {
while(state.cells) remove(state.cells.right());
return synchronize();
}
auto mTableLayout::row(uint position) const -> TableLayoutRow {
return state.rows(position, {});
}
auto mTableLayout::rowCount() const -> uint {
return state.rows.size();
}
auto mTableLayout::setAlignment(Alignment alignment) -> type& {
state.alignment = alignment;
return synchronize();
}
auto mTableLayout::setEnabled(bool enabled) -> type& {
mSizable::setEnabled(enabled);
for(auto& cell : state.cells) cell.setEnabled(cell.enabled());
return *this;
}
auto mTableLayout::setFont(const Font& font) -> type& {
mSizable::setFont(font);
for(auto& cell : state.cells) cell.setFont(cell.font());
return *this;
}
auto mTableLayout::setGeometry(Geometry geometry) -> type& {
mSizable::setGeometry(geometry);
auto window = parentWindow(true);
if(!window || !window->visible()) return *this;
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());
vector<float> widths;
widths.resize(columnCount());
uint maximumWidths = 0;
for(uint x : range(columnCount())) {
float width = 0;
auto column = this->column(x);
for(uint y : range(rowCount())) {
auto row = this->row(y);
auto cell = this->cell(x, y);
if(cell.size().width() == Size::Maximum) {
width = Size::Maximum;
maximumWidths++;
break;
}
if(cell.size().width() == Size::Minimum) {
width = max(width, cell.sizable()->minimumSize().width());
} else {
width = max(width, cell.size().width());
}
}
widths[x] = width;
}
vector<float> heights;
heights.resize(rowCount());
uint maximumHeights = 0;
for(uint y : range(rowCount())) {
float height = 0;
auto row = this->row(y);
for(uint x : range(columnCount())) {
auto column = this->column(x);
auto cell = this->cell(x, y);
if(cell.size().height() == Size::Maximum) {
height = Size::Maximum;
maximumHeights++;
break;
}
if(cell.size().height() == Size::Minimum) {
height = max(height, cell.sizable()->minimumSize().height());
} else {
height = max(height, cell.size().height());
}
}
heights[y] = height;
}
float fixedWidth = 0;
for(uint x : range(columnCount())) {
if(widths[x] != Size::Maximum) fixedWidth += widths[x];
if(x != columnCount() - 1) fixedWidth += column(x)->spacing();
}
float maximumWidth = (geometry.width() - fixedWidth) / maximumWidths;
for(auto& width : widths) {
if(width == Size::Maximum) width = maximumWidth;
}
float fixedHeight = 0;
for(uint y : range(rowCount())) {
if(heights[y] != Size::Maximum) fixedHeight += heights[y];
if(y != rowCount() - 1) fixedHeight += row(y)->spacing();
}
float maximumHeight = (geometry.height() - fixedHeight) / maximumHeights;
for(auto& height : heights) {
if(height == Size::Maximum) height = maximumHeight;
}
float geometryY = geometry.y();
for(uint y : range(rowCount())) {
float geometryX = geometry.x();
auto row = this->row(y);
for(uint x : range(columnCount())) {
auto column = this->column(x);
auto cell = this->cell(x, y);
float geometryWidth = widths [x];
float geometryHeight = heights[y];
auto alignment = cell.alignment();
if(!alignment) alignment = column.alignment();
if(!alignment) alignment = row.alignment();
if(!alignment) alignment = this->alignment();
if(!alignment) alignment = {0.0, 0.5};
float cellWidth = cell.size().width();
if(cellWidth == Size::Minimum) cellWidth = cell.sizable()->minimumSize().width();
if(cellWidth == Size::Maximum) cellWidth = geometryWidth;
cellWidth = min(cellWidth, geometryWidth);
float cellHeight = cell.size().height();
if(cellHeight == Size::Minimum) cellHeight = cell.sizable()->minimumSize().height();
if(cellHeight == Size::Maximum) cellHeight = geometryHeight;
cellHeight = min(cellHeight, geometryHeight);
float cellX = geometryX + alignment.horizontal() * (geometryWidth - cellWidth);
float cellY = geometryY + alignment.vertical() * (geometryHeight - cellHeight);
cell.sizable()->setGeometry({cellX, cellY, cellWidth, cellHeight});
geometryX += widths[x] + column.spacing();
}
geometryY += heights[y] + row.spacing();
}
return *this;
}
auto mTableLayout::setPadding(Geometry padding) -> type& {
state.padding = padding;
return synchronize();
}
auto mTableLayout::setParent(mObject* parent, int offset) -> type& {
for(auto& cell : reverse(state.cells)) cell->destruct();
for(auto& column : reverse(state.columns)) column->destruct();
for(auto& row : reverse(state.rows)) row->destruct();
mObject::setParent(parent, offset);
for(auto& cell : state.cells) cell->setParent(this, cell->offset());
for(auto& column : state.columns) column->setParent(this, column->offset());
for(auto& row : state.rows) row->setParent(this, row->offset());
return *this;
}
auto mTableLayout::setSize(Size size) -> type& {
state.size = size;
state.columns.reset();
state.rows.reset();
for(auto x : range(size.width())) state.columns.append(TableLayoutColumn());
for(auto y : range(size.height())) state.rows.append(TableLayoutRow());
return synchronize();
}
auto mTableLayout::setVisible(bool visible) -> type& {
mSizable::setVisible(visible);
for(auto& cell : state.cells) cell.setVisible(cell.visible());
return synchronize();
}
auto mTableLayout::size() const -> Size {
return state.size;
}
auto mTableLayout::synchronize() -> type& {
setGeometry(geometry());
return *this;
}
//
auto mTableLayoutColumn::alignment() const -> Alignment {
return state.alignment;
}
auto mTableLayoutColumn::setAlignment(Alignment alignment) -> type& {
state.alignment = alignment;
return synchronize();
}
auto mTableLayoutColumn::setSpacing(float spacing) -> type& {
state.spacing = spacing;
return synchronize();
}
auto mTableLayoutColumn::spacing() const -> float {
return state.spacing;
}
auto mTableLayoutColumn::synchronize() -> type& {
if(auto parent = this->parent()) {
if(auto tableLayout = dynamic_cast<mTableLayout*>(parent)) {
tableLayout->synchronize();
}
}
return *this;
}
//
auto mTableLayoutRow::alignment() const -> Alignment {
return state.alignment;
}
auto mTableLayoutRow::setAlignment(Alignment alignment) -> type& {
state.alignment = alignment;
return synchronize();
}
auto mTableLayoutRow::setSpacing(float spacing) -> type& {
state.spacing = spacing;
return synchronize();
}
auto mTableLayoutRow::spacing() const -> float {
return state.spacing;
}
auto mTableLayoutRow::synchronize() -> type& {
if(auto parent = this->parent()) {
if(auto tableLayout = dynamic_cast<mTableLayout*>(parent)) {
tableLayout->synchronize();
}
}
return *this;
}
//
auto mTableLayoutCell::alignment() const -> Alignment {
return state.alignment;
}
auto mTableLayoutCell::destruct() -> void {
if(auto& sizable = state.sizable) sizable->destruct();
mObject::destruct();
}
auto mTableLayoutCell::setAlignment(Alignment alignment) -> type& {
state.alignment = alignment;
return synchronize();
}
auto mTableLayoutCell::setEnabled(bool enabled) -> type& {
mObject::setEnabled(enabled);
state.sizable->setEnabled(state.sizable->enabled());
return *this;
}
auto mTableLayoutCell::setFont(const Font& font) -> type& {
mObject::setFont(font);
state.sizable->setFont(state.sizable->font());
return *this;
}
auto mTableLayoutCell::setParent(mObject* parent, int offset) -> type& {
state.sizable->destruct();
mObject::setParent(parent, offset);
state.sizable->setParent(this, 0);
return *this;
}
auto mTableLayoutCell::setSizable(sSizable sizable) -> type& {
state.sizable = sizable;
state.sizable->setParent(this, 0);
return synchronize();
}
auto mTableLayoutCell::setSize(Size size) -> type& {
state.size = size;
return synchronize();
}
auto mTableLayoutCell::setVisible(bool visible) -> type& {
mObject::setVisible(visible);
state.sizable->setVisible(state.sizable->visible());
return *this;
}
auto mTableLayoutCell::sizable() const -> Sizable {
return state.sizable;
}
auto mTableLayoutCell::size() const -> Size {
return state.size;
}
auto mTableLayoutCell::synchronize() -> type& {
if(auto parent = this->parent()) {
if(auto tableLayout = dynamic_cast<mTableLayout*>(parent)) {
tableLayout->synchronize();
}
}
return *this;
}
#endif

View File

@@ -0,0 +1,123 @@
#if defined(Hiro_TableLayout)
struct TableLayout;
struct TableLayoutColumn;
struct TableLayoutRow;
struct TableLayoutCell;
struct mTableLayout;
struct mTableLayoutColumn;
struct mTableLayoutRow;
struct mTableLayoutCell;
using sTableLayout = shared_pointer<mTableLayout>;
using sTableLayoutColumn = shared_pointer<mTableLayoutColumn>;
using sTableLayoutRow = shared_pointer<mTableLayoutRow>;
using sTableLayoutCell = shared_pointer<mTableLayoutCell>;
struct mTableLayout : mSizable {
using type = mTableLayout;
using mSizable::remove;
auto alignment() const -> Alignment;
auto append(sSizable sizable, Size size) -> type&;
auto cell(uint position) const -> TableLayoutCell;
auto cell(uint x, uint y) const -> TableLayoutCell;
auto cell(sSizable sizable) const -> TableLayoutCell;
auto cellCount() const -> uint;
auto column(uint position) const -> TableLayoutColumn;
auto columnCount() const -> uint;
auto minimumSize() const -> Size override;
auto padding() const -> Geometry;
auto remove(sTableLayoutCell cell) -> type&;
auto reset() -> type&;
auto row(uint position) const -> TableLayoutRow;
auto rowCount() const -> uint;
auto setAlignment(Alignment alignment) -> type&;
auto setEnabled(bool enabled) -> type& override;
auto setFont(const Font& font) -> type& override;
auto setGeometry(Geometry geometry) -> type& override;
auto setPadding(Geometry padding) -> type&;
auto setParent(mObject* parent = nullptr, int offset = -1) -> type& override;
auto setSize(Size size) -> type&;
auto setVisible(bool visible) -> type& override;
auto size() const -> Size;
auto synchronize() -> type&;
private:
auto destruct() -> void override;
struct State {
Alignment alignment;
vector<TableLayoutCell> cells;
vector<TableLayoutColumn> columns;
Geometry padding;
vector<TableLayoutRow> rows;
Size size;
} state;
};
struct mTableLayoutColumn : mObject {
using type = mTableLayoutColumn;
auto alignment() const -> Alignment;
auto setAlignment(Alignment alignment) -> type&;
auto setSpacing(float spacing) -> type&;
auto spacing() const -> float;
auto synchronize() -> type&;
private:
struct State {
Alignment alignment;
float spacing = 5;
} state;
friend class mTableLayout;
};
struct mTableLayoutRow : mObject {
using type = mTableLayoutRow;
auto alignment() const -> Alignment;
auto setAlignment(Alignment alignment) -> type&;
auto setSpacing(float spacing) -> type&;
auto spacing() const -> float;
auto synchronize() -> type&;
private:
struct State {
Alignment alignment;
float spacing = 5;
} state;
friend class mTableLayout;
};
struct mTableLayoutCell : mObject {
using type = mTableLayoutCell;
auto alignment() const -> Alignment;
auto setAlignment(Alignment alignment) -> type&;
auto setEnabled(bool enabled) -> type& override;
auto setFont(const Font& font) -> type& override;
auto setParent(mObject* parent = nullptr, int offset = -1) -> type& override;
auto setSizable(sSizable sizable) -> type&;
auto setSize(Size size) -> type&;
auto setVisible(bool visible) -> type& override;
auto sizable() const -> Sizable;
auto size() const -> Size;
auto synchronize() -> type&;
private:
auto destruct() -> void override;
struct State {
Alignment alignment;
sSizable sizable;
Size size;
} state;
friend class mTableLayout;
};
#endif

View File

@@ -1,149 +1,290 @@
#if defined(Hiro_VerticalLayout)
auto mVerticalLayout::append(sSizable sizable, Size size, float spacing) -> type& {
properties.append({size.width(), size.height(), spacing < 0 ? settings.spacing : spacing});
mLayout::append(sizable);
return *this;
auto mVerticalLayout::alignment() const -> maybe<float> {
return state.alignment;
}
auto mVerticalLayout::modify(sSizable sizable, Size size, float spacing) -> type& {
if(sizable && this->sizable(sizable->offset()) == sizable) {
auto& properties = this->properties[sizable->offset()];
properties.setWidth(size.width());
properties.setHeight(size.height());
properties.setSpacing(spacing);
auto mVerticalLayout::append(sSizable sizable, Size size, float spacing) -> type& {
VerticalLayoutCell cell;
cell->setSizable(sizable);
cell->setSize(size);
cell->setSpacing(spacing);
cell->setParent(this, cellCount());
state.cells.append(cell);
return synchronize();
}
auto mVerticalLayout::cell(uint position) const -> VerticalLayoutCell {
return state.cells(position, {});
}
auto mVerticalLayout::cell(sSizable sizable) const -> VerticalLayoutCell {
for(auto& cell : state.cells) {
if(cell->state.sizable == sizable) return cell;
}
return *this;
return {};
}
auto mVerticalLayout::cellCount() const -> uint {
return state.cells.size();
}
auto mVerticalLayout::destruct() -> void {
for(auto& cell : state.cells) cell->destruct();
mSizable::destruct();
}
auto mVerticalLayout::minimumSize() const -> Size {
float width = 0, height = 0;
for(auto n : range(sizableCount())) {
auto& child = properties[sizable(n)->offset()];
if(child.width() == Size::Minimum || child.width() == Size::Maximum) {
width = max(width, sizable(n)->minimumSize().width());
float width = 0;
for(auto index : range(cellCount())) {
auto cell = this->cell(index);
if(cell.size().width() == Size::Minimum || cell.size().width() == Size::Maximum) {
width = max(width, cell.sizable().minimumSize().width());
continue;
}
width = max(width, child.width());
width = max(width, cell.size().width());
}
for(auto n : range(sizableCount())) {
auto& child = properties[sizable(n)->offset()];
if(child.height() == Size::Minimum || child.height() == Size::Maximum) {
height += sizable(n)->minimumSize().height();
float height = 0;
for(auto index : range(cellCount())) {
auto cell = this->cell(index);
if(cell.size().height() == Size::Minimum || cell.size().height() == Size::Maximum) {
height += cell.sizable().minimumSize().height();
} else {
height += child.height();
height += cell.size().height();
}
if(&child != &properties.right()) height += child.spacing();
if(index != cellCount() - 1) height += cell.spacing();
}
return {
settings.padding.x() + width + settings.padding.width(),
settings.padding.y() + height + settings.padding.height()
padding().x() + width + padding().width(),
padding().y() + height + padding().height()
};
}
auto mVerticalLayout::padding() const -> Geometry {
return state.padding;
}
auto mVerticalLayout::remove(sSizable sizable) -> type& {
properties.remove(sizable->offset());
mLayout::remove(sizable);
for(auto& cell : state.cells) {
if(cell->state.sizable == sizable) {
remove(cell);
break;
}
}
return *this;
}
auto mVerticalLayout::remove(sVerticalLayoutCell cell) -> type& {
if(cell->parent() != this) return *this;
auto offset = cell->offset();
cell->setParent();
state.cells.remove(offset);
for(uint n : range(offset, cellCount())) state.cells[n]->adjustOffset(-1);
return synchronize();
}
auto mVerticalLayout::reset() -> type& {
mLayout::reset();
properties.reset();
return *this;
while(state.cells) remove(state.cells.right());
return synchronize();
}
auto mVerticalLayout::setAlignment(float alignment) -> type& {
settings.alignment = max(0.0, min(1.0, alignment));
return *this;
auto mVerticalLayout::setAlignment(maybe<float> alignment) -> type& {
state.alignment = alignment;
return synchronize();
}
auto mVerticalLayout::setEnabled(bool enabled) -> type& {
mLayout::setEnabled(enabled);
for(auto n : range(sizableCount())) {
sizable(n)->setEnabled(sizable(n)->enabled());
}
mSizable::setEnabled(enabled);
for(auto& cell : state.cells) cell.sizable().setEnabled(cell.sizable().enabled());
return *this;
}
auto mVerticalLayout::setFont(const Font& font) -> type& {
mLayout::setFont(font);
for(auto n : range(sizableCount())) {
sizable(n)->setFont(sizable(n)->font());
}
mSizable::setFont(font);
for(auto& cell : state.cells) cell.sizable().setFont(cell.sizable().font());
return *this;
}
auto mVerticalLayout::setGeometry(Geometry containerGeometry) -> type& {
mLayout::setGeometry(containerGeometry);
auto mVerticalLayout::setGeometry(Geometry geometry) -> type& {
mSizable::setGeometry(geometry);
auto window = parentWindow(true);
if(!window || !window->visible()) return *this;
auto properties = this->properties;
for(auto n : range(sizableCount())) {
auto& child = properties[sizable(n)->offset()];
if(child.width() == Size::Minimum) child.setWidth(sizable(n)->minimumSize().width());
if(child.height() == Size::Minimum) child.setHeight(sizable(n)->minimumSize().height());
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())) {
auto cell = this->cell(index);
if(cell.size().width() == Size::Maximum) {
width = geometry.width();
break;
} else if(cell.size().width() == Size::Minimum) {
width = max(width, cell.sizable().minimumSize().width());
} else {
width = max(width, cell.size().width());
}
}
Geometry geometry = containerGeometry;
geometry.setX (geometry.x() + settings.padding.x());
geometry.setY (geometry.y() + settings.padding.y());
geometry.setWidth (geometry.width() - settings.padding.x() - settings.padding.width());
geometry.setHeight(geometry.height() - settings.padding.y() - settings.padding.height());
float minimumHeight = 0, maximumHeightCounter = 0;
for(auto& child : properties) {
if(child.height() == Size::Maximum) maximumHeightCounter++;
if(child.height() != Size::Maximum) minimumHeight += child.height();
if(&child != &properties.right()) minimumHeight += child.spacing();
vector<float> heights;
heights.resize(cellCount());
uint maximumHeights = 0;
for(auto index : range(cellCount())) {
auto cell = this->cell(index);
float height = 0;
if(cell.size().height() == Size::Maximum) {
height = Size::Maximum;
maximumHeights++;
} else if(cell.size().height() == Size::Minimum) {
height = cell.sizable().minimumSize().height();
} else {
height = cell.size().height();
}
heights[index] = height;
}
for(auto& child : properties) {
if(child.width() == Size::Maximum) child.setWidth(geometry.width());
if(child.height() == Size::Maximum) child.setHeight((geometry.height() - minimumHeight) / maximumHeightCounter);
float fixedHeight = 0;
for(uint index : range(state.cells.size())) {
if(heights[index] != Size::Maximum) fixedHeight += heights[index];
if(index != cellCount() - 1) fixedHeight += cell(index).spacing();
}
float maximumWidth = 0;
for(auto& child : properties) maximumWidth = max(maximumWidth, child.width());
for(auto n : range(sizableCount())) {
auto& child = properties[sizable(n)->offset()];
float pivot = (maximumWidth - child.width()) * settings.alignment;
Geometry childGeometry = {geometry.x() + pivot, geometry.y(), child.width(), child.height()};
if(childGeometry.width() < 1) childGeometry.setWidth (1);
if(childGeometry.height() < 1) childGeometry.setHeight(1);
sizable(n)->setGeometry(childGeometry);
geometry.setY (geometry.y() + child.height() + child.spacing());
geometry.setHeight(geometry.height() - child.height() + child.spacing());
float maximumHeight = (geometry.height() - fixedHeight) / maximumHeights;
for(auto& height : heights) {
if(height == Size::Maximum) height = maximumHeight;
}
return *this;
}
float geometryX = geometry.x();
float geometryY = geometry.y();
for(auto index : range(cellCount())) {
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.5;
float cellWidth = cell.size().width();
float cellHeight = geometryHeight;
if(cellWidth == Size::Minimum) cellWidth = cell.sizable()->minimumSize().width();
if(cellWidth == Size::Maximum) cellWidth = geometryWidth;
float cellX = geometryX + alignment() * (geometryWidth - cellWidth);
float cellY = geometryY;
cell.sizable().setGeometry({cellX, cellY, cellWidth, cellHeight});
geometryY += geometryHeight + cell.spacing();
}
auto mVerticalLayout::setMargin(float margin) -> type& {
setPadding({margin, margin, margin, margin});
return *this;
}
auto mVerticalLayout::setPadding(Geometry padding) -> type& {
settings.padding = padding;
setGeometry(geometry());
state.padding = padding;
return synchronize();
}
auto mVerticalLayout::setParent(mObject* parent, int offset) -> type& {
for(auto& cell : reverse(state.cells)) cell->destruct();
mSizable::setParent(parent, offset);
for(auto& cell : state.cells) cell->setParent(this, cell->offset());
return *this;
}
auto mVerticalLayout::setSpacing(float spacing) -> type& {
settings.spacing = spacing;
state.spacing = spacing;
return synchronize();
}
auto mVerticalLayout::setVisible(bool visible) -> type& {
mSizable::setVisible(visible);
for(auto& cell : state.cells) cell.sizable().setVisible(cell.sizable().visible());
return synchronize();
}
auto mVerticalLayout::spacing() const -> float {
return state.spacing;
}
auto mVerticalLayout::synchronize() -> type& {
setGeometry(geometry());
return *this;
}
auto mVerticalLayout::setVisible(bool visible) -> type& {
mLayout::setVisible(visible);
for(auto n : range(sizableCount())) {
sizable(n)->setVisible(sizable(n)->visible());
//
auto mVerticalLayoutCell::alignment() const -> maybe<float> {
return state.alignment;
}
auto mVerticalLayoutCell::destruct() -> void {
if(auto& sizable = state.sizable) sizable->destruct();
mObject::destruct();
}
auto mVerticalLayoutCell::setAlignment(maybe<float> alignment) -> type& {
state.alignment = alignment;
return synchronize();
}
auto mVerticalLayoutCell::setEnabled(bool enabled) -> type& {
mObject::setEnabled(enabled);
state.sizable->setEnabled(state.sizable->enabled());
return *this;
}
auto mVerticalLayoutCell::setFont(const Font& font) -> type& {
mObject::setFont(font);
state.sizable->setFont(state.sizable->font());
return *this;
}
auto mVerticalLayoutCell::setParent(mObject* parent, int offset) -> type& {
state.sizable->destruct();
mObject::setParent(parent, offset);
state.sizable->setParent(this, 0);
return *this;
}
auto mVerticalLayoutCell::setSizable(sSizable sizable) -> type& {
state.sizable = sizable;
return synchronize();
}
auto mVerticalLayoutCell::setSize(Size size) -> type& {
state.size = size;
return synchronize();
}
auto mVerticalLayoutCell::setSpacing(float spacing) -> type& {
state.spacing = spacing;
return synchronize();
}
auto mVerticalLayoutCell::setVisible(bool visible) -> type& {
mObject::setVisible(visible);
state.sizable->setVisible(state.sizable->visible());
return *this;
}
auto mVerticalLayoutCell::sizable() const -> Sizable {
return state.sizable;
}
auto mVerticalLayoutCell::size() const -> Size {
return state.size;
}
auto mVerticalLayoutCell::spacing() const -> float {
return state.spacing;
}
auto mVerticalLayoutCell::synchronize() -> type& {
if(auto parent = this->parent()) {
if(auto verticalLayout = dynamic_cast<mVerticalLayout*>(parent)) {
verticalLayout->synchronize();
}
}
return *this;
}

View File

@@ -1,38 +1,78 @@
#if defined(Hiro_VerticalLayout)
struct mVerticalLayout : mLayout {
struct VerticalLayout;
struct VerticalLayoutCell;
struct mVerticalLayout;
struct mVerticalLayoutCell;
using sVerticalLayout = shared_pointer<mVerticalLayout>;
using sVerticalLayoutCell = shared_pointer<mVerticalLayoutCell>;
struct mVerticalLayout : mSizable {
using type = mVerticalLayout;
using mLayout::append;
using mLayout::remove;
using mSizable::remove;
auto alignment() const -> maybe<float>;
auto append(sSizable sizable, Size size, float spacing = 5) -> type&;
auto modify(sSizable sizable, Size size, float spacing = 5) -> type&;
auto cell(uint position) const -> VerticalLayoutCell;
auto cell(sSizable sizable) const -> VerticalLayoutCell;
auto cellCount() const -> uint;
auto minimumSize() const -> Size override;
auto remove(sSizable sizable) -> type& override;
auto padding() const -> Geometry;
auto remove(sSizable sizable) -> type&;
auto remove(sVerticalLayoutCell cell) -> type&;
auto reset() -> type& override;
auto setAlignment(float alignment = 0.0) -> type&;
auto setEnabled(bool enabled = true) -> type& override;
auto setFont(const Font& font = {}) -> type& override;
auto setAlignment(maybe<float> alignment) -> type&;
auto setEnabled(bool enabled) -> type& override;
auto setFont(const Font& font) -> type& override;
auto setGeometry(Geometry geometry) -> type& override;
auto setMargin(float margin = 0) -> type&;
auto setPadding(Geometry padding = {}) -> type&;
auto setSpacing(float spacing = 5) -> type&;
auto setVisible(bool visible = true) -> type& override;
auto setPadding(Geometry padding) -> type&;
auto setParent(mObject* parent = nullptr, int offset = -1) -> type& override;
auto setSpacing(float spacing) -> type&;
auto setVisible(bool visible) -> type& override;
auto spacing() const -> float;
auto synchronize() -> type&;
struct Settings {
float alignment = 0.0;
private:
auto destruct() -> void override;
struct State {
maybe<float> alignment;
vector<VerticalLayoutCell> cells;
Geometry padding;
float spacing = 5;
} settings;
} state;
};
struct Property : Size {
Property() = default;
Property(float width, float height, float spacing) : Size(width, height), _spacing(spacing) {}
auto setSpacing(float spacing) -> Property& { return _spacing = spacing, *this; }
auto spacing() const -> float { return _spacing; }
float _spacing = 0;
};
vector<Property> properties;
struct mVerticalLayoutCell : mObject {
using type = mVerticalLayoutCell;
auto alignment() const -> maybe<float>;
auto setAlignment(maybe<float> alignment) -> type&;
auto setEnabled(bool enabled) -> type& override;
auto setFont(const Font& font) -> type& override;
auto setParent(mObject* parent = nullptr, int offset = -1) -> type& override;
auto setSizable(sSizable sizable) -> type&;
auto setSize(Size size) -> type&;
auto setSpacing(float spacing) -> type&;
auto setVisible(bool visible) -> type& override;
auto sizable() const -> Sizable;
auto size() const -> Size;
auto spacing() const -> float;
auto synchronize() -> type&;
private:
auto destruct() -> void override;
struct State {
maybe<float> alignment;
sSizable sizable;
Size size;
float spacing = 5;
} state;
friend class mVerticalLayout;
};
#endif