Update to v094r24 release.

byuu says:

Finally!! Compilation works once again on Windows.

However, it's pretty buggy. Modality isn't really working right, you can
still poke at other windows, but when you select ListView items, they
redraw as empty boxes (need to process WM_DRAWITEM before checking
modality.)

The program crashes when you close it (probably a ruby driver's term()
function, that's what it usually is.)

The Layout::setEnabled(false) call isn't working right, so you get that
annoying chiming sound and cursor movement when mapping keyboard keys to
game inputs.

The column sizing seems off a bit on first display for the Hotkeys tab.

And probably lots more.
This commit is contained in:
Tim Allen
2015-06-12 23:14:38 +10:00
parent 314aee8c5c
commit f0c17ffc0d
188 changed files with 5474 additions and 3834 deletions

View File

@@ -8,13 +8,11 @@ static auto MenuRadioItem_activate(GtkCheckMenuItem* gtkCheckMenuItem, pMenuRadi
auto pMenuRadioItem::construct() -> void {
widget = gtk_radio_menu_item_new_with_mnemonic(0, "");
setGroup(state().group);
gtkCheckMenuItem = GTK_CHECK_MENU_ITEM(widget);
gtkRadioMenuItem = GTK_RADIO_MENU_ITEM(widget);
setText(state().text);
for(auto& weak : state().group) {
if(auto item = weak.acquire()) {
if(item->self()) gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item->self()->widget), item->checked());
}
}
g_signal_connect(G_OBJECT(widget), "toggled", G_CALLBACK(MenuRadioItem_activate), (gpointer)this);
}
@@ -23,55 +21,54 @@ auto pMenuRadioItem::destruct() -> void {
}
auto pMenuRadioItem::setChecked() -> void {
_parent().lock();
for(auto& weak : state().group) {
if(auto item = weak.acquire()) {
if(item->self()) gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item->self()->widget), false);
}
}
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(widget), true);
_parent().unlock();
lock();
gtk_check_menu_item_set_active(gtkCheckMenuItem, true);
unlock();
}
auto pMenuRadioItem::setGroup(const vector<shared_pointer_weak<mMenuRadioItem>>& group) -> void {
_parent().lock();
shared_pointer<mMenuRadioItem> first;
for(auto& weak : group) {
if(!first) {
first = weak.acquire();
continue;
}
if(auto item = weak.acquire()) {
if(item->self() && first->self()) {
GSList* currentGroup = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(first->self()->widget));
if(currentGroup != gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(item->self()->widget))) {
gtk_radio_menu_item_set_group(GTK_RADIO_MENU_ITEM(item->self()->widget), currentGroup);
auto pMenuRadioItem::setGroup(sGroup group) -> void {
if(!group) return;
maybe<GtkRadioMenuItem*> gtkRadioMenuItem;
for(auto& weak : group->state.objects) {
if(auto object = weak.acquire()) {
if(auto menuRadioItem = dynamic_cast<mMenuRadioItem*>(object.data())) {
if(auto self = menuRadioItem->self()) {
self->lock();
gtk_radio_menu_item_set_group(self->gtkRadioMenuItem, nullptr);
if(!gtkRadioMenuItem) gtkRadioMenuItem = self->gtkRadioMenuItem;
else gtk_radio_menu_item_set_group(self->gtkRadioMenuItem, gtk_radio_menu_item_get_group(*gtkRadioMenuItem));
gtk_check_menu_item_set_active(self->gtkCheckMenuItem, menuRadioItem->checked());
self->unlock();
}
}
}
}
_parent().unlock();
}
auto pMenuRadioItem::setText(const string& text) -> void {
gtk_menu_item_set_label(GTK_MENU_ITEM(widget), _mnemonic(text));
}
auto pMenuRadioItem::_doActivate() -> void {
if(!_parent().locked()) {
bool wasChecked = state().checked;
self().setChecked();
if(!wasChecked) self().doActivate();
auto pMenuRadioItem::groupLocked() const -> bool {
if(auto group = state().group) {
for(auto& weak : group->state.objects) {
if(auto object = weak.acquire()) {
if(auto self = object->self()) {
if(self->locked()) return true;
}
}
}
return false;
}
return locked();
}
auto pMenuRadioItem::_parent() -> pMenuRadioItem& {
if(state().group.size()) {
if(auto item = state().group.first().acquire()) {
if(item->self()) return *item->self();
}
}
return *this;
auto pMenuRadioItem::_doActivate() -> void {
if(groupLocked()) return;
bool wasChecked = state().checked;
self().setChecked();
if(!wasChecked) self().doActivate();
}
}

View File

@@ -6,11 +6,15 @@ struct pMenuRadioItem : pAction {
Declare(MenuRadioItem, Action)
auto setChecked() -> void;
auto setGroup(const vector<shared_pointer_weak<mMenuRadioItem>>& group) -> void;
auto setGroup(sGroup group) -> void;
auto setText(const string& text) -> void;
auto groupLocked() const -> bool;
auto _doActivate() -> void;
auto _parent() -> pMenuRadioItem&;
GtkCheckMenuItem* gtkCheckMenuItem = nullptr;
GtkRadioMenuItem* gtkRadioMenuItem = nullptr;
};
}

View File

@@ -2,31 +2,31 @@
namespace hiro {
string pFont::serif(unsigned size, string style) {
auto pFont::serif(unsigned size, string style) -> string {
if(size == 0) size = 8;
if(style == "") style = "Normal";
return {"Serif, ", size, ", ", style};
}
string pFont::sans(unsigned size, string style) {
auto pFont::sans(unsigned size, string style) -> string {
if(size == 0) size = 8;
if(style == "") style = "Normal";
return {"Sans, ", size, ", ", style};
}
string pFont::monospace(unsigned size, string style) {
auto pFont::monospace(unsigned size, string style) -> string {
if(size == 0) size = 8;
return {"Liberation Mono, ", size, ", ", style};
}
Size pFont::size(string font, string text) {
auto pFont::size(string font, string text) -> Size {
PangoFontDescription* description = create(font);
Size size = pFont::size(description, text);
free(description);
return size;
}
PangoFontDescription* pFont::create(string description) {
auto pFont::create(string description) -> PangoFontDescription* {
lstring part = description.split<2>(",").strip();
string family = "Sans";
@@ -47,11 +47,11 @@ PangoFontDescription* pFont::create(string description) {
return font;
}
void pFont::free(PangoFontDescription* font) {
auto pFont::free(PangoFontDescription* font) -> void {
pango_font_description_free(font);
}
Size pFont::size(PangoFontDescription* font, string text) {
auto pFont::size(PangoFontDescription* font, string text) -> Size {
PangoContext* context = gdk_pango_context_get_for_screen(gdk_screen_get_default());
PangoLayout* layout = pango_layout_new(context);
pango_layout_set_font_description(layout, font);
@@ -62,13 +62,13 @@ Size pFont::size(PangoFontDescription* font, string text) {
return {width, height};
}
void pFont::setFont(GtkWidget* widget, string font) {
auto pFont::setFont(GtkWidget* widget, string font) -> void {
auto gtkFont = pFont::create(font);
pFont::setFont(widget, (gpointer)gtkFont);
pFont::free(gtkFont);
}
void pFont::setFont(GtkWidget* widget, gpointer font) {
auto pFont::setFont(GtkWidget* widget, gpointer font) -> void {
if(font == nullptr) return;
gtk_widget_modify_font(widget, (PangoFontDescription*)font);
if(GTK_IS_CONTAINER(widget)) {

View File

@@ -3,16 +3,16 @@
namespace hiro {
struct pFont {
static string serif(unsigned size, string style);
static string sans(unsigned size, string style);
static string monospace(unsigned size, string style);
static Size size(string font, string text);
static auto serif(unsigned size, string style) -> string;
static auto sans(unsigned size, string style) -> string;
static auto monospace(unsigned size, string style) -> string;
static auto size(string font, string text) -> Size;
static PangoFontDescription* create(string description);
static void free(PangoFontDescription* font);
static Size size(PangoFontDescription* font, string text);
static void setFont(GtkWidget* widget, string font);
static void setFont(GtkWidget* widget, gpointer font);
static auto create(string description) -> PangoFontDescription*;
static auto free(PangoFontDescription* font) -> void;
static auto size(PangoFontDescription* font, string text) -> Size;
static auto setFont(GtkWidget* widget, string font) -> void;
static auto setFont(GtkWidget* widget, gpointer font) -> void;
};
}

13
hiro/gtk/group.cpp Normal file
View File

@@ -0,0 +1,13 @@
#if defined(Hiro_Group)
namespace hiro {
auto pGroup::construct() -> void {
}
auto pGroup::destruct() -> void {
}
}
#endif

11
hiro/gtk/group.hpp Normal file
View File

@@ -0,0 +1,11 @@
#if defined(Hiro_Group)
namespace hiro {
struct pGroup : pObject {
Declare(Group, Object)
};
}
#endif

View File

@@ -240,7 +240,7 @@ auto pKeyboard::initialize() -> void {
#include <hiro/platform/xorg/keyboard.hpp>
#endif
//print("[phoenix/gtk] warning: unhandled key: ", key, "\n");
//print("[hiro/gtk] warning: unhandled key: ", key, "\n");
append(0);
}
#undef map

View File

@@ -8,7 +8,10 @@
#include "mouse.cpp"
#include "browser-window.cpp"
#include "message-window.cpp"
#include "object.cpp"
#include "group.cpp"
#include "hotkey.cpp"
#include "timer.cpp"
#include "window.cpp"
@@ -45,6 +48,7 @@
#include "widget/list-view.cpp"
#include "widget/list-view-column.cpp"
#include "widget/list-view-item.cpp"
#include "widget/list-view-cell.cpp"
#include "widget/progress-bar.cpp"
#include "widget/radio-button.cpp"
#include "widget/radio-label.cpp"

View File

@@ -3,7 +3,7 @@ namespace hiro {
struct pMenu;
struct pLayout;
struct pWidget;
};
}
#define Declare(Name, Base) \
p##Name(m##Name& reference) : p##Base(reference) {} \
@@ -19,7 +19,10 @@ namespace hiro {
#include "mouse.hpp"
#include "browser-window.hpp"
#include "message-window.hpp"
#include "object.hpp"
#include "group.hpp"
#include "hotkey.hpp"
#include "timer.hpp"
#include "window.hpp"
@@ -56,6 +59,7 @@ namespace hiro {
#include "widget/list-view.hpp"
#include "widget/list-view-column.hpp"
#include "widget/list-view-item.hpp"
#include "widget/list-view-cell.hpp"
#include "widget/progress-bar.hpp"
#include "widget/radio-button.hpp"
#include "widget/radio-label.hpp"

View File

@@ -12,6 +12,8 @@ static auto CreateColor(const Color& color) -> GdkColor {
#endif
static auto CreatePixbuf(const nall::image& image, bool scale = false) -> GdkPixbuf* {
if(!image) return nullptr;
nall::image gdkImage = image;
gdkImage.transform(0, 32, 255u << 24, 255u << 0, 255u << 8, 255u << 16);
if(scale) gdkImage.scale(15, 15);

View File

@@ -8,17 +8,12 @@ auto pComboButtonItem::construct() -> void {
auto pComboButtonItem::destruct() -> void {
}
auto pComboButtonItem::setIcon(const image& icon) -> void {
auto pComboButtonItem::setIcon(image icon) -> void {
if(auto parent = _parent()) {
if(icon) {
auto copy = icon;
auto size = pFont::size(self().font(true), " ").height();
copy.scale(size, size);
auto pixbuf = CreatePixbuf(copy);
gtk_list_store_set(parent->gtkListStore, &gtkIter, 0, pixbuf, -1);
} else {
gtk_list_store_set(parent->gtkListStore, &gtkIter, 0, nullptr, -1);
}
auto size = pFont::size(self().font(true), " ").height();
if(icon) icon.scale(size, size);
auto pixbuf = CreatePixbuf(icon);
gtk_list_store_set(parent->gtkListStore, &gtkIter, 0, pixbuf, -1);
}
}

View File

@@ -5,7 +5,7 @@ namespace hiro {
struct pComboButtonItem : pObject {
Declare(ComboButtonItem, Object)
auto setIcon(const image& icon) -> void;
auto setIcon(image icon) -> void;
auto setSelected() -> void;
auto setText(const string& text) -> void;

View File

@@ -17,10 +17,7 @@ auto pComboButton::construct() -> void {
gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(gtkWidget), gtkCellText, true);
gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(gtkWidget), gtkCellText, "text", 1, nullptr);
for(auto& item : state().items) {
append(item);
if(item->selected()) item->setSelected();
}
for(auto& item : state().items) append(item);
g_signal_connect(G_OBJECT(gtkWidget), "changed", G_CALLBACK(ComboButton_change), (gpointer)this);
@@ -33,11 +30,13 @@ auto pComboButton::destruct() -> void {
auto pComboButton::append(sComboButtonItem item) -> void {
lock();
if(auto delegate = item->self()) {
gtk_list_store_append(gtkListStore, &delegate->gtkIter);
item->setIcon(item->state.icon);
item->setText(item->state.text);
if(auto self = item->self()) {
gtk_list_store_append(gtkListStore, &self->gtkIter);
self->setIcon(item->state.icon);
if(item->state.selected) self->setSelected();
self->setText(item->state.text);
}
if(gtk_combo_box_get_active(gtkComboBox) < 0) item->setSelected();
unlock();
}
@@ -46,25 +45,19 @@ auto pComboButton::minimumSize() const -> Size {
signed maximumWidth = 0;
for(auto& item : state().items) {
maximumWidth = max(maximumWidth,
(item->state.icon ? 2 + pFont::size(font, " ").height() : 0)
(item->state.icon ? pFont::size(font, " ").height() + 2 : 0)
+ pFont::size(font, item->state.text).width()
);
}
Size size = pFont::size(font, " ");
return {maximumWidth + 40, size.height() + 12};
return {maximumWidth + 40, pFont::size(font, " ").height() + 12};
}
auto pComboButton::remove(sComboButtonItem item) -> void {
lock();
if(auto delegate = item->self()) {
gtk_list_store_remove(gtkListStore, &delegate->gtkIter);
//if the currently selected item is removed; GTK+ deselects everything
//detect this behavior and select the first item instead of nothing
if(gtk_combo_box_get_active(GTK_COMBO_BOX(gtkWidget)) < 0) {
if(gtk_tree_model_iter_n_children(gtkTreeModel, nullptr) > 0) {
gtk_combo_box_set_active(GTK_COMBO_BOX(gtkWidget), 0);
state().selected = 0;
}
if(gtk_combo_box_get_active(gtkComboBox) < 0) {
if(auto item = self().item(0)) item->setSelected();
}
}
unlock();
@@ -83,10 +76,13 @@ auto pComboButton::setFont(const string& font) -> void {
}
auto pComboButton::_updateSelected() -> void {
for(auto& item : state().items) item->state.selected = false;
signed selected = gtk_combo_box_get_active(gtkComboBox);
if(selected >= 0) {
state().selected = selected;
if(!locked()) self().doChange();
if(auto item = self().item(selected)) {
item->state.selected = true;
if(!locked()) self().doChange();
}
}
}

View File

@@ -0,0 +1,40 @@
#if defined(Hiro_ListView)
namespace hiro {
auto pListViewCell::construct() -> void {
}
auto pListViewCell::destruct() -> void {
}
auto pListViewCell::setBackgroundColor(Color color) -> void {
}
auto pListViewCell::setForegroundColor(Color color) -> void {
}
auto pListViewCell::setIcon(const image& icon) -> void {
if(auto item = _parent()) {
if(auto view = item->_parent()) {
gtk_list_store_set(view->gtkListStore, &item->gtkIter, 1 + self().offset() * 2, CreatePixbuf(icon), -1);
}
}
}
auto pListViewCell::setText(const string& text) -> void {
if(auto item = _parent()) {
if(auto view = item->_parent()) {
gtk_list_store_set(view->gtkListStore, &item->gtkIter, 1 + self().offset() * 2 + 1, text.data(), -1);
}
}
}
auto pListViewCell::_parent() -> pListViewItem* {
if(auto parent = self().parentListViewItem()) return parent->self();
return nullptr;
}
}
#endif

View File

@@ -0,0 +1,18 @@
#if defined(Hiro_ListView)
namespace hiro {
struct pListViewCell : pObject {
Declare(ListViewCell, Object)
auto setBackgroundColor(Color color) -> void;
auto setForegroundColor(Color color) -> void;
auto setIcon(const image& icon) -> void;
auto setText(const string& text) -> void;
auto _parent() -> pListViewItem*;
};
}
#endif

View File

@@ -63,6 +63,12 @@ auto pListViewColumn::setEditable(bool editable) -> void {
g_object_set(G_OBJECT(gtkCellText), "editable", editable ? TRUE : FALSE, nullptr);
}
auto pListViewColumn::setExpandable(bool expandable) -> void {
if(auto parent = _parent()) {
parent->resizeColumns();
}
}
auto pListViewColumn::setFont(const string& font) -> void {
pFont::setFont(gtkHeaderText, font);
auto fontDescription = pFont::create(font);
@@ -95,10 +101,6 @@ auto pListViewColumn::setResizable(bool resizable) -> void {
gtk_tree_view_column_set_resizable(gtkColumn, resizable);
}
auto pListViewColumn::setSortable(bool sortable) -> void {
gtk_tree_view_column_set_clickable(gtkColumn, sortable);
}
auto pListViewColumn::setText(const string& text) -> void {
gtk_label_set_text(GTK_LABEL(gtkHeaderText), text);
}

View File

@@ -8,12 +8,12 @@ struct pListViewColumn : pObject {
auto setActive() -> void;
auto setBackgroundColor(Color color) -> void;
auto setEditable(bool editable) -> void;
auto setExpandable(bool expandable) -> void;
auto setFont(const string& font) -> void override;
auto setForegroundColor(Color color) -> void;
auto setHorizontalAlignment(double alignment) -> void;
auto setIcon(const image& icon) -> void;
auto setResizable(bool resizable) -> void;
auto setSortable(bool sortable) -> void;
auto setText(const string& text) -> void;
auto setVerticalAlignment(double alignment) -> void;
auto setVisible(bool visible) -> void override;

View File

@@ -8,6 +8,15 @@ auto pListViewItem::construct() -> void {
auto pListViewItem::destruct() -> void {
}
auto pListViewItem::append(sListViewCell cell) -> void {
}
auto pListViewItem::remove(sListViewCell cell) -> void {
}
auto pListViewItem::setBackgroundColor(Color color) -> void {
}
auto pListViewItem::setChecked(bool checked) -> void {
if(auto parent = _parent()) {
gtk_list_store_set(parent->gtkListStore, &gtkIter, 0, checked, -1);
@@ -23,15 +32,7 @@ auto pListViewItem::setFocused() -> void {
}
}
auto pListViewItem::setIcon(unsigned column, const image& icon) -> void {
if(auto parent = _parent()) {
if(icon) {
auto pixbuf = CreatePixbuf(icon, true);
gtk_list_store_set(parent->gtkListStore, &gtkIter, 1 + column * 2, pixbuf, -1);
} else {
gtk_list_store_set(parent->gtkListStore, &gtkIter, 1 + column * 2, nullptr, -1);
}
}
auto pListViewItem::setForegroundColor(Color color) -> void {
}
auto pListViewItem::setSelected(bool selected) -> void {
@@ -47,12 +48,6 @@ auto pListViewItem::setSelected(bool selected) -> void {
}
}
auto pListViewItem::setText(unsigned column, const string& text) -> void {
if(auto parent = _parent()) {
gtk_list_store_set(parent->gtkListStore, &gtkIter, 1 + column * 2 + 1, text.data(), -1);
}
}
auto pListViewItem::_parent() -> pListView* {
if(auto parent = self().parentListView()) return parent->self();
return nullptr;

View File

@@ -5,11 +5,13 @@ namespace hiro {
struct pListViewItem : pObject {
Declare(ListViewItem, Object)
auto append(sListViewCell cell) -> void;
auto remove(sListViewCell cell) -> void;
auto setBackgroundColor(Color color) -> void;
auto setChecked(bool checked) -> void;
auto setFocused() -> void;
auto setIcon(unsigned column, const image& icon) -> void;
auto setForegroundColor(Color color) -> void;
auto setSelected(bool selected) -> void;
auto setText(unsigned column, const string& text) -> void;
auto _parent() -> pListView*;

View File

@@ -26,12 +26,13 @@ auto pListView::construct() -> void {
gtk_widget_show(gtkWidgetChild);
setBackgroundColor(state().backgroundColor);
setBatchable(state().batchable);
setCheckable(state().checkable);
setFont(self().font(true));
setForegroundColor(state().foregroundColor);
setGridVisible(state().gridVisible);
setHeaderVisible(state().headerVisible);
setMultiSelect(state().multiSelect);
setSortable(state().sortable);
g_signal_connect(G_OBJECT(gtkTreeView), "button-press-event", G_CALLBACK(ListView_buttonEvent), (gpointer)this);
g_signal_connect(G_OBJECT(gtkTreeView), "button-release-event", G_CALLBACK(ListView_buttonEvent), (gpointer)this);
@@ -56,9 +57,12 @@ auto pListView::append(sListViewColumn column) -> void {
column->setFont(column->font());
column->setForegroundColor(column->foregroundColor());
column->setHorizontalAlignment(column->horizontalAlignment());
column->setResizable(column->resizable());
column->setVerticalAlignment(column->verticalAlignment());
setCheckable(state().checkable);
setSortable(state().sortable);
_createModel();
resizeColumns();
gtk_tree_view_set_rules_hint(gtkTreeView, self().columns() >= 2); //two or more columns + checkbutton column
}
@@ -68,8 +72,18 @@ auto pListView::append(sListViewItem item) -> void {
item->setChecked(item->checked());
item->setSelected(item->selected());
for(auto column : range(self().columns())) {
item->setIcon(column, item->state.icon(column, {}));
item->setText(column, item->state.text(column, ""));
if(auto cell = item->cell(column)) {
if(auto self = cell->self()) {
self->setIcon(cell->state.icon);
self->setText(cell->state.text);
}
}
}
}
auto pListView::checkAll() -> void {
for(auto& item : state().items) {
if(auto delegate = item->self()) delegate->setChecked(true);
}
}
@@ -106,83 +120,61 @@ auto pListView::reset() -> void {
gtk_tree_view_set_rules_hint(gtkTreeView, false);
}
//column widths:
//< 0 = expanding (consume all remaining space)
// 0 = auto (resize to contents
//> 0 = fixed width
auto pListView::resizeColumns() -> void {
lock();
//compute the minimum width required for each column based upon the contents of all rows
vector<signed> minimumWidths;
for(auto column : range(self().columns())) {
signed maximumWidth = 1;
if(self().headerVisible()) {
maximumWidth = max(maximumWidth, 8 //margin
+ state().columns[column]->state.icon.width
+ Font::size(state().columns[column]->font(true), state().columns[column]->state.text).width()
);
vector<signed> widths;
signed minimumWidth = 0;
signed expandable = 0;
for(auto column : range(state().columns)) {
signed width = _width(column);
widths.append(width);
minimumWidth += width;
if(state().columns[column]->expandable()) expandable++;
}
signed maximumWidth = self().geometry().width() - 6;
if(auto scrollBar = gtk_scrolled_window_get_vscrollbar(gtkScrolledWindow)) {
if(gtk_widget_get_visible(scrollBar)) maximumWidth -= scrollBar->allocation.width;
}
signed expandWidth = 0;
if(expandable && maximumWidth > minimumWidth) {
expandWidth = (maximumWidth - minimumWidth) / expandable;
}
for(auto column : range(state().columns)) {
if(auto self = state().columns[column]->self()) {
signed width = widths[column];
if(self->state().expandable) width += expandWidth;
gtk_tree_view_column_set_fixed_width(self->gtkColumn, width);
}
for(auto row : range(self().items())) {
maximumWidth = max(maximumWidth, 8 //margin
+ (row == 0 && state().checkable ? 32 : 0) //check box
+ state().items[row]->state.icon(column, {}).width
+ Font::size(state().columns[column]->font(true), state().items[row]->state.text(column, "")).width()
);
}
if(!state().columns[column]->visible()) maximumWidth = 1;
minimumWidths.append(maximumWidth);
}
//subtract the widths of all non-expanding columns from the available widget space
signed expansions = 0; //count the number of expanded columns
signed emptyWidth = pSizable::state().geometry.width() - 5; //margin
for(auto column : range(self().columns())) {
signed width = state().columns[column]->width();
if(!state().columns[column]->visible()) width = 1;
if(width < 0) { expansions++; continue; }
if(width == 0) width = minimumWidths[column];
emptyWidth -= width;
}
//the vertical scroll bar consumes header space when visible; subtract it from available space if needed
auto scrollBar = gtk_scrolled_window_get_vscrollbar(gtkScrolledWindow);
if(scrollBar && gtk_widget_get_visible(scrollBar)) {
emptyWidth -= scrollBar->allocation.width;
}
//divide remaining space among all expanded columns
if(expansions && emptyWidth >= expansions) emptyWidth /= expansions;
else emptyWidth = 1;
for(auto column : range(self().columns())) {
signed width = state().columns[column]->width();
if(!state().columns[column]->visible()) width = 1;
if(width < 0) width = emptyWidth;
if(width == 0) width = minimumWidths[column];
gtk_tree_view_column_set_fixed_width(_column(column)->gtkColumn, width);
}
unlock();
}
auto pListView::selectAll() -> void {
for(auto& item : state().items) {
if(auto delegate = item->self()) delegate->setSelected(true);
}
}
auto pListView::setBackgroundColor(Color color) -> void {
GdkColor gdkColor = CreateColor(color);
gtk_widget_modify_base(gtkWidgetChild, GTK_STATE_NORMAL, color ? &gdkColor : nullptr);
}
auto pListView::setBatchable(bool batchable) -> void {
gtk_tree_selection_set_mode(gtkTreeSelection, batchable ? GTK_SELECTION_MULTIPLE : GTK_SELECTION_SINGLE);
}
auto pListView::setCheckable(bool checkable) -> void {
if(auto delegate = _column(0)) {
gtk_cell_renderer_set_visible(delegate->gtkCellToggle, checkable);
}
}
auto pListView::setChecked(bool checked) -> void {
for(auto& item : state().items) {
if(auto delegate = item->self()) delegate->setChecked(checked);
}
}
auto pListView::setFocused() -> void {
gtk_widget_grab_focus(gtkWidgetChild);
}
@@ -206,21 +198,60 @@ auto pListView::setHeaderVisible(bool visible) -> void {
gtk_tree_view_set_headers_visible(gtkTreeView, visible);
}
auto pListView::setMultiSelect(bool multiSelect) -> void {
gtk_tree_selection_set_mode(gtkTreeSelection, multiSelect ? GTK_SELECTION_MULTIPLE : GTK_SELECTION_SINGLE);
auto pListView::setSortable(bool sortable) -> void {
for(auto& column : state().columns) {
if(auto delegate = column->self()) {
gtk_tree_view_column_set_clickable(delegate->gtkColumn, sortable);
}
}
}
auto pListView::setSelected(bool selected) -> void {
auto pListView::uncheckAll() -> void {
for(auto& item : state().items) {
if(auto delegate = item->self()) delegate->setSelected(selected);
if(auto delegate = item->self()) delegate->setChecked(false);
}
}
auto pListView::unselectAll() -> void {
for(auto& item : state().items) {
if(auto delegate = item->self()) delegate->setSelected(false);
}
}
auto pListView::_cellWidth(unsigned _row, unsigned _column) -> unsigned {
unsigned width = 8; //margin
if(state().checkable && _column == 0) width += 32; //checkbox
if(auto item = self().item(_row)) {
if(auto cell = item->cell(_column)) {
if(auto& icon = cell->state.icon) {
width += icon.width + 2;
}
if(auto& text = cell->state.text) {
width += Font::size(cell->font(true), text).width();
}
}
}
return width;
}
auto pListView::_column(unsigned column) -> pListViewColumn* {
if(auto delegate = self().column(column)) return delegate->self();
return nullptr;
}
auto pListView::_columnWidth(unsigned _column) -> unsigned {
unsigned width = 8; //margin
if(auto column = self().column(_column)) {
if(auto& icon = column->state.icon) {
width += icon.width + 2;
}
if(auto& text = column->state.text) {
width += Font::size(column->font(true), text).width();
}
}
return width;
}
auto pListView::_createModel() -> void {
gtk_tree_view_set_model(gtkTreeView, nullptr);
gtkListStore = nullptr;
@@ -256,13 +287,16 @@ auto pListView::_doContext() -> void {
auto pListView::_doEdit(GtkCellRendererText* gtkCellRendererText, const char* path, const char* text) -> void {
for(auto& column : state().columns) {
if(auto delegate = column->self()) {
if(gtkCellRendererText = GTK_CELL_RENDERER_TEXT(delegate->gtkCellText)) {
if(auto item = self().item(decimal(path))) {
if(string{text} != item->text(column->offset())) {
item->setText(column->offset(), text);
if(!locked()) self().doEdit(item, column);
if(gtkCellRendererText == GTK_CELL_RENDERER_TEXT(delegate->gtkCellText)) {
auto row = decimal(path);
if(auto item = self().item(row)) {
if(auto cell = item->cell(column->offset())) {
if(string{text} != cell->state.text) {
cell->setText(text);
if(!locked()) self().doEdit(cell);
}
return;
}
return;
}
}
}
@@ -277,7 +311,7 @@ auto pListView::_doEvent(GdkEventButton* event) -> signed {
//when clicking in empty space below the last list view item; GTK+ does not deselect all items;
//below code enables this functionality, to match behavior with all other UI toolkits (and because it's very convenient to have)
if(path == nullptr && gtk_tree_selection_count_selected_rows(gtkTreeSelection) > 0) {
self().setSelected({});
self().unselectAll();
self().doChange();
return true;
}
@@ -373,6 +407,19 @@ auto pListView::_updateSelected() -> void {
if(!locked()) self().doChange();
}
auto pListView::_width(unsigned column) -> unsigned {
if(auto width = state().columns[column]->width()) return width;
unsigned width = 1;
if(!state().columns[column]->visible()) return width;
if(state().headerVisible) {
width = max(width, _columnWidth(column));
}
for(auto row : range(state().items)) {
width = max(width, _cellWidth(row, column));
}
return width;
}
}
#endif

View File

@@ -7,23 +7,28 @@ struct pListView : pWidget {
auto append(sListViewColumn column) -> void;
auto append(sListViewItem item) -> void;
auto checkAll() -> void;
auto focused() -> bool;
auto remove(sListViewColumn column) -> void;
auto remove(sListViewItem item) -> void;
auto reset() -> void;
auto resizeColumns() -> void;
auto selectAll() -> void;
auto setBackgroundColor(Color color) -> void;
auto setBatchable(bool batchable) -> void;
auto setCheckable(bool checkable) -> void;
auto setChecked(bool checked) -> void;
auto setFocused() -> void override;
auto setFont(const string& font) -> void override;
auto setForegroundColor(Color color) -> void;
auto setGridVisible(bool visible) -> void;
auto setHeaderVisible(bool visible) -> void;
auto setMultiSelect(bool multiSelect) -> void;
auto setSelected(bool selected) -> void;
auto setSortable(bool sortable) -> void;
auto uncheckAll() -> void;
auto unselectAll() -> void;
auto _cellWidth(unsigned row, unsigned column) -> unsigned;
auto _column(unsigned column) -> pListViewColumn*;
auto _columnWidth(unsigned column) -> unsigned;
auto _createModel() -> void;
auto _doActivate() -> void;
auto _doChange() -> void;
@@ -34,6 +39,7 @@ struct pListView : pWidget {
auto _doMouseMove() -> signed;
auto _doToggle(const char* path) -> void;
auto _updateSelected() -> void;
auto _width(unsigned column) -> unsigned;
GtkScrolledWindow* gtkScrolledWindow = nullptr;
GtkWidget* gtkWidgetChild = nullptr;

View File

@@ -3,7 +3,7 @@
namespace hiro {
static auto RadioButton_activate(GtkToggleButton*, pRadioButton* p) -> void {
if(p->_parent().locked()) return;
if(p->groupLocked()) return;
bool wasChecked = p->state().checked;
p->setChecked();
if(!wasChecked) p->self().doActivate();
@@ -12,7 +12,6 @@ static auto RadioButton_activate(GtkToggleButton*, pRadioButton* p) -> void {
auto pRadioButton::construct() -> void {
gtkWidget = gtk_toggle_button_new();
setGroup(state().group);
setBordered(state().bordered);
setIcon(state().icon);
setOrientation(state().orientation);
@@ -48,26 +47,34 @@ auto pRadioButton::setBordered(bool bordered) -> void {
}
auto pRadioButton::setChecked() -> void {
_parent().lock();
for(auto& weak : state().group) {
if(auto item = weak.acquire()) {
if(item->self()) {
bool checked = item->self() == this;
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(item->self()->gtkWidget), item->state.checked = checked);
if(!self().group()) return;
for(auto& weak : self().group()->state.objects) {
if(auto object = weak.acquire()) {
if(auto radioButton = dynamic_cast<mRadioButton*>(object.data())) {
if(auto self = radioButton->self()) {
self->lock();
bool checked = self == this;
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->gtkWidget), radioButton->state.checked = checked);
self->unlock();
}
}
}
}
_parent().unlock();
}
auto pRadioButton::setGroup(const vector<wRadioButton>& group) -> void {
_parent().lock();
for(auto& weak : state().group) {
if(auto item = weak.acquire()) {
if(item->self()) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(item->self()->gtkWidget), item->checked());
auto pRadioButton::setGroup(sGroup group) -> void {
if(!group) return;
for(auto& weak : group->state.objects) {
if(auto object = weak.acquire()) {
if(auto radioButton = dynamic_cast<mRadioButton*>(object.data())) {
if(auto self = radioButton->self()) {
self->lock();
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->gtkWidget), radioButton->checked());
self->unlock();
}
}
}
}
_parent().unlock();
}
auto pRadioButton::setIcon(const image& icon) -> void {
@@ -91,13 +98,18 @@ auto pRadioButton::setText(const string& text) -> void {
setFont(self().font(true)); //gtk_button_set_label() recreates label, which destroys currently assigned font
}
auto pRadioButton::_parent() -> pRadioButton& {
if(state().group.size()) {
if(auto item = state().group.first().acquire()) {
if(item->self()) return *item->self();
auto pRadioButton::groupLocked() const -> bool {
if(auto group = state().group) {
for(auto& weak : group->state.objects) {
if(auto object = weak.acquire()) {
if(auto self = object->self()) {
if(self->locked()) return true;
}
}
}
return false;
}
return *this;
return locked();
}
}

View File

@@ -8,12 +8,12 @@ struct pRadioButton : pWidget {
auto minimumSize() const -> Size;
auto setBordered(bool bordered) -> void;
auto setChecked() -> void;
auto setGroup(const vector<wRadioButton>& group) -> void;
auto setGroup(sGroup group) -> void;
auto setIcon(const image& icon) -> void;
auto setOrientation(Orientation orientation) -> void;
auto setText(const string& text) -> void;
auto _parent() -> pRadioButton&;
auto groupLocked() const -> bool;
};
}

View File

@@ -3,7 +3,7 @@
namespace hiro {
static auto RadioLabel_activate(GtkToggleButton*, pRadioLabel* p) -> void {
if(p->_parent().locked()) return;
if(p->groupLocked()) return;
bool wasChecked = p->state().checked;
p->setChecked();
if(!wasChecked) p->self().doActivate();
@@ -11,8 +11,9 @@ static auto RadioLabel_activate(GtkToggleButton*, pRadioLabel* p) -> void {
auto pRadioLabel::construct() -> void {
gtkWidget = gtk_radio_button_new_with_label(nullptr, "");
gtkToggleButton = GTK_TOGGLE_BUTTON(gtkWidget);
gtkRadioButton = GTK_RADIO_BUTTON(gtkWidget);
setGroup(state().group);
setText(state().text);
g_signal_connect(G_OBJECT(gtkWidget), "toggled", G_CALLBACK(RadioLabel_activate), (gpointer)this);
@@ -30,30 +31,29 @@ auto pRadioLabel::minimumSize() const -> Size {
}
auto pRadioLabel::setChecked() -> void {
_parent().lock();
for(auto& weak : state().group) {
if(auto item = weak.acquire()) item->state.checked = false;
}
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gtkWidget), state().checked = true);
_parent().unlock();
lock();
gtk_toggle_button_set_active(gtkToggleButton, true);
unlock();
}
auto pRadioLabel::setGroup(const vector<shared_pointer_weak<mRadioLabel>>& group) -> void {
if(&_parent() == this) return;
_parent().lock();
gtk_radio_button_set_group(
GTK_RADIO_BUTTON(gtkWidget),
gtk_radio_button_get_group(GTK_RADIO_BUTTON(_parent().gtkWidget))
);
for(auto& weak : state().group) {
if(auto item = weak.acquire()) {
if(item->self() && item->checked()) {
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(item->self()->gtkWidget), true);
break;
auto pRadioLabel::setGroup(sGroup group) -> void {
if(!group) return;
maybe<GtkRadioButton*> gtkRadioButton;
for(auto& weak : group->state.objects) {
if(auto object = weak.acquire()) {
if(auto radioLabel = dynamic_cast<mRadioLabel*>(object.data())) {
if(auto self = radioLabel->self()) {
self->lock();
gtk_radio_button_set_group(self->gtkRadioButton, nullptr);
if(!gtkRadioButton) gtkRadioButton = self->gtkRadioButton;
else gtk_radio_button_set_group(self->gtkRadioButton, gtk_radio_button_get_group(*gtkRadioButton));
gtk_toggle_button_set_active(self->gtkToggleButton, radioLabel->checked());
self->unlock();
}
}
}
}
_parent().unlock();
}
auto pRadioLabel::setText(const string& text) -> void {
@@ -61,13 +61,18 @@ auto pRadioLabel::setText(const string& text) -> void {
setFont(self().font(true)); //gtk_button_set_label() recreates label, which destroys currently assigned font
}
auto pRadioLabel::_parent() -> pRadioLabel& {
if(state().group.size()) {
if(auto item = state().group.first().acquire()) {
if(item->self()) return *item->self();
auto pRadioLabel::groupLocked() const -> bool {
if(auto group = state().group) {
for(auto& weak : group->state.objects) {
if(auto object = weak.acquire()) {
if(auto self = object->self()) {
if(self->locked()) return true;
}
}
}
return false;
}
return *this;
return locked();
}
}

View File

@@ -7,10 +7,13 @@ struct pRadioLabel : pWidget {
auto minimumSize() const -> Size;
auto setChecked() -> void;
auto setGroup(const vector<shared_pointer_weak<mRadioLabel>>& group) -> void;
auto setGroup(sGroup group) -> void;
auto setText(const string& text) -> void;
auto _parent() -> pRadioLabel&;
auto groupLocked() const -> bool;
GtkToggleButton* gtkToggleButton = nullptr;
GtkRadioButton* gtkRadioButton = nullptr;
};
}

View File

@@ -10,6 +10,12 @@ auto pTabFrameItem::destruct() -> void {
if(auto layout = state().layout) layout->destruct();
}
auto pTabFrameItem::append(sLayout layout) -> void {
}
auto pTabFrameItem::remove(sLayout layout) -> void {
}
auto pTabFrameItem::setClosable(bool closable) -> void {
if(auto parent = _parent()) {
parent->setItemClosable(self().offset(), closable);

View File

@@ -5,6 +5,8 @@ namespace hiro {
struct pTabFrameItem : pObject {
Declare(TabFrameItem, Object)
auto append(sLayout layout) -> void;
auto remove(sLayout layout) -> void;
auto setClosable(bool closable) -> void;
auto setIcon(const image& icon) -> void;
auto setMovable(bool movable) -> void;

View File

@@ -3,7 +3,9 @@
namespace hiro {
static auto TabFrame_change(GtkNotebook* notebook, GtkWidget* page, unsigned position, pTabFrame* p) -> void {
p->state().selected = position;
for(auto& item : p->state().items) item->state.selected = false;
if(auto item = p->self().item(position)) item->state.selected = true;
p->_synchronizeLayout();
if(!p->locked()) p->self().doChange();
}
@@ -22,7 +24,10 @@ static auto TabFrame_close(GtkButton* button, pTabFrame* p) -> void {
}
static auto TabFrame_move(GtkNotebook* notebook, GtkWidget* page, unsigned moveTo, pTabFrame* p) -> void {
p->state().selected = gtk_notebook_get_current_page(notebook);
unsigned position = gtk_notebook_get_current_page(notebook);
for(auto& item : p->state().items) item->state.selected = false;
if(auto item = p->self().item(position)) item->state.selected = true;
maybe<unsigned> moveFrom;
for(auto n : range(p->tabs)) {
if(page == p->tabs[n].child) {
@@ -45,7 +50,6 @@ auto pTabFrame::construct() -> void {
tabs.reset(); //todo: memory leak, need to release each tab
for(auto& item : state().items) append(item);
setEdge(state().edge);
setItemSelected(state().selected);
g_signal_connect(G_OBJECT(gtkWidget), "page-reordered", G_CALLBACK(TabFrame_move), (gpointer)this);
g_signal_connect(G_OBJECT(gtkWidget), "switch-page", G_CALLBACK(TabFrame_change), (gpointer)this);
@@ -87,6 +91,7 @@ auto pTabFrame::append(sTabFrameItem item) -> void {
setFont(self().font(true));
setItemMovable(item->offset(), item->movable());
if(item->selected()) setItemSelected(item->offset());
_synchronizeTab(tabs.size() - 1);
setGeometry(self().geometry());
unlock();
@@ -128,7 +133,10 @@ auto pTabFrame::remove(sTabFrameItem item) -> void {
}
tabs.remove(item->offset());
gtk_notebook_remove_page(GTK_NOTEBOOK(gtkWidget), item->offset());
state().selected = gtk_notebook_get_current_page(GTK_NOTEBOOK(gtkWidget));
unsigned position = gtk_notebook_get_current_page(GTK_NOTEBOOK(gtkWidget));
for(auto& item : state().items) item->state.selected = false;
if(auto item = self().item(position)) item->state.selected = true;
unlock();
}
@@ -218,14 +226,12 @@ auto pTabFrame::setVisible(bool visible) -> void {
}
auto pTabFrame::_synchronizeLayout() -> void {
unsigned position = 0;
for(auto& item : state().items) {
if(auto layout = item->state.layout) {
if(layout->self()) {
layout->self()->setVisible(layout->visible(true) && position == state().selected);
if(auto self = layout->self()) {
self->setVisible(layout->visible(true) && item->selected());
}
}
position++;
}
}

View File

@@ -184,13 +184,16 @@ auto pWindow::construct() -> void {
auto pWindow::destruct() -> void {
}
auto pWindow::append(shared_pointer<mMenuBar> menuBar) -> void {
auto pWindow::append(sLayout layout) -> void {
}
auto pWindow::append(sMenuBar menuBar) -> void {
_setMenuEnabled(menuBar->enabled(true));
_setMenuFont(menuBar->font(true));
_setMenuVisible(menuBar->visible(true));
}
auto pWindow::append(shared_pointer<mStatusBar> statusBar) -> void {
auto pWindow::append(sStatusBar statusBar) -> void {
_setStatusEnabled(statusBar->enabled(true));
_setStatusFont(statusBar->font(true));
_setStatusText(statusBar->text());
@@ -215,11 +218,14 @@ auto pWindow::frameMargin() const -> Geometry {
};
}
auto pWindow::remove(shared_pointer<mMenuBar> menuBar) -> void {
auto pWindow::remove(sLayout layout) -> void {
}
auto pWindow::remove(sMenuBar menuBar) -> void {
_setMenuVisible(false);
}
auto pWindow::remove(shared_pointer<mStatusBar> statusBar) -> void {
auto pWindow::remove(sStatusBar statusBar) -> void {
_setStatusVisible(false);
}

View File

@@ -14,12 +14,14 @@ struct pWindow : pObject {
GtkAllocation lastAllocation = {0};
bool onSizePending = false;
auto append(shared_pointer<mMenuBar> menuBar) -> void;
auto append(shared_pointer<mStatusBar> statusBar) -> void;
auto append(sLayout layout) -> void;
auto append(sMenuBar menuBar) -> void;
auto append(sStatusBar statusBar) -> void;
auto focused() const -> bool override;
auto frameMargin() const -> Geometry;
auto remove(shared_pointer<mMenuBar> menuBar) -> void;
auto remove(shared_pointer<mStatusBar> statusBar) -> void;
auto remove(sLayout layout) -> void;
auto remove(sMenuBar menuBar) -> void;
auto remove(sStatusBar statusBar) -> void;
auto setBackgroundColor(Color color) -> void;
auto setDroppable(bool droppable) -> void;
auto setEnabled(bool enabled) -> void override;