mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-08-31 19:51:52 +02:00
Update to v094r40 release.
byuu says: Changelog: - updated to newest hiro API - SFC performance profile builds once again - hiro: Qt port completed Errata 1: the hiro/Qt target won't run tomoko just yet. Starts by crashing inside InputSettings because hiro/Qt isn't forcefully selecting the first item added to a ComboButton just yet. Even with a monkey patch to get around that, the UI is incredibly unstable. Lots of geometry calculation bugs, and a crash when you try and access certain folders in the browser dialog. Lots of work left to be done there, sadly. Errata 2: the hiro/Windows port has black backgrounds on all ListView items. It's because I need to test for unassigned colors and grab the default Windows brush colors in those cases. Note: alternating row colors on multi-column ListView widgets is gone now. Not a bug. May add it back later, but I'm not sure. It doesn't interact nicely with per-cell background colors. Things left to do: First, I have to fix the Windows and Qt target bugs. Next, I need to go through and revise the hiro API even more (nothing too major.) Next, I need to update icarus to use the new hiro API, and add support for the SFC games database. Next, I have to rewrite my TSV->BML cheat code tool. Next, I need to post a final WIP of higan+icarus publicly and wait a few days. Next, I need to fix any bugs reported from the final WIP that I can. Finally, I should be able to release v095.
This commit is contained in:
@@ -8,7 +8,7 @@ auto pMenuBar::construct() -> void {
|
||||
auto pMenuBar::destruct() -> void {
|
||||
}
|
||||
|
||||
auto pMenuBar::append(shared_pointer<mMenu> menu) -> void {
|
||||
auto pMenuBar::append(sMenu menu) -> void {
|
||||
if(auto parent = _parent()) {
|
||||
parent->_append(*menu);
|
||||
if(menu->self()) {
|
||||
@@ -18,7 +18,7 @@ auto pMenuBar::append(shared_pointer<mMenu> menu) -> void {
|
||||
}
|
||||
}
|
||||
|
||||
auto pMenuBar::remove(shared_pointer<mMenu> menu) -> void {
|
||||
auto pMenuBar::remove(sMenu menu) -> void {
|
||||
}
|
||||
|
||||
auto pMenuBar::setEnabled(bool enabled) -> void {
|
||||
|
@@ -5,8 +5,8 @@ namespace hiro {
|
||||
struct pMenuBar : pObject {
|
||||
Declare(MenuBar, Object)
|
||||
|
||||
auto append(shared_pointer<mMenu> menu) -> void;
|
||||
auto remove(shared_pointer<mMenu> menu) -> void;
|
||||
auto append(sMenu menu) -> void;
|
||||
auto remove(sMenu menu) -> void;
|
||||
auto setEnabled(bool enabled) -> void override;
|
||||
auto setFont(const string& font) -> void override;
|
||||
auto setVisible(bool visible) -> void override;
|
||||
|
@@ -46,6 +46,7 @@
|
||||
#include "widget/label.cpp"
|
||||
#include "widget/line-edit.cpp"
|
||||
#include "widget/list-view.cpp"
|
||||
#include "widget/list-view-header.cpp"
|
||||
#include "widget/list-view-column.cpp"
|
||||
#include "widget/list-view-item.cpp"
|
||||
#include "widget/list-view-cell.cpp"
|
||||
|
@@ -57,6 +57,7 @@ namespace hiro {
|
||||
#include "widget/label.hpp"
|
||||
#include "widget/line-edit.hpp"
|
||||
#include "widget/list-view.hpp"
|
||||
#include "widget/list-view-header.hpp"
|
||||
#include "widget/list-view-column.hpp"
|
||||
#include "widget/list-view-item.hpp"
|
||||
#include "widget/list-view-cell.hpp"
|
||||
|
@@ -1,16 +1,5 @@
|
||||
namespace hiro {
|
||||
|
||||
void Settings::load() {
|
||||
string path = {configpath(), "hiro/"};
|
||||
Configuration::Document::load({path, "gtk.bml"});
|
||||
}
|
||||
|
||||
void Settings::save() {
|
||||
string path = {configpath(), "hiro/"};
|
||||
directory::create(path, 0755);
|
||||
Configuration::Document::save({path, "gtk.bml"});
|
||||
}
|
||||
|
||||
Settings::Settings() {
|
||||
geometry.append(geometry.frameX = 4, "FrameX");
|
||||
geometry.append(geometry.frameY = 24, "FrameY");
|
||||
@@ -23,4 +12,15 @@ Settings::Settings() {
|
||||
append(window, "Window");
|
||||
}
|
||||
|
||||
auto Settings::load() -> void {
|
||||
string path = {configpath(), "hiro/"};
|
||||
Configuration::Document::load({path, "gtk.bml"});
|
||||
}
|
||||
|
||||
auto Settings::save() -> void {
|
||||
string path = {configpath(), "hiro/"};
|
||||
directory::create(path, 0755);
|
||||
Configuration::Document::save({path, "gtk.bml"});
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -16,9 +16,9 @@ struct Settings : Configuration::Document {
|
||||
unsigned backgroundColor;
|
||||
} window;
|
||||
|
||||
void load();
|
||||
void save();
|
||||
Settings();
|
||||
auto load() -> void;
|
||||
auto save() -> void;
|
||||
};
|
||||
|
||||
static Settings* settings = nullptr;
|
||||
|
@@ -52,12 +52,12 @@ auto pHexEdit::construct() -> void {
|
||||
gtk_widget_show(subWidget);
|
||||
gtk_widget_show(container);
|
||||
|
||||
setAddress(state().address);
|
||||
setBackgroundColor(state().backgroundColor);
|
||||
setColumns(state().columns);
|
||||
setForegroundColor(state().foregroundColor);
|
||||
setRows(state().rows);
|
||||
setLength(state().length);
|
||||
setOffset(state().offset);
|
||||
update();
|
||||
|
||||
g_signal_connect(G_OBJECT(subWidget), "key-press-event", G_CALLBACK(HexEdit_keyPress), (gpointer)this);
|
||||
@@ -80,6 +80,12 @@ auto pHexEdit::focused() const -> bool {
|
||||
return GTK_WIDGET_HAS_FOCUS(subWidget) || GTK_WIDGET_HAS_FOCUS(scrollBar);
|
||||
}
|
||||
|
||||
auto pHexEdit::setAddress(unsigned address) -> void {
|
||||
setScroll();
|
||||
updateScroll();
|
||||
update();
|
||||
}
|
||||
|
||||
auto pHexEdit::setBackgroundColor(Color color) -> void {
|
||||
GdkColor gdkColor = CreateColor(color);
|
||||
gtk_widget_modify_base(subWidget, GTK_STATE_NORMAL, color ? &gdkColor : nullptr);
|
||||
@@ -100,12 +106,6 @@ auto pHexEdit::setLength(unsigned length) -> void {
|
||||
update();
|
||||
}
|
||||
|
||||
auto pHexEdit::setOffset(unsigned offset) -> void {
|
||||
setScroll();
|
||||
updateScroll();
|
||||
update();
|
||||
}
|
||||
|
||||
auto pHexEdit::setRows(unsigned rows) -> void {
|
||||
setScroll();
|
||||
update();
|
||||
@@ -120,16 +120,16 @@ auto pHexEdit::update() -> void {
|
||||
unsigned position = cursorPosition();
|
||||
|
||||
string output;
|
||||
unsigned offset = state().offset;
|
||||
unsigned address = state().address;
|
||||
for(auto row : range(state().rows)) {
|
||||
output.append(hex(offset, 8L));
|
||||
output.append(hex(address, 8L));
|
||||
output.append(" ");
|
||||
|
||||
string hexdata;
|
||||
string ansidata = " ";
|
||||
for(auto column : range(state().columns)) {
|
||||
if(offset < state().length) {
|
||||
uint8_t data = self().doRead(offset++);
|
||||
if(address < state().length) {
|
||||
uint8_t data = self().doRead(address++);
|
||||
hexdata.append(hex(data, 2L));
|
||||
hexdata.append(" ");
|
||||
ansidata.append(data >= 0x20 && data <= 0x7e ? (char)data : '.');
|
||||
@@ -141,7 +141,7 @@ auto pHexEdit::update() -> void {
|
||||
|
||||
output.append(hexdata);
|
||||
output.append(ansidata);
|
||||
if(offset >= state().length) break;
|
||||
if(address >= state().length) break;
|
||||
if(row != state().rows - 1) output.append("\n");
|
||||
}
|
||||
|
||||
@@ -179,9 +179,9 @@ auto pHexEdit::keyPress(unsigned scancode, unsigned mask) -> bool {
|
||||
if(scancode == GDK_Up) {
|
||||
if(cursorY != 0) return false;
|
||||
|
||||
signed newOffset = state().offset - state().columns;
|
||||
if(newOffset >= 0) {
|
||||
self().setOffset(newOffset);
|
||||
signed newAddress = state().address - state().columns;
|
||||
if(newAddress >= 0) {
|
||||
self().setAddress(newAddress);
|
||||
update();
|
||||
}
|
||||
return true;
|
||||
@@ -191,34 +191,34 @@ auto pHexEdit::keyPress(unsigned scancode, unsigned mask) -> bool {
|
||||
if(cursorY >= rows() - 1) return true;
|
||||
if(cursorY != state().rows - 1) return false;
|
||||
|
||||
signed newOffset = state().offset + state().columns;
|
||||
if(newOffset + state().columns * state().rows - (state().columns - 1) <= state().length) {
|
||||
self().setOffset(newOffset);
|
||||
signed newAddress = state().address + state().columns;
|
||||
if(newAddress + state().columns * state().rows - (state().columns - 1) <= state().length) {
|
||||
self().setAddress(newAddress);
|
||||
update();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if(scancode == GDK_Page_Up) {
|
||||
signed newOffset = state().offset - state().columns * state().rows;
|
||||
if(newOffset >= 0) {
|
||||
self().setOffset(newOffset);
|
||||
signed newAddress = state().address - state().columns * state().rows;
|
||||
if(newAddress >= 0) {
|
||||
self().setAddress(newAddress);
|
||||
} else {
|
||||
self().setOffset(0);
|
||||
self().setAddress(0);
|
||||
}
|
||||
update();
|
||||
return true;
|
||||
}
|
||||
|
||||
if(scancode == GDK_Page_Down) {
|
||||
signed newOffset = state().offset + state().columns * state().rows;
|
||||
signed newAddress = state().address + state().columns * state().rows;
|
||||
for(auto n : range(state().rows)) {
|
||||
if(newOffset + state().columns * state().rows - (state().columns - 1) <= state().length) {
|
||||
self().setOffset(newOffset);
|
||||
if(newAddress + state().columns * state().rows - (state().columns - 1) <= state().length) {
|
||||
self().setAddress(newAddress);
|
||||
update();
|
||||
break;
|
||||
}
|
||||
newOffset -= state().columns;
|
||||
newAddress -= state().columns;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -238,10 +238,10 @@ auto pHexEdit::keyPress(unsigned scancode, unsigned mask) -> bool {
|
||||
cursorX /= 3;
|
||||
if(cursorX < state().columns) {
|
||||
//not in ANSI region
|
||||
unsigned offset = state().offset + (cursorY * state().columns + cursorX);
|
||||
unsigned address = state().address + (cursorY * state().columns + cursorX);
|
||||
|
||||
if(offset >= state().length) return false; //do not edit past end of data
|
||||
uint8_t data = self().doRead(offset);
|
||||
if(address >= state().length) return false; //do not edit past end of data
|
||||
uint8_t data = self().doRead(address);
|
||||
|
||||
//write modified value
|
||||
if(cursorNibble == 1) {
|
||||
@@ -249,7 +249,7 @@ auto pHexEdit::keyPress(unsigned scancode, unsigned mask) -> bool {
|
||||
} else {
|
||||
data = (data & 0x0f) | (scancode << 4);
|
||||
}
|
||||
self().doWrite(offset, data);
|
||||
self().doWrite(address, data);
|
||||
|
||||
//auto-advance cursor to next nibble/byte
|
||||
position++;
|
||||
@@ -278,7 +278,7 @@ auto pHexEdit::rowsScrollable() -> signed {
|
||||
auto pHexEdit::scroll(signed position) -> void {
|
||||
if(position > rowsScrollable()) position = rowsScrollable();
|
||||
if(position < 0) position = 0;
|
||||
self().setOffset(position * state().columns);
|
||||
self().setAddress(position * state().columns);
|
||||
}
|
||||
|
||||
auto pHexEdit::setCursorPosition(unsigned position) -> void {
|
||||
@@ -304,7 +304,7 @@ auto pHexEdit::setScroll() -> void {
|
||||
}
|
||||
|
||||
auto pHexEdit::updateScroll() -> void {
|
||||
unsigned row = state().offset / state().columns;
|
||||
unsigned row = state().address / state().columns;
|
||||
gtk_range_set_value(GTK_RANGE(scrollBar), row);
|
||||
}
|
||||
|
||||
|
@@ -6,11 +6,11 @@ struct pHexEdit : pWidget {
|
||||
Declare(HexEdit, Widget)
|
||||
|
||||
auto focused() const -> bool override;
|
||||
auto setAddress(unsigned address) -> void;
|
||||
auto setBackgroundColor(Color color) -> void;
|
||||
auto setColumns(unsigned columns) -> void;
|
||||
auto setForegroundColor(Color color) -> void;
|
||||
auto setLength(unsigned length) -> void;
|
||||
auto setOffset(unsigned offset) -> void;
|
||||
auto setRows(unsigned rows) -> void;
|
||||
auto update() -> void;
|
||||
|
||||
|
@@ -116,7 +116,7 @@ auto pIconView::setGeometry(Geometry geometry) -> void {
|
||||
}
|
||||
|
||||
auto pIconView::setItemIcon(unsigned position, const image& icon) -> void {
|
||||
if(position >= self().items()) return;
|
||||
if(position >= self().itemCount()) return;
|
||||
GtkTreeIter iter;
|
||||
if(gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(store), &iter, string{position})) {
|
||||
if(icon) {
|
||||
@@ -129,7 +129,7 @@ auto pIconView::setItemIcon(unsigned position, const image& icon) -> void {
|
||||
}
|
||||
|
||||
auto pIconView::setItemSelected(unsigned position, bool selected) -> void {
|
||||
if(position >= self().items()) return;
|
||||
if(position >= self().itemCount()) return;
|
||||
lock();
|
||||
GtkTreePath* path = gtk_tree_path_new_from_string(string{position});
|
||||
if(selected) {
|
||||
@@ -165,7 +165,7 @@ auto pIconView::setItemSelectedNone() -> void {
|
||||
}
|
||||
|
||||
auto pIconView::setItemText(unsigned position, const string& text) -> void {
|
||||
if(position >= self().items()) return;
|
||||
if(position >= self().itemCount()) return;
|
||||
GtkTreeIter iter;
|
||||
if(gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(store), &iter, string{position})) {
|
||||
gtk_list_store_set(store, &iter, 1, (const char*)text, -1);
|
||||
@@ -216,7 +216,7 @@ auto pIconView::_updateSelected() -> void {
|
||||
currentSelection = selected;
|
||||
for(auto& item : state().items) item->state.selected = false;
|
||||
for(auto& position : currentSelection) {
|
||||
if(position >= self().items()) continue;
|
||||
if(position >= self().itemCount()) continue;
|
||||
state().items[position]->state.selected = true;
|
||||
}
|
||||
|
||||
|
@@ -5,7 +5,7 @@ namespace hiro {
|
||||
auto pLabel::construct() -> void {
|
||||
gtkWidget = gtk_label_new("");
|
||||
|
||||
_setAlignment();
|
||||
setAlignment(state().alignment);
|
||||
setText(state().text);
|
||||
|
||||
pWidget::construct();
|
||||
@@ -16,30 +16,23 @@ auto pLabel::destruct() -> void {
|
||||
}
|
||||
|
||||
auto pLabel::minimumSize() const -> Size {
|
||||
Size size = pFont::size(self().font(true), state().text);
|
||||
auto size = pFont::size(self().font(true), state().text);
|
||||
return {size.width(), size.height()};
|
||||
}
|
||||
|
||||
auto pLabel::setHorizontalAlignment(double alignment) -> void {
|
||||
_setAlignment();
|
||||
auto pLabel::setAlignment(Alignment alignment) -> void {
|
||||
if(!alignment) alignment = {0.0, 0.5};
|
||||
gtk_misc_set_alignment(GTK_MISC(gtkWidget), alignment.horizontal(), alignment.vertical());
|
||||
auto justify = GTK_JUSTIFY_CENTER;
|
||||
if(alignment.horizontal() < 0.333) justify = GTK_JUSTIFY_LEFT;
|
||||
if(alignment.horizontal() > 0.666) justify = GTK_JUSTIFY_RIGHT;
|
||||
gtk_label_set_justify(GTK_LABEL(gtkWidget), justify);
|
||||
}
|
||||
|
||||
auto pLabel::setText(const string& text) -> void {
|
||||
gtk_label_set_text(GTK_LABEL(gtkWidget), text);
|
||||
}
|
||||
|
||||
auto pLabel::setVerticalAlignment(double alignment) -> void {
|
||||
_setAlignment();
|
||||
}
|
||||
|
||||
auto pLabel::_setAlignment() -> void {
|
||||
gtk_misc_set_alignment(GTK_MISC(gtkWidget), state().horizontalAlignment, state().verticalAlignment);
|
||||
auto justify = GTK_JUSTIFY_CENTER;
|
||||
if(state().horizontalAlignment < 0.333) justify = GTK_JUSTIFY_LEFT;
|
||||
if(state().horizontalAlignment > 0.666) justify = GTK_JUSTIFY_RIGHT;
|
||||
gtk_label_set_justify(GTK_LABEL(gtkWidget), justify);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@@ -6,11 +6,8 @@ struct pLabel : pWidget {
|
||||
Declare(Label, Widget)
|
||||
|
||||
auto minimumSize() const -> Size override;
|
||||
auto setHorizontalAlignment(double alignment) -> void;
|
||||
auto setAlignment(Alignment alignment) -> void;
|
||||
auto setText(const string& text) -> void;
|
||||
auto setVerticalAlignment(double alignment) -> void;
|
||||
|
||||
auto _setAlignment() -> void;
|
||||
};
|
||||
|
||||
}
|
||||
|
@@ -3,36 +3,58 @@
|
||||
namespace hiro {
|
||||
|
||||
auto pListViewCell::construct() -> void {
|
||||
_setState();
|
||||
}
|
||||
|
||||
auto pListViewCell::destruct() -> void {
|
||||
}
|
||||
|
||||
auto pListViewCell::setAlignment(Alignment alignment) -> void {
|
||||
}
|
||||
|
||||
auto pListViewCell::setBackgroundColor(Color color) -> void {
|
||||
}
|
||||
|
||||
auto pListViewCell::setCheckable(bool checkable) -> void {
|
||||
}
|
||||
|
||||
auto pListViewCell::setChecked(bool checked) -> void {
|
||||
_setState();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
_setState();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
_setState();
|
||||
}
|
||||
|
||||
auto pListViewCell::_parent() -> pListViewItem* {
|
||||
if(auto parent = self().parentListViewItem()) return parent->self();
|
||||
return nullptr;
|
||||
auto pListViewCell::_grandparent() -> maybe<pListView&> {
|
||||
if(auto parent = _parent()) return parent->_parent();
|
||||
return nothing;
|
||||
}
|
||||
|
||||
auto pListViewCell::_parent() -> maybe<pListViewItem&> {
|
||||
if(auto parent = self().parentListViewItem()) {
|
||||
if(auto self = parent->self()) return *self;
|
||||
}
|
||||
return nothing;
|
||||
}
|
||||
|
||||
auto pListViewCell::_setState() -> void {
|
||||
if(auto parent = _parent()) {
|
||||
if(auto grandparent = _grandparent()) {
|
||||
grandparent->lock();
|
||||
gtk_list_store_set(grandparent->gtkListStore, &parent->gtkIter, 3 * self().offset() + 0, state().checked, -1);
|
||||
gtk_list_store_set(grandparent->gtkListStore, &parent->gtkIter, 3 * self().offset() + 1, CreatePixbuf(state().icon), -1);
|
||||
gtk_list_store_set(grandparent->gtkListStore, &parent->gtkIter, 3 * self().offset() + 2, state().text.data(), -1);
|
||||
grandparent->unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -5,12 +5,17 @@ namespace hiro {
|
||||
struct pListViewCell : pObject {
|
||||
Declare(ListViewCell, Object)
|
||||
|
||||
auto setAlignment(Alignment alignment) -> void;
|
||||
auto setBackgroundColor(Color color) -> void;
|
||||
auto setCheckable(bool checkable) -> void;
|
||||
auto setChecked(bool checked) -> void;
|
||||
auto setForegroundColor(Color color) -> void;
|
||||
auto setIcon(const image& icon) -> void;
|
||||
auto setText(const string& text) -> void;
|
||||
|
||||
auto _parent() -> pListViewItem*;
|
||||
auto _grandparent() -> maybe<pListView&>;
|
||||
auto _parent() -> maybe<pListViewItem&>;
|
||||
auto _setState() -> void;
|
||||
};
|
||||
|
||||
}
|
||||
|
@@ -3,91 +3,82 @@
|
||||
namespace hiro {
|
||||
|
||||
auto pListViewColumn::construct() -> void {
|
||||
unsigned offset = self().offset();
|
||||
if(auto grandparent = _grandparent()) {
|
||||
auto handle = grandparent.data();
|
||||
unsigned offset = self().offset();
|
||||
|
||||
gtkHeader = gtk_hbox_new(false, 0);
|
||||
gtkHeader = gtk_hbox_new(false, 0);
|
||||
|
||||
gtkHeaderIcon = gtk_image_new();
|
||||
gtk_box_pack_start(GTK_BOX(gtkHeader), gtkHeaderIcon, false, false, 0);
|
||||
gtkHeaderIcon = gtk_image_new();
|
||||
gtk_box_pack_start(GTK_BOX(gtkHeader), gtkHeaderIcon, false, false, 0);
|
||||
|
||||
gtkHeaderText = gtk_label_new(state().text);
|
||||
gtk_box_pack_start(GTK_BOX(gtkHeader), gtkHeaderText, true, false, 2);
|
||||
gtkHeaderText = gtk_label_new(state().text);
|
||||
gtk_box_pack_start(GTK_BOX(gtkHeader), gtkHeaderText, true, false, 2);
|
||||
|
||||
gtkColumn = gtk_tree_view_column_new();
|
||||
gtk_tree_view_column_set_sizing(gtkColumn, GTK_TREE_VIEW_COLUMN_FIXED);
|
||||
gtk_tree_view_column_set_title(gtkColumn, "");
|
||||
gtk_tree_view_column_set_widget(gtkColumn, gtkHeader);
|
||||
gtkColumn = gtk_tree_view_column_new();
|
||||
gtk_tree_view_column_set_sizing(gtkColumn, GTK_TREE_VIEW_COLUMN_FIXED);
|
||||
gtk_tree_view_column_set_title(gtkColumn, "");
|
||||
gtk_tree_view_column_set_widget(gtkColumn, gtkHeader);
|
||||
|
||||
if(offset == 0) {
|
||||
gtkCellToggle = gtk_cell_renderer_toggle_new();
|
||||
gtk_tree_view_column_pack_start(gtkColumn, gtkCellToggle, false);
|
||||
gtk_tree_view_column_set_attributes(gtkColumn, gtkCellToggle, "active", 0, nullptr);
|
||||
gtk_tree_view_column_set_cell_data_func(gtkColumn, GTK_CELL_RENDERER(gtkCellToggle), (GtkTreeCellDataFunc)ListView_cellRendererToggleDataFunc, (gpointer)_parent(), nullptr);
|
||||
gtk_tree_view_column_set_attributes(gtkColumn, gtkCellToggle, "active", 3 * offset + 0, nullptr);
|
||||
gtk_tree_view_column_set_cell_data_func(gtkColumn, GTK_CELL_RENDERER(gtkCellToggle), (GtkTreeCellDataFunc)ListView_dataFunc, (gpointer)handle, nullptr);
|
||||
|
||||
gtkCellIcon = gtk_cell_renderer_pixbuf_new();
|
||||
gtk_tree_view_column_pack_start(gtkColumn, gtkCellIcon, false);
|
||||
gtk_tree_view_column_set_attributes(gtkColumn, gtkCellIcon, "pixbuf", 3 * offset + 1, nullptr);
|
||||
gtk_tree_view_column_set_cell_data_func(gtkColumn, GTK_CELL_RENDERER(gtkCellIcon), (GtkTreeCellDataFunc)ListView_dataFunc, (gpointer)handle, nullptr);
|
||||
|
||||
gtkCellText = gtk_cell_renderer_text_new();
|
||||
gtk_tree_view_column_pack_start(gtkColumn, gtkCellText, true); //text must expand to cell width for horizontal alignment to work
|
||||
gtk_tree_view_column_set_attributes(gtkColumn, gtkCellText, "text", 3 * offset + 2, nullptr);
|
||||
gtk_tree_view_column_set_cell_data_func(gtkColumn, GTK_CELL_RENDERER(gtkCellText), (GtkTreeCellDataFunc)ListView_dataFunc, (gpointer)handle, nullptr);
|
||||
|
||||
g_signal_connect(G_OBJECT(gtkColumn), "clicked", G_CALLBACK(ListView_headerActivate), (gpointer)handle);
|
||||
g_signal_connect(G_OBJECT(gtkCellText), "edited", G_CALLBACK(ListView_edit), (gpointer)handle);
|
||||
g_signal_connect(G_OBJECT(gtkCellToggle), "toggled", G_CALLBACK(ListView_toggle), (gpointer)handle);
|
||||
|
||||
gtk_tree_view_append_column(grandparent->gtkTreeView, gtkColumn);
|
||||
gtk_widget_show_all(gtkHeader);
|
||||
grandparent->_createModel();
|
||||
|
||||
_setState();
|
||||
}
|
||||
|
||||
gtkCellIcon = gtk_cell_renderer_pixbuf_new();
|
||||
gtk_tree_view_column_pack_start(gtkColumn, gtkCellIcon, false);
|
||||
gtk_tree_view_column_set_attributes(gtkColumn, gtkCellIcon, "pixbuf", 1 + offset * 2 + 0, nullptr);
|
||||
|
||||
gtkCellText = gtk_cell_renderer_text_new();
|
||||
gtk_tree_view_column_pack_start(gtkColumn, gtkCellText, false);
|
||||
gtk_tree_view_column_set_attributes(gtkColumn, gtkCellText, "text", 1 + offset * 2 + 1, nullptr);
|
||||
|
||||
g_signal_connect(G_OBJECT(gtkColumn), "clicked", G_CALLBACK(ListView_headerActivate), (gpointer)_parent());
|
||||
g_signal_connect(G_OBJECT(gtkCellText), "edited", G_CALLBACK(ListView_edit), (gpointer)_parent());
|
||||
if(gtkCellToggle) g_signal_connect(G_OBJECT(gtkCellToggle), "toggled", G_CALLBACK(ListView_toggle), (gpointer)_parent());
|
||||
}
|
||||
|
||||
auto pListViewColumn::destruct() -> void {
|
||||
if(auto grandparent = _grandparent()) {
|
||||
gtk_tree_view_remove_column(grandparent->gtkTreeView, gtkColumn);
|
||||
gtkColumn = nullptr;
|
||||
grandparent->_createModel();
|
||||
}
|
||||
}
|
||||
|
||||
auto pListViewColumn::setActive() -> void {
|
||||
if(auto parent = _parent()) {
|
||||
gtk_tree_view_set_search_column(parent->gtkTreeView, 1 + self().offset() * 2 + 1);
|
||||
}
|
||||
_setState();
|
||||
}
|
||||
|
||||
auto pListViewColumn::setAlignment(Alignment alignment) -> void {
|
||||
}
|
||||
|
||||
auto pListViewColumn::setBackgroundColor(Color color) -> void {
|
||||
if(color) {
|
||||
GdkColor gdkColor = CreateColor(color);
|
||||
if(gtkCellToggle) g_object_set(G_OBJECT(gtkCellToggle), "cell-background-gdk", &gdkColor, nullptr);
|
||||
g_object_set(G_OBJECT(gtkCellIcon), "cell-background-gdk", &gdkColor, nullptr);
|
||||
g_object_set(G_OBJECT(gtkCellText), "cell-background-gdk", &gdkColor, nullptr);
|
||||
} else {
|
||||
if(gtkCellToggle) g_object_set(G_OBJECT(gtkCellToggle), "cell-background-set", FALSE, nullptr);
|
||||
g_object_set(G_OBJECT(gtkCellIcon), "cell-background-set", FALSE, nullptr);
|
||||
g_object_set(G_OBJECT(gtkCellText), "cell-background-set", FALSE, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
auto pListViewColumn::setEditable(bool editable) -> void {
|
||||
g_object_set(G_OBJECT(gtkCellText), "editable", editable ? TRUE : FALSE, nullptr);
|
||||
g_object_set(G_OBJECT(gtkCellText), "editable", editable ? true : false, nullptr);
|
||||
}
|
||||
|
||||
auto pListViewColumn::setExpandable(bool expandable) -> void {
|
||||
if(auto parent = _parent()) {
|
||||
parent->resizeColumns();
|
||||
if(auto grandparent = _grandparent()) {
|
||||
grandparent->resizeColumns();
|
||||
}
|
||||
}
|
||||
|
||||
auto pListViewColumn::setFont(const string& font) -> void {
|
||||
pFont::setFont(gtkHeaderText, font);
|
||||
auto fontDescription = pFont::create(font);
|
||||
g_object_set(G_OBJECT(gtkCellText), "font-desc", fontDescription, nullptr);
|
||||
pango_font_description_free(fontDescription);
|
||||
}
|
||||
|
||||
auto pListViewColumn::setForegroundColor(Color color) -> void {
|
||||
if(color) {
|
||||
GdkColor gdkColor = CreateColor(color);
|
||||
g_object_set(G_OBJECT(gtkCellText), "foreground-gdk", &gdkColor, nullptr);
|
||||
} else {
|
||||
g_object_set(G_OBJECT(gtkCellText), "foreground-set", FALSE, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
auto pListViewColumn::setHorizontalAlignment(double alignment) -> void {
|
||||
_setAlignment();
|
||||
}
|
||||
|
||||
auto pListViewColumn::setIcon(const image& icon) -> void {
|
||||
@@ -99,40 +90,47 @@ auto pListViewColumn::setIcon(const image& icon) -> void {
|
||||
}
|
||||
|
||||
auto pListViewColumn::setResizable(bool resizable) -> void {
|
||||
gtk_tree_view_column_set_resizable(gtkColumn, resizable);
|
||||
_setState();
|
||||
}
|
||||
|
||||
auto pListViewColumn::setSortable(bool sortable) -> void {
|
||||
_setState();
|
||||
}
|
||||
|
||||
auto pListViewColumn::setText(const string& text) -> void {
|
||||
gtk_label_set_text(GTK_LABEL(gtkHeaderText), text);
|
||||
}
|
||||
|
||||
auto pListViewColumn::setVerticalAlignment(double alignment) -> void {
|
||||
_setAlignment();
|
||||
_setState();
|
||||
}
|
||||
|
||||
auto pListViewColumn::setVisible(bool visible) -> void {
|
||||
gtk_tree_view_column_set_visible(gtkColumn, visible);
|
||||
_setState();
|
||||
}
|
||||
|
||||
auto pListViewColumn::setWidth(signed width) -> void {
|
||||
if(auto parent = _parent()) {
|
||||
parent->resizeColumns();
|
||||
if(auto grandparent = _grandparent()) {
|
||||
grandparent->resizeColumns();
|
||||
}
|
||||
}
|
||||
|
||||
auto pListViewColumn::_parent() -> pListView* {
|
||||
if(auto parent = self().parentListView()) return parent->self();
|
||||
return nullptr;
|
||||
auto pListViewColumn::_grandparent() -> maybe<pListView&> {
|
||||
if(auto parent = _parent()) return parent->_parent();
|
||||
return nothing;
|
||||
}
|
||||
|
||||
auto pListViewColumn::_setAlignment() -> void {
|
||||
gtk_tree_view_column_set_alignment(gtkColumn, state().horizontalAlignment);
|
||||
gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(gtkCellText), state().horizontalAlignment, state().verticalAlignment);
|
||||
//set multi-line text alignment
|
||||
auto pangoAlignment = PANGO_ALIGN_CENTER;
|
||||
if(state().horizontalAlignment < 0.333) pangoAlignment = PANGO_ALIGN_LEFT;
|
||||
if(state().horizontalAlignment > 0.666) pangoAlignment = PANGO_ALIGN_RIGHT;
|
||||
g_object_set(G_OBJECT(gtkCellText), "alignment", pangoAlignment, nullptr);
|
||||
auto pListViewColumn::_parent() -> maybe<pListViewHeader&> {
|
||||
if(auto parent = self().parentListViewHeader()) {
|
||||
if(auto self = parent->self()) return *self;
|
||||
}
|
||||
return nothing;
|
||||
}
|
||||
|
||||
auto pListViewColumn::_setState() -> void {
|
||||
if(auto grandparent = _grandparent()) {
|
||||
gtk_tree_view_set_search_column(grandparent->gtkTreeView, 3 * self().offset() + 2);
|
||||
gtk_tree_view_column_set_resizable(gtkColumn, state().resizable);
|
||||
gtk_tree_view_column_set_clickable(gtkColumn, state().sortable);
|
||||
gtk_label_set_text(GTK_LABEL(gtkHeaderText), state().text);
|
||||
gtk_tree_view_column_set_visible(gtkColumn, self().visible());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -6,21 +6,24 @@ struct pListViewColumn : pObject {
|
||||
Declare(ListViewColumn, Object)
|
||||
|
||||
auto setActive() -> void;
|
||||
auto setAlignment(Alignment alignment) -> 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 setHorizontalAlignment(double) -> 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 setVerticalAlignment(double) -> void {}
|
||||
auto setVisible(bool visible) -> void override;
|
||||
auto setWidth(signed width) -> void;
|
||||
|
||||
auto _parent() -> pListView*;
|
||||
auto _setAlignment() -> void;
|
||||
auto _grandparent() -> maybe<pListView&>;
|
||||
auto _parent() -> maybe<pListViewHeader&>;
|
||||
auto _setState() -> void;
|
||||
|
||||
GtkTreeViewColumn* gtkColumn = nullptr;
|
||||
GtkWidget* gtkHeader = nullptr;
|
||||
|
41
hiro/gtk/widget/list-view-header.cpp
Normal file
41
hiro/gtk/widget/list-view-header.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
#if defined(Hiro_ListView)
|
||||
|
||||
namespace hiro {
|
||||
|
||||
auto pListViewHeader::construct() -> void {
|
||||
_setState();
|
||||
}
|
||||
|
||||
auto pListViewHeader::destruct() -> void {
|
||||
}
|
||||
|
||||
auto pListViewHeader::append(sListViewColumn column) -> void {
|
||||
_setState();
|
||||
}
|
||||
|
||||
auto pListViewHeader::remove(sListViewColumn column) -> void {
|
||||
}
|
||||
|
||||
auto pListViewHeader::setVisible(bool visible) -> void {
|
||||
_setState();
|
||||
}
|
||||
|
||||
auto pListViewHeader::_parent() -> maybe<pListView&> {
|
||||
if(auto parent = self().parentListView()) {
|
||||
if(auto self = parent->self()) return *self;
|
||||
}
|
||||
return nothing;
|
||||
}
|
||||
|
||||
auto pListViewHeader::_setState() -> void {
|
||||
if(auto parent = _parent()) {
|
||||
gtk_tree_view_set_headers_visible(parent->gtkTreeView, self().visible());
|
||||
for(auto& column : state().columns) {
|
||||
if(auto self = column->self()) self->_setState();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
18
hiro/gtk/widget/list-view-header.hpp
Normal file
18
hiro/gtk/widget/list-view-header.hpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#if defined(Hiro_ListView)
|
||||
|
||||
namespace hiro {
|
||||
|
||||
struct pListViewHeader : pObject {
|
||||
Declare(ListViewHeader, Object)
|
||||
|
||||
auto append(sListViewColumn column) -> void;
|
||||
auto remove(sListViewColumn column) -> void;
|
||||
auto setVisible(bool visible) -> void override;
|
||||
|
||||
auto _parent() -> maybe<pListView&>;
|
||||
auto _setState() -> void;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@@ -3,9 +3,21 @@
|
||||
namespace hiro {
|
||||
|
||||
auto pListViewItem::construct() -> void {
|
||||
if(auto parent = _parent()) {
|
||||
parent->lock();
|
||||
gtk_list_store_append(parent->gtkListStore, >kIter);
|
||||
_setState();
|
||||
parent->unlock();
|
||||
}
|
||||
}
|
||||
|
||||
auto pListViewItem::destruct() -> void {
|
||||
if(auto parent = _parent()) {
|
||||
parent->lock();
|
||||
gtk_list_store_remove(parent->gtkListStore, >kIter);
|
||||
parent->_updateSelected();
|
||||
parent->unlock();
|
||||
}
|
||||
}
|
||||
|
||||
auto pListViewItem::append(sListViewCell cell) -> void {
|
||||
@@ -14,18 +26,12 @@ auto pListViewItem::append(sListViewCell cell) -> void {
|
||||
auto pListViewItem::remove(sListViewCell cell) -> void {
|
||||
}
|
||||
|
||||
auto pListViewItem::setAlignment(Alignment alignment) -> void {
|
||||
}
|
||||
|
||||
auto pListViewItem::setBackgroundColor(Color color) -> void {
|
||||
}
|
||||
|
||||
auto pListViewItem::setCheckable(bool checkable) -> void {
|
||||
}
|
||||
|
||||
auto pListViewItem::setChecked(bool checked) -> void {
|
||||
if(auto parent = _parent()) {
|
||||
gtk_list_store_set(parent->gtkListStore, >kIter, 0, checked, -1);
|
||||
}
|
||||
}
|
||||
|
||||
auto pListViewItem::setFocused() -> void {
|
||||
if(auto parent = _parent()) {
|
||||
GtkTreePath* path = gtk_tree_path_new_from_string(string{self().offset()});
|
||||
@@ -39,23 +45,32 @@ auto pListViewItem::setForegroundColor(Color color) -> void {
|
||||
}
|
||||
|
||||
auto pListViewItem::setSelected(bool selected) -> void {
|
||||
_setState();
|
||||
}
|
||||
|
||||
auto pListViewItem::_parent() -> maybe<pListView&> {
|
||||
if(auto parent = self().parentListView()) {
|
||||
if(auto self = parent->self()) return *self;
|
||||
}
|
||||
return nothing;
|
||||
}
|
||||
|
||||
auto pListViewItem::_setState() -> void {
|
||||
if(auto parent = _parent()) {
|
||||
parent->lock();
|
||||
if(selected) {
|
||||
if(state().selected) {
|
||||
gtk_tree_selection_select_iter(parent->gtkTreeSelection, >kIter);
|
||||
} else {
|
||||
gtk_tree_selection_unselect_iter(parent->gtkTreeSelection, >kIter);
|
||||
}
|
||||
parent->_updateSelected();
|
||||
for(auto& cell : state().cells) {
|
||||
if(auto self = cell->self()) self->_setState();
|
||||
}
|
||||
parent->unlock();
|
||||
}
|
||||
}
|
||||
|
||||
auto pListViewItem::_parent() -> pListView* {
|
||||
if(auto parent = self().parentListView()) return parent->self();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@@ -7,14 +7,14 @@ struct pListViewItem : pObject {
|
||||
|
||||
auto append(sListViewCell cell) -> void;
|
||||
auto remove(sListViewCell cell) -> void;
|
||||
auto setAlignment(Alignment alignment) -> void;
|
||||
auto setBackgroundColor(Color color) -> void;
|
||||
auto setCheckable(bool checkable) -> void;
|
||||
auto setChecked(bool checked) -> void;
|
||||
auto setFocused() -> void;
|
||||
auto setForegroundColor(Color color) -> void;
|
||||
auto setSelected(bool selected) -> void;
|
||||
|
||||
auto _parent() -> pListView*;
|
||||
auto _parent() -> maybe<pListView&>;
|
||||
auto _setState() -> void;
|
||||
|
||||
GtkTreeIter gtkIter;
|
||||
};
|
||||
|
@@ -10,8 +10,10 @@ static auto ListView_headerActivate(GtkTreeViewColumn* column, pListView* p) ->
|
||||
static auto ListView_mouseMoveEvent(GtkWidget*, GdkEvent*, pListView* p) -> signed { return p->_doMouseMove(); }
|
||||
static auto ListView_popup(GtkTreeView*, pListView* p) -> void { return p->_doContext(); }
|
||||
|
||||
static auto ListView_cellRendererToggleDataFunc(GtkTreeViewColumn* column, GtkCellRenderer* renderer, GtkTreeModel* model, GtkTreeIter* iter, pListView* p) -> void { return p->_doCellRendererToggleDataFunc(renderer, iter); }
|
||||
static auto ListView_toggle(GtkCellRendererToggle*, const char* path, pListView* p) -> void { return p->_doToggle(path); }
|
||||
static auto ListView_dataFunc(GtkTreeViewColumn* column, GtkCellRenderer* renderer, GtkTreeModel* model, GtkTreeIter* iter, pListView* p) -> void { return p->_doDataFunc(column, renderer, iter); }
|
||||
static auto ListView_toggle(GtkCellRendererToggle* toggle, const char* path, pListView* p) -> void { return p->_doToggle(toggle, path); }
|
||||
|
||||
//gtk_tree_view_set_rules_hint(gtkTreeView, true);
|
||||
|
||||
auto pListView::construct() -> void {
|
||||
gtkWidget = gtk_scrolled_window_new(0, 0);
|
||||
@@ -29,12 +31,9 @@ auto pListView::construct() -> void {
|
||||
|
||||
setBackgroundColor(state().backgroundColor);
|
||||
setBatchable(state().batchable);
|
||||
setCheckable(state().checkable);
|
||||
setBordered(state().bordered);
|
||||
setFont(self().font(true));
|
||||
setForegroundColor(state().foregroundColor);
|
||||
setGridVisible(state().gridVisible);
|
||||
setHeaderVisible(state().headerVisible);
|
||||
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);
|
||||
@@ -51,68 +50,25 @@ auto pListView::destruct() -> void {
|
||||
gtk_widget_destroy(gtkWidget);
|
||||
}
|
||||
|
||||
auto pListView::append(sListViewColumn column) -> void {
|
||||
gtk_tree_view_append_column(gtkTreeView, column->self()->gtkColumn);
|
||||
gtk_widget_show_all(column->self()->gtkHeader);
|
||||
column->setBackgroundColor(column->backgroundColor());
|
||||
column->setEditable(column->editable());
|
||||
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
|
||||
auto pListView::append(sListViewHeader header) -> void {
|
||||
}
|
||||
|
||||
auto pListView::append(sListViewItem item) -> void {
|
||||
gtk_list_store_append(gtkListStore, &item->self()->gtkIter);
|
||||
|
||||
item->setChecked(item->checked());
|
||||
item->setSelected(item->selected());
|
||||
for(auto column : range(self().columns())) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
auto pListView::focused() -> bool {
|
||||
auto pListView::focused() const -> bool {
|
||||
return GTK_WIDGET_HAS_FOCUS(gtkTreeView);
|
||||
}
|
||||
|
||||
auto pListView::remove(sListViewColumn column) -> void {
|
||||
if(auto delegate = column->self()) {
|
||||
gtk_tree_view_remove_column(gtkTreeView, delegate->gtkColumn);
|
||||
delegate->gtkColumn = nullptr;
|
||||
}
|
||||
_createModel();
|
||||
gtk_tree_view_set_rules_hint(gtkTreeView, self().columns() >= 2); //two or more columns + checkbutton column
|
||||
auto pListView::remove(sListViewHeader header) -> void {
|
||||
}
|
||||
|
||||
auto pListView::remove(sListViewItem item) -> void {
|
||||
lock();
|
||||
if(auto delegate = item->self()) {
|
||||
gtk_list_store_remove(gtkListStore, &delegate->gtkIter);
|
||||
_updateSelected();
|
||||
}
|
||||
unlock();
|
||||
}
|
||||
|
||||
auto pListView::reset() -> void {
|
||||
GList* list = gtk_tree_view_get_columns(gtkTreeView), *p = list;
|
||||
GList* list = gtk_tree_view_get_columns(gtkTreeView);
|
||||
GList* p = list;
|
||||
while(p && p->data) {
|
||||
gtk_tree_view_remove_column(gtkTreeView, (GtkTreeViewColumn*)p->data);
|
||||
p = p->next;
|
||||
@@ -125,41 +81,40 @@ auto pListView::reset() -> void {
|
||||
auto pListView::resizeColumns() -> void {
|
||||
lock();
|
||||
|
||||
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++;
|
||||
}
|
||||
if(auto& header = state().header) {
|
||||
vector<signed> widths;
|
||||
signed minimumWidth = 0;
|
||||
signed expandable = 0;
|
||||
for(auto column : range(header->columnCount())) {
|
||||
signed width = _width(column);
|
||||
widths.append(width);
|
||||
minimumWidth += width;
|
||||
if(header->column(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 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;
|
||||
}
|
||||
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 column : range(header->columnCount())) {
|
||||
if(auto self = header->state.columns[column]->self()) {
|
||||
signed width = widths[column];
|
||||
if(self->state().expandable) width += expandWidth;
|
||||
gtk_tree_view_column_set_fixed_width(self->gtkColumn, width);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unlock();
|
||||
}
|
||||
|
||||
auto pListView::selectAll() -> void {
|
||||
for(auto& item : state().items) {
|
||||
if(auto delegate = item->self()) delegate->setSelected(true);
|
||||
}
|
||||
auto pListView::setAlignment(Alignment alignment) -> void {
|
||||
}
|
||||
|
||||
auto pListView::setBackgroundColor(Color color) -> void {
|
||||
@@ -171,10 +126,8 @@ 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::setBordered(bool bordered) -> void {
|
||||
gtk_tree_view_set_grid_lines(gtkTreeView, bordered ? GTK_TREE_VIEW_GRID_LINES_BOTH : GTK_TREE_VIEW_GRID_LINES_NONE);
|
||||
}
|
||||
|
||||
auto pListView::setFocused() -> void {
|
||||
@@ -182,8 +135,8 @@ auto pListView::setFocused() -> void {
|
||||
}
|
||||
|
||||
auto pListView::setFont(const string& font) -> void {
|
||||
for(auto& column : state().columns) {
|
||||
if(auto delegate = column->self()) delegate->setFont(column->font(true));
|
||||
if(auto& header = state().header) {
|
||||
if(auto self = header->self()) self->_setState();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,39 +145,22 @@ auto pListView::setForegroundColor(Color color) -> void {
|
||||
gtk_widget_modify_text(gtkWidgetChild, GTK_STATE_NORMAL, color ? &gdkColor : nullptr);
|
||||
}
|
||||
|
||||
auto pListView::setGridVisible(bool visible) -> void {
|
||||
gtk_tree_view_set_grid_lines(gtkTreeView, visible ? GTK_TREE_VIEW_GRID_LINES_BOTH : GTK_TREE_VIEW_GRID_LINES_NONE);
|
||||
}
|
||||
|
||||
auto pListView::setHeaderVisible(bool visible) -> void {
|
||||
gtk_tree_view_set_headers_visible(gtkTreeView, visible);
|
||||
}
|
||||
|
||||
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::setGeometry(Geometry geometry) -> void {
|
||||
pWidget::setGeometry(geometry);
|
||||
if(auto& header = state().header) {
|
||||
for(auto& column : header->state.columns) {
|
||||
if(column->state.expandable) return resizeColumns();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto pListView::uncheckAll() -> void {
|
||||
for(auto& item : state().items) {
|
||||
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
|
||||
unsigned width = 8;
|
||||
if(auto item = self().item(_row)) {
|
||||
if(auto cell = item->cell(_column)) {
|
||||
if(cell->state.checkable) {
|
||||
width += 32;
|
||||
}
|
||||
if(auto& icon = cell->state.icon) {
|
||||
width += icon.width() + 2;
|
||||
}
|
||||
@@ -236,19 +172,16 @@ auto pListView::_cellWidth(unsigned _row, unsigned _column) -> unsigned {
|
||||
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();
|
||||
unsigned width = 8;
|
||||
if(auto& header = state().header) {
|
||||
if(auto column = header->column(_column)) {
|
||||
if(auto& icon = column->state.icon) {
|
||||
width += icon.width() + 2;
|
||||
}
|
||||
if(auto& text = column->state.text) {
|
||||
width += Font::size(column->font(true), text).width();
|
||||
}
|
||||
}
|
||||
}
|
||||
return width;
|
||||
@@ -260,12 +193,15 @@ auto pListView::_createModel() -> void {
|
||||
gtkTreeModel = nullptr;
|
||||
|
||||
vector<GType> types;
|
||||
unsigned position = 0;
|
||||
for(auto column : state().columns) {
|
||||
if(!column->self()->gtkColumn) continue; //column is being removed
|
||||
if(position++ == 0) types.append(G_TYPE_BOOLEAN);
|
||||
types.append(GDK_TYPE_PIXBUF);
|
||||
types.append(G_TYPE_STRING);
|
||||
if(auto& header = state().header) {
|
||||
for(auto column : header->state.columns) {
|
||||
if(auto self = column->self()) {
|
||||
if(!self->gtkColumn) continue; //may not have been created yet; or recently destroyed
|
||||
types.append(G_TYPE_BOOLEAN);
|
||||
types.append(GDK_TYPE_PIXBUF);
|
||||
types.append(G_TYPE_STRING);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!types) return; //no columns available
|
||||
|
||||
@@ -278,15 +214,6 @@ auto pListView::_doActivate() -> void {
|
||||
if(!locked()) self().doActivate();
|
||||
}
|
||||
|
||||
auto pListView::_doCellRendererToggleDataFunc(GtkCellRenderer* renderer, GtkTreeIter* iter) -> void {
|
||||
auto path = gtk_tree_model_get_string_from_iter(gtkTreeModel, iter);
|
||||
auto row = decimal(path);
|
||||
if(auto item = self().item(row)) {
|
||||
gtk_cell_renderer_set_visible(renderer, state().checkable && item->state.checkable);
|
||||
}
|
||||
g_free(path);
|
||||
}
|
||||
|
||||
auto pListView::_doChange() -> void {
|
||||
if(!locked()) _updateSelected();
|
||||
}
|
||||
@@ -295,18 +222,70 @@ auto pListView::_doContext() -> void {
|
||||
if(!locked()) self().doContext();
|
||||
}
|
||||
|
||||
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)) {
|
||||
auto row = decimal(path);
|
||||
auto pListView::_doDataFunc(GtkTreeViewColumn* gtkColumn, GtkCellRenderer* renderer, GtkTreeIter* iter) -> void {
|
||||
auto path = gtk_tree_model_get_string_from_iter(gtkTreeModel, iter);
|
||||
auto row = decimal(path);
|
||||
g_free(path);
|
||||
|
||||
if(auto& header = state().header) {
|
||||
for(auto& column : header->state.columns) {
|
||||
if(auto p = column->self()) {
|
||||
if(renderer != GTK_CELL_RENDERER(p->gtkCellToggle)
|
||||
&& renderer != GTK_CELL_RENDERER(p->gtkCellIcon)
|
||||
&& renderer != GTK_CELL_RENDERER(p->gtkCellText)
|
||||
) continue;
|
||||
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);
|
||||
if(renderer == GTK_CELL_RENDERER(p->gtkCellToggle)) {
|
||||
gtk_cell_renderer_set_visible(renderer, cell->state.checkable);
|
||||
} else if(renderer == GTK_CELL_RENDERER(p->gtkCellText)) {
|
||||
auto alignment = cell->alignment(true);
|
||||
if(!alignment) alignment = {0.0, 0.5};
|
||||
//note: below line will center column header text; but causes strange glitches
|
||||
//(specifically, windows fail to respond to the close button ... some kind of heap corruption inside GTK+)
|
||||
//gtk_tree_view_column_set_alignment(gtkColumn, alignment.horizontal());
|
||||
gtk_cell_renderer_set_alignment(renderer, alignment.horizontal(), alignment.vertical());
|
||||
auto pangoAlignment = PANGO_ALIGN_CENTER;
|
||||
if(alignment.horizontal() < 0.333) pangoAlignment = PANGO_ALIGN_LEFT;
|
||||
if(alignment.horizontal() > 0.666) pangoAlignment = PANGO_ALIGN_RIGHT;
|
||||
g_object_set(G_OBJECT(renderer), "alignment", pangoAlignment, nullptr);
|
||||
auto font = pFont::create(cell->font(true));
|
||||
g_object_set(G_OBJECT(renderer), "font-desc", font, nullptr);
|
||||
pango_font_description_free(font);
|
||||
if(auto color = cell->foregroundColor(true)) {
|
||||
auto gdkColor = CreateColor(color);
|
||||
g_object_set(G_OBJECT(renderer), "foreground-gdk", &gdkColor, nullptr);
|
||||
} else {
|
||||
g_object_set(G_OBJECT(renderer), "foreground-set", false, nullptr);
|
||||
}
|
||||
}
|
||||
if(auto color = cell->backgroundColor(true)) {
|
||||
auto gdkColor = CreateColor(color);
|
||||
g_object_set(G_OBJECT(renderer), "cell-background-gdk", &gdkColor, nullptr);
|
||||
} else {
|
||||
g_object_set(G_OBJECT(renderer), "cell-background-set", false, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto pListView::_doEdit(GtkCellRendererText* gtkCellRendererText, const char* path, const char* text) -> void {
|
||||
if(auto& header = state().header) {
|
||||
for(auto& column : header->state.columns) {
|
||||
if(auto delegate = column->self()) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -322,7 +301,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().unselectAll();
|
||||
for(auto& item : state().items) item->setSelected(false);
|
||||
self().doChange();
|
||||
return true;
|
||||
}
|
||||
@@ -344,11 +323,13 @@ auto pListView::_doEvent(GdkEventButton* event) -> signed {
|
||||
}
|
||||
|
||||
auto pListView::_doHeaderActivate(GtkTreeViewColumn* gtkTreeViewColumn) -> void {
|
||||
for(auto& column : state().columns) {
|
||||
if(auto delegate = column->self()) {
|
||||
if(gtkTreeViewColumn == delegate->gtkColumn) {
|
||||
if(!locked()) self().doSort(column);
|
||||
return;
|
||||
if(auto& header = state().header) {
|
||||
for(auto& column : header->state.columns) {
|
||||
if(auto delegate = column->self()) {
|
||||
if(gtkTreeViewColumn == delegate->gtkColumn) {
|
||||
if(!locked()) self().doSort(column);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -363,12 +344,21 @@ auto pListView::_doMouseMove() -> signed {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto pListView::_doToggle(const char* path) -> void {
|
||||
if(auto item = self().item(decimal(path))) {
|
||||
if(auto delegate = item->self()) {
|
||||
item->state.checked = !item->state.checked;
|
||||
delegate->setChecked(item->state.checked);
|
||||
if(!locked()) self().doToggle(item);
|
||||
auto pListView::_doToggle(GtkCellRendererToggle* gtkCellRendererToggle, const char* path) -> void {
|
||||
if(auto& header = state().header) {
|
||||
for(auto& column : header->state.columns) {
|
||||
if(auto delegate = column->self()) {
|
||||
if(gtkCellRendererToggle == GTK_CELL_RENDERER_TOGGLE(delegate->gtkCellToggle)) {
|
||||
auto row = decimal(path);
|
||||
if(auto item = self().item(row)) {
|
||||
if(auto cell = item->cell(column->offset())) {
|
||||
cell->setChecked(!cell->checked());
|
||||
if(!locked()) self().doToggle(cell);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -411,7 +401,7 @@ auto pListView::_updateSelected() -> void {
|
||||
currentSelection = selected;
|
||||
for(auto& item : state().items) item->state.selected = false;
|
||||
for(auto& position : currentSelection) {
|
||||
if(position >= self().items()) continue;
|
||||
if(position >= self().itemCount()) continue;
|
||||
self().item(position)->state.selected = true;
|
||||
}
|
||||
|
||||
@@ -419,16 +409,17 @@ auto pListView::_updateSelected() -> void {
|
||||
}
|
||||
|
||||
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));
|
||||
if(auto& header = state().header) {
|
||||
if(auto width = header->column(column).width()) return width;
|
||||
unsigned width = 1;
|
||||
if(!header->column(column).visible()) return width;
|
||||
if(header->visible()) width = max(width, _columnWidth(column));
|
||||
for(auto row : range(state().items)) {
|
||||
width = max(width, _cellWidth(row, column));
|
||||
}
|
||||
return width;
|
||||
}
|
||||
for(auto row : range(state().items)) {
|
||||
width = max(width, _cellWidth(row, column));
|
||||
}
|
||||
return width;
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -5,40 +5,34 @@ namespace hiro {
|
||||
struct pListView : pWidget {
|
||||
Declare(ListView, Widget)
|
||||
|
||||
auto append(sListViewColumn column) -> void;
|
||||
auto append(sListViewHeader column) -> void;
|
||||
auto append(sListViewItem item) -> void;
|
||||
auto checkAll() -> void;
|
||||
auto focused() -> bool;
|
||||
auto remove(sListViewColumn column) -> void;
|
||||
auto focused() const -> bool override;
|
||||
auto remove(sListViewHeader column) -> void;
|
||||
auto remove(sListViewItem item) -> void;
|
||||
auto reset() -> void;
|
||||
auto resizeColumns() -> void;
|
||||
auto selectAll() -> void;
|
||||
auto setAlignment(Alignment alignment) -> void;
|
||||
auto setBackgroundColor(Color color) -> void;
|
||||
auto setBatchable(bool batchable) -> void;
|
||||
auto setCheckable(bool checkable) -> void;
|
||||
auto setBordered(bool bordered) -> 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 setSortable(bool sortable) -> void;
|
||||
auto uncheckAll() -> void;
|
||||
auto unselectAll() -> void;
|
||||
auto setGeometry(Geometry geometry) -> void override;
|
||||
|
||||
auto _cellWidth(unsigned row, unsigned column) -> unsigned;
|
||||
auto _column(unsigned column) -> pListViewColumn*;
|
||||
auto _columnWidth(unsigned column) -> unsigned;
|
||||
auto _createModel() -> void;
|
||||
auto _doActivate() -> void;
|
||||
auto _doCellRendererToggleDataFunc(GtkCellRenderer* renderer, GtkTreeIter* iter) -> void;
|
||||
auto _doChange() -> void;
|
||||
auto _doContext() -> void;
|
||||
auto _doEdit(GtkCellRendererText* renderer, const char* path, const char* text) -> void;
|
||||
auto _doDataFunc(GtkTreeViewColumn* column, GtkCellRenderer* renderer, GtkTreeIter* iter) -> void;
|
||||
auto _doEdit(GtkCellRendererText* gtkCellRendererText, const char* path, const char* text) -> void;
|
||||
auto _doEvent(GdkEventButton* event) -> signed;
|
||||
auto _doHeaderActivate(GtkTreeViewColumn* column) -> void;
|
||||
auto _doMouseMove() -> signed;
|
||||
auto _doToggle(const char* path) -> void;
|
||||
auto _doToggle(GtkCellRendererToggle* gtkCellRendererToggle, const char* path) -> void;
|
||||
auto _updateSelected() -> void;
|
||||
auto _width(unsigned column) -> unsigned;
|
||||
|
||||
|
@@ -125,9 +125,9 @@ auto pTabFrame::remove(sTabFrameItem item) -> void {
|
||||
//the new tab will be the one after this one
|
||||
unsigned displacement = 1;
|
||||
//... unless it's the last tab, in which case it's the one before it
|
||||
if(item->offset() == self().items() - 1) displacement = -1;
|
||||
if(item->offset() == self().itemCount() - 1) displacement = -1;
|
||||
//... unless there are no tabs left, in which case nothing is selected
|
||||
if(self().items() > 1) {
|
||||
if(self().itemCount() > 1) {
|
||||
setItemSelected(item->offset() + displacement);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user