From 7dda70baa496551866bb16b93f612d02c416219d Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Mon, 7 Feb 2011 20:20:07 +1100 Subject: [PATCH] Update to v075r10 release. byuu says: phoenix/Windows and phoenix/Qt are mostly fully operational now. All platforms support dynamic layout resizing. I tried WM_GETMINMAXINFO (thanks, OV2), but it was acting kind of choppy on resize, and it would get confused and go crazy if you snapped one direction to the minimum height but not another, so for now I'm leaving it off. phoenix/GTK+ will be missing some functionality in regards to window geometry. The other two have a more coherent strategy now: geometry() is the client area, and setGeometry moves the client area to where you ask for. This makes truly centering your client area trivial. frameGeometry() includes the borders, menu and status. There is no setFrameGeometry(), not sure if I really even want that, but it could be useful so who knows. All targets also support non-resizable windows. X11 is of course horrendously poor with frame sizes, Qt and GTK+ don't even pretend to simulate them, so they say the frame is 0x0 pixels in size until your widget is fully realized and visible to the end user. So for now, to get window positioning right, I have to wait until the window appears and then reposition the window again, causing a slight jump. My plan is to build some persistent caching support directly into phoenix. From here, I can just have the window snap the very first time you run your very first phoenix app. I'll then determine the frame size information, and use that to create future windows. Once they spawn, I'll recheck and update the frame size info in case it has changed (eg user changed themes.) Saving settings into .config/phoenix will allow me to avoid having to snap the window every time on first startup. If the config file is missing or unwritable, too bad, happens every time then. I'm thinking about renaming onResize to onSize, and getting rid of Window::create(). Rather make it spawn like every other control in its constructor. --- bsnes/phoenix/gtk/gtk.hpp | 6 +- bsnes/phoenix/gtk/horizontalslider.cpp | 2 +- bsnes/phoenix/gtk/layout.cpp | 9 +- bsnes/phoenix/gtk/listbox.cpp | 100 ++++++++++------- bsnes/phoenix/gtk/object.cpp | 75 ++++++++++++- bsnes/phoenix/gtk/verticalslider.cpp | 2 +- bsnes/phoenix/gtk/widget.cpp | 1 + bsnes/phoenix/gtk/window.cpp | 32 +++--- bsnes/phoenix/layout/fixed-layout.cpp | 2 +- bsnes/phoenix/layout/horizontal-layout.cpp | 2 +- bsnes/phoenix/layout/vertical-layout.cpp | 2 +- bsnes/phoenix/qt/layout.cpp | 2 +- bsnes/phoenix/qt/qt.hpp | 4 +- bsnes/phoenix/qt/qt.moc | 2 +- bsnes/phoenix/qt/qt.moc.hpp | 15 +-- bsnes/phoenix/qt/window.cpp | 71 +++++++++++- bsnes/phoenix/windows/layout.cpp | 10 +- bsnes/phoenix/windows/listbox.cpp | 4 + bsnes/phoenix/windows/object.cpp | 17 ++- bsnes/phoenix/windows/window.cpp | 123 ++++++++++++--------- bsnes/phoenix/windows/windows.cpp | 35 ++++++ bsnes/phoenix/windows/windows.hpp | 8 +- bsnes/snes/snes.hpp | 2 +- 23 files changed, 383 insertions(+), 143 deletions(-) diff --git a/bsnes/phoenix/gtk/gtk.hpp b/bsnes/phoenix/gtk/gtk.hpp index 3adf2d7a..16531843 100755 --- a/bsnes/phoenix/gtk/gtk.hpp +++ b/bsnes/phoenix/gtk/gtk.hpp @@ -87,8 +87,10 @@ struct Window : Object { nall::function onResize; void create(unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); void setLayout(Layout &layout); + void setResizable(bool resizable = true); bool focused(); void setFocused(); + Geometry frameGeometry(); Geometry geometry(); void setGeometry(unsigned x, unsigned y, unsigned width, unsigned height); void setDefaultFont(Font &font); @@ -110,7 +112,7 @@ struct Window : Object { struct Layout : Object { virtual void setParent(Window &parent); - virtual void setParent(Window &parent, Widget &child); + virtual void append(Widget &widget); virtual void update(Geometry &geometry) = 0; Layout(); //private: @@ -237,6 +239,8 @@ struct ListBox : Widget { //private: struct Data; Data *listBox; +private: + void create(const nall::string &text); }; struct ProgressBar : Widget { diff --git a/bsnes/phoenix/gtk/horizontalslider.cpp b/bsnes/phoenix/gtk/horizontalslider.cpp index 6a38118d..fca12bec 100755 --- a/bsnes/phoenix/gtk/horizontalslider.cpp +++ b/bsnes/phoenix/gtk/horizontalslider.cpp @@ -19,7 +19,7 @@ void HorizontalSlider::setPosition(unsigned position) { HorizontalSlider::HorizontalSlider() { object->position = 0; - object->widget = gtk_hscale_new_with_range(0, 0, 1); + object->widget = gtk_hscale_new_with_range(0, 1, 1); gtk_scale_set_draw_value(GTK_SCALE(object->widget), false); g_signal_connect_swapped(G_OBJECT(object->widget), "value-changed", G_CALLBACK(HorizontalSlider_change), (gpointer)this); } diff --git a/bsnes/phoenix/gtk/layout.cpp b/bsnes/phoenix/gtk/layout.cpp index 19b92220..764af4c6 100755 --- a/bsnes/phoenix/gtk/layout.cpp +++ b/bsnes/phoenix/gtk/layout.cpp @@ -2,9 +2,14 @@ void Layout::setParent(Window &parent) { layout->parent = &parent; } -void Layout::setParent(Window &parent, Widget &child) { - child.widget->parent = &parent; +void Layout::append(Widget &child) { + child.widget->parent = layout->parent; gtk_fixed_put(GTK_FIXED(layout->parent->object->formContainer), child.object->widget, 0, 0); + if(child.widget->font) { + child.setFont(*child.widget->font); + } else if(layout->parent->window->defaultFont) { + child.setFont(*layout->parent->window->defaultFont); + } gtk_widget_show(child.object->widget); } diff --git a/bsnes/phoenix/gtk/listbox.cpp b/bsnes/phoenix/gtk/listbox.cpp index ebae27ea..8683424f 100755 --- a/bsnes/phoenix/gtk/listbox.cpp +++ b/bsnes/phoenix/gtk/listbox.cpp @@ -24,47 +24,7 @@ void ListBox::setFocused() { } void ListBox::setHeaderText(const string &text) { - lstring list; - list.split("\t", string("\t", text)); - - GType *v = (GType*)malloc(list.size() * sizeof(GType)); - for(unsigned i = 0; i < list.size(); i++) v[i] = (i == 0 ? G_TYPE_BOOLEAN : G_TYPE_STRING); - listBox->store = gtk_list_store_newv(list.size(), v); - free(v); - - object->subWidget = gtk_tree_view_new_with_model(GTK_TREE_MODEL(listBox->store)); - gtk_container_add(GTK_CONTAINER(object->widget), object->subWidget); - g_object_unref(G_OBJECT(listBox->store)); - - for(unsigned i = 0; i < list.size(); i++) { - if(i == 0) { - listBox->column[i].renderer = gtk_cell_renderer_toggle_new(); - listBox->column[i].column = gtk_tree_view_column_new_with_attributes( - "", listBox->column[i].renderer, "active", i, (void*)0 - ); - gtk_tree_view_column_set_resizable(listBox->column[i].column, false); - gtk_tree_view_column_set_visible(listBox->column[i].column, listBox->checkable); - g_signal_connect(listBox->column[i].renderer, "toggled", G_CALLBACK(ListBox_tick), (gpointer)this); - } else { - listBox->column[i].renderer = gtk_cell_renderer_text_new(); - listBox->column[i].column = gtk_tree_view_column_new_with_attributes( - "", listBox->column[i].renderer, "text", i, (void*)0 - ); - gtk_tree_view_column_set_resizable(listBox->column[i].column, true); - } - listBox->column[i].label = gtk_label_new(list[i]); - gtk_tree_view_column_set_widget(GTK_TREE_VIEW_COLUMN(listBox->column[i].column), listBox->column[i].label); - gtk_tree_view_append_column(GTK_TREE_VIEW(object->subWidget), listBox->column[i].column); - gtk_widget_show(listBox->column[i].label); - } - gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(object->subWidget), false); - gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(object->subWidget), list.size() >= 3); //>= 2 + one for the checkbox column - gtk_tree_view_set_search_column(GTK_TREE_VIEW(object->subWidget), 1); - - g_signal_connect_swapped(G_OBJECT(object->subWidget), "cursor-changed", G_CALLBACK(ListBox_change), (gpointer)this); - g_signal_connect_swapped(G_OBJECT(object->subWidget), "row-activated", G_CALLBACK(ListBox_activate), (gpointer)this); - - gtk_widget_show(object->subWidget); + create(text); } void ListBox::setHeaderVisible(bool visible) { @@ -181,10 +141,64 @@ void ListBox::setSelection(unsigned row) { ListBox::ListBox() { listBox = new ListBox::Data; - listBox->checkable = false; - listBox->selection = -1; + create(""); +} + +//internal + +void ListBox::create(const string &text) { + if(object->subWidget) gtk_widget_destroy(object->subWidget); + if(object->widget) gtk_widget_destroy(object->widget); object->widget = gtk_scrolled_window_new(0, 0); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(object->widget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(object->widget), GTK_SHADOW_ETCHED_IN); + + lstring list; + list.split("\t", string("\t", text)); + + GType *v = (GType*)malloc(list.size() * sizeof(GType)); + for(unsigned i = 0; i < list.size(); i++) v[i] = (i == 0 ? G_TYPE_BOOLEAN : G_TYPE_STRING); + listBox->store = gtk_list_store_newv(list.size(), v); + free(v); + + object->subWidget = gtk_tree_view_new_with_model(GTK_TREE_MODEL(listBox->store)); + gtk_container_add(GTK_CONTAINER(object->widget), object->subWidget); + g_object_unref(G_OBJECT(listBox->store)); + + for(unsigned i = 0; i < list.size(); i++) { + if(i == 0) { + listBox->column[i].renderer = gtk_cell_renderer_toggle_new(); + listBox->column[i].column = gtk_tree_view_column_new_with_attributes( + "", listBox->column[i].renderer, "active", i, (void*)0 + ); + gtk_tree_view_column_set_resizable(listBox->column[i].column, false); + gtk_tree_view_column_set_visible(listBox->column[i].column, listBox->checkable); + g_signal_connect(listBox->column[i].renderer, "toggled", G_CALLBACK(ListBox_tick), (gpointer)this); + } else { + listBox->column[i].renderer = gtk_cell_renderer_text_new(); + listBox->column[i].column = gtk_tree_view_column_new_with_attributes( + "", listBox->column[i].renderer, "text", i, (void*)0 + ); + gtk_tree_view_column_set_resizable(listBox->column[i].column, true); + } + listBox->column[i].label = gtk_label_new(list[i]); + gtk_tree_view_column_set_widget(GTK_TREE_VIEW_COLUMN(listBox->column[i].column), listBox->column[i].label); + gtk_tree_view_append_column(GTK_TREE_VIEW(object->subWidget), listBox->column[i].column); + gtk_widget_show(listBox->column[i].label); + } + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(object->subWidget), false); + gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(object->subWidget), list.size() >= 3); //>= 2 + one for the checkbox column + gtk_tree_view_set_search_column(GTK_TREE_VIEW(object->subWidget), 1); + + g_signal_connect_swapped(G_OBJECT(object->subWidget), "cursor-changed", G_CALLBACK(ListBox_change), (gpointer)this); + g_signal_connect_swapped(G_OBJECT(object->subWidget), "row-activated", G_CALLBACK(ListBox_activate), (gpointer)this); + + gtk_widget_show(object->subWidget); + + if(widget->font) { + setFont(*widget->font); + } else if(widget->parent && widget->parent->window->defaultFont) { + setFont(*widget->parent->window->defaultFont); + } } diff --git a/bsnes/phoenix/gtk/object.cpp b/bsnes/phoenix/gtk/object.cpp index bebf58a9..6ac5bee2 100755 --- a/bsnes/phoenix/gtk/object.cpp +++ b/bsnes/phoenix/gtk/object.cpp @@ -11,54 +11,119 @@ struct Object::Data { Window *parentWindow; GtkTextBuffer *textBuffer; unsigned position; + + Data() { + locked = false; + widget = 0; + subWidget = 0; + menuContainer = 0; + formContainer = 0; + statusContainer = 0; + menu = 0; + status = 0; + parentMenu = 0; + parentWindow = 0; + textBuffer = 0; + position = 0; + } }; struct Font::Data { PangoFontDescription *font; + + Data() { + font = 0; + } }; struct Action::Data { Font *font; + + Data() { + font = 0; + } }; struct Window::Data { Layout *layout; Font *defaultFont; - bool isFullscreen; + bool resizable; + bool fullscreen; unsigned x, y; unsigned width, height; + + Data() { + layout = 0; + defaultFont = 0; + resizable = true; + fullscreen = false; + x = y = 0; + width = height = 0; + } }; struct Widget::Data { Window *parent; + Font *font; unsigned x, y; unsigned width, height; + + Data() { + parent = 0; + font = 0; + x = y = 0; + width = height = 0; + } }; struct Layout::Data { Window *parent; + + Data() { + parent = 0; + } }; struct ComboBox::Data { unsigned counter; + + Data() { + counter = 0; + } }; struct Canvas::Data { uint32_t *bufferRGB; uint32_t *bufferBGR; unsigned pitch; + + Data() { + bufferRGB = 0; + bufferBGR = 0; + pitch = 0; + } }; struct HexEditor::Data { GtkWidget *container; GtkWidget *widget; GtkWidget *scroll; - GtkTextMark *cursor; unsigned size; unsigned offset; unsigned columns; unsigned rows; + + Data() { + container = 0; + widget = 0; + scroll = 0; + cursor = 0; + size = 0; + offset = 0; + columns = 0; + rows = 0; + } }; struct ListBox::Data { @@ -71,6 +136,12 @@ struct ListBox::Data { linear_vector column; bool checkable; signed selection; + + Data() { + store = 0; + checkable = false; + selection = -1; + } }; void Object::unused() { diff --git a/bsnes/phoenix/gtk/verticalslider.cpp b/bsnes/phoenix/gtk/verticalslider.cpp index 0fdd7e8e..5c6a7a5c 100755 --- a/bsnes/phoenix/gtk/verticalslider.cpp +++ b/bsnes/phoenix/gtk/verticalslider.cpp @@ -19,7 +19,7 @@ void VerticalSlider::setPosition(unsigned position) { VerticalSlider::VerticalSlider() { object->position = 0; - object->widget = gtk_vscale_new_with_range(0, 0, 1); + object->widget = gtk_vscale_new_with_range(0, 1, 1); gtk_scale_set_draw_value(GTK_SCALE(object->widget), false); g_signal_connect_swapped(G_OBJECT(object->widget), "value-changed", G_CALLBACK(VerticalSlider_change), (gpointer)this); } diff --git a/bsnes/phoenix/gtk/widget.cpp b/bsnes/phoenix/gtk/widget.cpp index 2650395e..51390d30 100755 --- a/bsnes/phoenix/gtk/widget.cpp +++ b/bsnes/phoenix/gtk/widget.cpp @@ -6,6 +6,7 @@ static void Widget_setFont(GtkWidget *widget, gpointer font) { } void Widget::setFont(Font &font) { + widget->font = &font; Widget_setFont(object->widget, font.font->font); } diff --git a/bsnes/phoenix/gtk/window.cpp b/bsnes/phoenix/gtk/window.cpp index a450af79..c15bb4de 100755 --- a/bsnes/phoenix/gtk/window.cpp +++ b/bsnes/phoenix/gtk/window.cpp @@ -24,9 +24,6 @@ static void Window_configure(GtkWindow *widget, GdkEvent *event, Window *window) } } -static gboolean Window_expose(GtkWidget *widget, GdkEventExpose *event, Window *window) { -} - void Window::create(unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { window->x = x; window->y = y; @@ -42,7 +39,6 @@ void Window::create(unsigned x, unsigned y, unsigned width, unsigned height, con g_signal_connect_swapped(G_OBJECT(object->widget), "delete_event", G_CALLBACK(Window_close), (gpointer)this); g_signal_connect(G_OBJECT(object->widget), "configure_event", G_CALLBACK(Window_configure), (gpointer)this); - g_signal_connect(G_OBJECT(object->widget), "expose_event", G_CALLBACK(Window_expose), (gpointer)this); object->menuContainer = gtk_vbox_new(false, 0); gtk_container_add(GTK_CONTAINER(object->widget), object->menuContainer); @@ -74,6 +70,11 @@ void Window::setLayout(Layout &layout) { layout.update(geom); } +void Window::setResizable(bool resizable) { + window->resizable = resizable; + gtk_window_set_resizable(GTK_WINDOW(object->widget), resizable); +} + bool Window::focused() { return gtk_window_is_active(GTK_WINDOW(object->widget)); } @@ -82,6 +83,9 @@ void Window::setFocused() { gtk_window_present(GTK_WINDOW(object->widget)); } +Geometry Window::frameGeometry() { +} + Geometry Window::geometry() { return { window->x, window->y, window->width, window->height }; } @@ -130,30 +134,21 @@ void Window::setStatusVisible(bool visible) { } bool Window::fullscreen() { - return window->isFullscreen; + return window->fullscreen; } void Window::setFullscreen(bool fullscreen) { - window->isFullscreen = fullscreen; + window->fullscreen = fullscreen; if(fullscreen == true) { gtk_window_fullscreen(GTK_WINDOW(object->widget)); gtk_window_set_decorated(GTK_WINDOW(object->widget), false); gtk_widget_set_size_request(object->widget, gdk_screen_width(), gdk_screen_height()); + gtk_window_set_resizable(GTK_WINDOW(object->widget), window->resizable); } else { + gtk_window_set_resizable(GTK_WINDOW(object->widget), true); gtk_widget_set_size_request(object->widget, -1, -1); gtk_window_set_decorated(GTK_WINDOW(object->widget), true); gtk_window_unfullscreen(GTK_WINDOW(object->widget)); - - //at this point, GTK+ has not updated window geometry - //this causes Window::geometry() calls to return incorrect info - //thus, wait until the geometry has changed before continuing - Geometry geom; - time_t startTime = time(0); - do { - OS::run(); - Geometry geom = geometry(); - if(startTime - time(0) > 3) break; //prevent application from freezing - } while(geom.x == 0 && geom.y == 0 && geom.width == gdk_screen_width() && geom.height == gdk_screen_height()); } } @@ -161,7 +156,8 @@ Window::Window() { window = new Window::Data; window->layout = 0; window->defaultFont = 0; - window->isFullscreen = false; + window->resizable = false; + window->fullscreen = false; window->x = 0; window->y = 0; window->width = 0; diff --git a/bsnes/phoenix/layout/fixed-layout.cpp b/bsnes/phoenix/layout/fixed-layout.cpp index ecae72ba..b442599a 100755 --- a/bsnes/phoenix/layout/fixed-layout.cpp +++ b/bsnes/phoenix/layout/fixed-layout.cpp @@ -1,7 +1,7 @@ void FixedLayout::setParent(Window &parent) { Layout::setParent(parent); foreach(child, children) { - Layout::setParent(parent, *child.widget); + Layout::append(*child.widget); child.widget->setGeometry(child.x, child.y, child.width, child.height); } } diff --git a/bsnes/phoenix/layout/horizontal-layout.cpp b/bsnes/phoenix/layout/horizontal-layout.cpp index e61b4ff7..5a80c084 100755 --- a/bsnes/phoenix/layout/horizontal-layout.cpp +++ b/bsnes/phoenix/layout/horizontal-layout.cpp @@ -2,7 +2,7 @@ void HorizontalLayout::setParent(Window &parent) { Layout::setParent(parent); foreach(child, children) { if(child.layout) child.layout->setParent(parent); - if(child.widget) Layout::setParent(parent, *child.widget); + if(child.widget) Layout::append(*child.widget); } } diff --git a/bsnes/phoenix/layout/vertical-layout.cpp b/bsnes/phoenix/layout/vertical-layout.cpp index 277851d4..fc07c763 100755 --- a/bsnes/phoenix/layout/vertical-layout.cpp +++ b/bsnes/phoenix/layout/vertical-layout.cpp @@ -2,7 +2,7 @@ void VerticalLayout::setParent(Window &parent) { Layout::setParent(parent); foreach(child, children) { if(child.layout) child.layout->setParent(parent); - if(child.widget) Layout::setParent(parent, *child.widget); + if(child.widget) Layout::append(*child.widget); } } diff --git a/bsnes/phoenix/qt/layout.cpp b/bsnes/phoenix/qt/layout.cpp index 63157325..1d4d315e 100755 --- a/bsnes/phoenix/qt/layout.cpp +++ b/bsnes/phoenix/qt/layout.cpp @@ -2,7 +2,7 @@ void Layout::setParent(Window &parent) { layout->parent = &parent; } -void Layout::setParent(Window &parent, Widget &child) { +void Layout::append(Widget &child) { child.widget->widget->setParent(layout->parent->window->container); if(!child.widget->font && layout->parent->window->defaultFont) { QWidget *control = child.widget->widget; diff --git a/bsnes/phoenix/qt/qt.hpp b/bsnes/phoenix/qt/qt.hpp index 285a91c5..cbc81930 100755 --- a/bsnes/phoenix/qt/qt.hpp +++ b/bsnes/phoenix/qt/qt.hpp @@ -121,6 +121,8 @@ struct Window : Object { nall::function onResize; void create(unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); void setLayout(Layout &layout); + void setResizable(bool resizable = true); + Geometry frameGeometry(); Geometry geometry(); void setGeometry(unsigned x, unsigned y, unsigned width, unsigned height); void setDefaultFont(Font &font); @@ -143,7 +145,7 @@ struct Window : Object { struct Layout : Object { virtual void setParent(Window &parent); - virtual void setParent(Window &parent, Widget &child); + virtual void append(Widget &widget); virtual void update(Geometry &geometry) = 0; Layout(); //private: diff --git a/bsnes/phoenix/qt/qt.moc b/bsnes/phoenix/qt/qt.moc index 4be6f8a0..cead6899 100755 --- a/bsnes/phoenix/qt/qt.moc +++ b/bsnes/phoenix/qt/qt.moc @@ -1,7 +1,7 @@ /**************************************************************************** ** Meta object code from reading C++ file 'qt.moc.hpp' ** -** Created: Sat Feb 5 21:33:04 2011 +** Created: Mon Feb 7 01:23:56 2011 ** by: The Qt Meta Object Compiler version 62 (Qt 4.6.2) ** ** WARNING! All changes made in this file will be lost! diff --git a/bsnes/phoenix/qt/qt.moc.hpp b/bsnes/phoenix/qt/qt.moc.hpp index 35cce53d..cb92ef73 100755 --- a/bsnes/phoenix/qt/qt.moc.hpp +++ b/bsnes/phoenix/qt/qt.moc.hpp @@ -87,7 +87,7 @@ struct Window::Data : public QWidget { public: Window &self; unsigned x, y, width, height; - bool fullscreen, menuVisible, statusVisible; + bool resizable, fullscreen, menuVisible, statusVisible; Layout *layout; QFont *defaultFont; QVBoxLayout *vlayout; @@ -110,18 +110,18 @@ public: } void moveEvent(QMoveEvent *event) { - if(fullscreen == false && isVisible() == true) { - x = frameGeometry().x(); - y = frameGeometry().y(); + if(self.object->locked == false && fullscreen == false && isVisible() == true) { + x += event->pos().x() - event->oldPos().x(); + y += event->pos().y() - event->oldPos().y(); } - if(self.onMove) { + if(self.object->locked == false && self.onMove) { self.onMove(); } } void resizeEvent(QResizeEvent *event) { - if(fullscreen == false && isVisible() == true) { + if(self.object->locked == false && fullscreen == false && isVisible() == true) { width = container->geometry().width(); height = container->geometry().height(); } @@ -132,12 +132,13 @@ public: layout->update(geom); } - if(self.onResize) { + if(self.object->locked == false && self.onResize) { self.onResize(); } } Data(Window &self) : self(self) { + resizable = true; fullscreen = false; menuVisible = false; statusVisible = false; diff --git a/bsnes/phoenix/qt/window.cpp b/bsnes/phoenix/qt/window.cpp index b395201b..7ba64c33 100755 --- a/bsnes/phoenix/qt/window.cpp +++ b/bsnes/phoenix/qt/window.cpp @@ -32,20 +32,67 @@ void Window::setLayout(Layout &layout) { layout.update(geom); } +void Window::setResizable(bool resizable) { + window->resizable = resizable; + if(window->resizable) { + window->vlayout->setSizeConstraint(QLayout::SetDefaultConstraint); + window->container->setMinimumSize(window->width, window->height); + } else { + window->vlayout->setSizeConstraint(QLayout::SetFixedSize); + window->container->setFixedSize(window->width, window->height); + } +} + +Geometry Window::frameGeometry() { + if(window->fullscreen) return { 0, 0, OS::desktopWidth(), OS::desktopHeight() }; + + QRect rect = window->frameGeometry(); + unsigned x = rect.x(); + unsigned y = rect.y(); + unsigned width = rect.width(); + unsigned height = rect.height(); + if(x > 65535) x = 0; + if(y > 65535) y = 0; + + return { x, y, width, height }; +} + Geometry Window::geometry() { - //QWidget::geometry() is not at all reliable - if(window->fullscreen == false) return { window->x, window->y, window->width, window->height }; - return { 0, 0, OS::desktopWidth(), OS::desktopHeight() }; + if(window->fullscreen) return { 0, 0, OS::desktopWidth(), OS::desktopHeight() }; + return { window->x, window->y, window->width, window->height }; + + unsigned x = window->x; + unsigned y = window->y; + + if(window->fullscreen == false) { + QRect border = window->frameGeometry(); + QRect client = window->geometry(); + x += client.x() - border.x(); + y += client.y() - border.y(); + if(window->menuVisible) y += window->menuBar->height(); + if(x > 65535) x = 0; + if(y > 65535) y = 0; + } + + return { x, y, window->width, window->height }; } void Window::setGeometry(unsigned x, unsigned y, unsigned width, unsigned height) { + object->locked = true; + + QRect border = window->frameGeometry(); + QRect client = window->geometry(); + window->x = x; window->y = y; window->width = width; window->height = height; - window->move(x, y); + setResizable(window->resizable); + window->move(x - (client.x() - border.x()), y - (client.y() - border.y())); window->adjustSize(); + + object->locked = false; } void Window::setDefaultFont(Font &font) { @@ -73,12 +120,23 @@ void Window::setStatusText(const string &text) { } void Window::setVisible(bool visible) { + object->locked = true; + if(visible) { window->show(); - window->adjustSize(); + //an unfortunate hack: before window is visible, geometry() == frameGeometry(); + //we must wait for frameGeometry() to correctly populate and then move window + if(window->fullscreen == false) for(unsigned n = 0; n < 100; n++) { + if(window->geometry().x() > window->frameGeometry().x()) break; + usleep(100); + QApplication::processEvents(); + } + setGeometry(window->x, window->y, window->width, window->height); } else { window->hide(); } + + object->locked = false; } void Window::setMenuVisible(bool visible) { @@ -105,9 +163,12 @@ void Window::setFullscreen(bool fullscreen) { window->fullscreen = fullscreen; if(fullscreen == false) { + setResizable(window->resizable); window->showNormal(); window->adjustSize(); } else { + window->vlayout->setSizeConstraint(QLayout::SetDefaultConstraint); + window->container->setFixedSize(OS::desktopWidth(), OS::desktopHeight()); window->showFullScreen(); } } diff --git a/bsnes/phoenix/windows/layout.cpp b/bsnes/phoenix/windows/layout.cpp index 8c47924e..b68a2119 100755 --- a/bsnes/phoenix/windows/layout.cpp +++ b/bsnes/phoenix/windows/layout.cpp @@ -2,10 +2,12 @@ void Layout::setParent(Window &parent) { layout->parent = &parent; } -void Layout::setParent(Window &parent, Widget &child) { - SetParent(child.widget->window, parent.widget->window); - - SendMessage(child.widget->window, WM_SETFONT, (WPARAM)(parent.window->defaultFont ? parent.window->defaultFont : OS::os->proportionalFont), 0); +void Layout::append(Widget &child) { + SetParent(child.widget->window, layout->parent->widget->window); + SendMessage( + child.widget->window, WM_SETFONT, + (WPARAM)(layout->parent->window->defaultFont ? layout->parent->window->defaultFont : OS::os->proportionalFont), 0 + ); } Layout::Layout() { diff --git a/bsnes/phoenix/windows/listbox.cpp b/bsnes/phoenix/windows/listbox.cpp index b8df535d..612b3ef4 100755 --- a/bsnes/phoenix/windows/listbox.cpp +++ b/bsnes/phoenix/windows/listbox.cpp @@ -1,4 +1,7 @@ void ListBox::setHeaderText(const string &text) { + //delete all existing columns + while(ListView_DeleteColumn(widget->window, 0)); + lstring list; list.split("\t", text); listBox->columns = list.size(); @@ -110,4 +113,5 @@ ListBox::ListBox() { ); SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); ListView_SetExtendedListViewStyle(widget->window, LVS_EX_FULLROWSELECT); + setHeaderText(""); } diff --git a/bsnes/phoenix/windows/object.cpp b/bsnes/phoenix/windows/object.cpp index e5706fb3..fac81e70 100755 --- a/bsnes/phoenix/windows/object.cpp +++ b/bsnes/phoenix/windows/object.cpp @@ -16,16 +16,31 @@ struct Action::Data { }; struct Window::Data { + Layout *layout; HFONT defaultFont; HBRUSH brush; COLORREF brushColor; HMENU menu; HWND status; - bool isFullscreen; + bool resizable; + bool fullscreen; unsigned x; unsigned y; unsigned width; unsigned height; + + Data() { + layout = 0; + defaultFont = 0; + brush = 0; + brushColor = 0; + menu = 0; + status = 0; + resizable = true; + fullscreen = false; + x = y = 0; + width = height = 0; + } }; struct Widget::Data { diff --git a/bsnes/phoenix/windows/window.cpp b/bsnes/phoenix/windows/window.cpp index bbe9a3b4..df7c7ec3 100755 --- a/bsnes/phoenix/windows/window.cpp +++ b/bsnes/phoenix/windows/window.cpp @@ -1,30 +1,36 @@ +static const unsigned FixedStyle = WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX | WS_BORDER; +static const unsigned ResizableStyle = WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME; + void Window::create(unsigned x, unsigned y, unsigned width, unsigned height, const string &text) { widget->window = CreateWindowEx( - 0, L"phoenix_window", utf16_t(text), - WS_POPUP | WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX, - window->x = x, window->y = y, window->width = width, window->height = height, - 0, 0, GetModuleHandle(0), 0 + 0, L"phoenix_window", utf16_t(text), ResizableStyle, + 0, 0, 64, 64, 0, 0, GetModuleHandle(0), 0 ); window->menu = CreateMenu(); window->status = CreateWindowEx( - 0, STATUSCLASSNAME, L"", - WS_CHILD, - 0, 0, 0, 0, - widget->window, 0, GetModuleHandle(0), 0 + 0, STATUSCLASSNAME, L"", WS_CHILD, + 0, 0, 0, 0, widget->window, 0, GetModuleHandle(0), 0 ); //StatusBar will be capable of receiving tab focus if it is not disabled SetWindowLongPtr(window->status, GWL_STYLE, GetWindowLong(window->status, GWL_STYLE) | WS_DISABLED); - resize(width, height); SetWindowLongPtr(widget->window, GWLP_USERDATA, (LONG_PTR)this); + setGeometry(x, y, width, height); } void Window::setLayout(Layout &layout) { + window->layout = &layout; Geometry geom = geometry(); geom.x = geom.y = 0; layout.setParent(*this); layout.update(geom); } +void Window::setResizable(bool resizable) { + window->resizable = resizable; + SetWindowLongPtr(widget->window, GWL_STYLE, window->resizable ? ResizableStyle : FixedStyle); + setGeometry(window->x, window->y, window->width, window->height); +} + void Window::setDefaultFont(Font &font) { window->defaultFont = font.font->font; } @@ -33,31 +39,47 @@ void Window::setFont(Font &font) { SendMessage(window->status, WM_SETFONT, (WPARAM)font.font->font, 0); } +Geometry Window::frameGeometry() { + RECT rc; + GetWindowRect(widget->window, &rc); + + unsigned x = rc.left; + unsigned y = rc.top; + unsigned width = (rc.right - rc.left); + unsigned height = (rc.bottom - rc.top); + if((signed)x < 0) x = 0; + if((signed)y < 0) y = 0; + + return { x, y, width, height }; +} + Geometry Window::geometry() { - RECT position, size; - GetWindowRect(widget->window, &position); - GetClientRect(widget->window, &size); - if(GetWindowLongPtr(window->status, GWL_STYLE) & WS_VISIBLE) { - RECT status; - GetClientRect(window->status, &status); - size.bottom -= status.bottom - status.top; - } - return Geometry(position.left, position.top, size.right, size.bottom); + Geometry fm = frameMargin(); + RECT rc; + GetWindowRect(widget->window, &rc); + + unsigned x = rc.left + fm.x; + unsigned y = rc.top + fm.y; + unsigned width = (rc.right - rc.left) - fm.width; + unsigned height = (rc.bottom - rc.top) - fm.height; + if((signed)x < 0) x = 0; + if((signed)y < 0) y = 0; + + return { x, y, width, height }; } void Window::setGeometry(unsigned x, unsigned y, unsigned width, unsigned height) { - if(window->isFullscreen == false) { + object->locked = true; + if(window->fullscreen == false) { window->x = x; window->y = y; window->width = width; window->height = height; } - - bool isVisible = visible(); - if(isVisible) setVisible(false); - SetWindowPos(widget->window, NULL, x, y, width, height, SWP_NOZORDER | SWP_FRAMECHANGED); - resize(width, height); - if(isVisible) setVisible(true); + Geometry fm = frameMargin(); + SetWindowPos(widget->window, NULL, x - fm.x, y - fm.y, width + fm.width, height + fm.height, SWP_NOZORDER | SWP_FRAMECHANGED); + SetWindowPos(window->status, NULL, 0, 0, 0, 0, SWP_NOZORDER | SWP_FRAMECHANGED); + object->locked = false; } void Window::setBackgroundColor(uint8_t red, uint8_t green, uint8_t blue) { @@ -75,53 +97,54 @@ void Window::setStatusText(const string &text) { } void Window::setMenuVisible(bool visible) { + object->locked = true; SetMenu(widget->window, visible ? window->menu : 0); - resize(window->width, window->height); + setGeometry(window->x, window->y, window->width, window->height); + object->locked = false; } void Window::setStatusVisible(bool visible) { + object->locked = true; ShowWindow(window->status, visible ? SW_SHOWNORMAL : SW_HIDE); - resize(window->width, window->height); + setGeometry(window->x, window->y, window->width, window->height); + object->locked = false; } bool Window::fullscreen() { - return window->isFullscreen; + return window->fullscreen; } void Window::setFullscreen(bool fullscreen) { - window->isFullscreen = fullscreen; - if(fullscreen == false) { - SetWindowLong(widget->window, GWL_STYLE, WS_VISIBLE | WS_POPUP | WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX); + object->locked = true; + window->fullscreen = fullscreen; + if(window->fullscreen == false) { + SetWindowLong(widget->window, GWL_STYLE, WS_VISIBLE | (window->resizable ? ResizableStyle : FixedStyle)); setGeometry(window->x, window->y, window->width, window->height); } else { SetWindowLong(widget->window, GWL_STYLE, WS_VISIBLE | WS_POPUP); setGeometry(0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN)); } + object->locked = false; } Window::Window() { window = new Window::Data; - window->defaultFont = 0; - window->brush = 0; - window->isFullscreen = false; - window->x = 0; - window->y = 0; - window->width = 0; - window->height = 0; } -void Window::resize(unsigned width, unsigned height) { - SetWindowPos(widget->window, NULL, 0, 0, width, height, SWP_NOZORDER | SWP_NOMOVE | SWP_FRAMECHANGED); - RECT rc; - GetClientRect(widget->window, &rc); - width += width - (rc.right - rc.left); - height += height - (rc.bottom - rc.top); +//internal +Geometry Window::frameMargin() { + //determine the frame overhead (window border, menu bar, status bar) to compute client geometry + //x,y: pixels from the top-left of window to the client area + //width,height: total pixels of window geometry that are not part of client area + //width-x,height-y: pixels from the bottom-right of window to the client area + RECT rc = { 0, 0, 640, 480 }; + AdjustWindowRect(&rc, window->resizable ? ResizableStyle : FixedStyle, (bool)GetMenu(widget->window)); + unsigned statusHeight = 0; if(GetWindowLongPtr(window->status, GWL_STYLE) & WS_VISIBLE) { - GetClientRect(window->status, &rc); - height += rc.bottom - rc.top; + RECT src; + GetClientRect(window->status, &src); + statusHeight = src.bottom - src.top; } - - SetWindowPos(widget->window, NULL, 0, 0, width, height, SWP_NOZORDER | SWP_NOMOVE | SWP_FRAMECHANGED); - SetWindowPos(window->status, NULL, 0, 0, 0, 0, SWP_NOZORDER | SWP_FRAMECHANGED); -} \ No newline at end of file + return { abs(rc.left), abs(rc.top), (rc.right - rc.left) - 640, statusHeight + (rc.bottom - rc.top) - 480 }; +} diff --git a/bsnes/phoenix/windows/windows.cpp b/bsnes/phoenix/windows/windows.cpp index f679b9f0..90006af5 100755 --- a/bsnes/phoenix/windows/windows.cpp +++ b/bsnes/phoenix/windows/windows.cpp @@ -314,6 +314,41 @@ static LRESULT CALLBACK OS_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM return TRUE; } + case WM_MOVE: { + if(window.object->locked == true) break; + + Geometry geometry = window.geometry(); + window.window->x = geometry.x; + window.window->y = geometry.y; + + if(window.onMove) window.onMove(); + break; + } + + case WM_SIZE: { + if(window.object->locked == true) break; + SetWindowPos(window.window->status, NULL, 0, 0, 0, 0, SWP_NOZORDER | SWP_FRAMECHANGED); + + Geometry geometry = window.geometry(); + window.window->width = geometry.width; + window.window->height = geometry.height; + + if(window.window->layout) { + geometry.x = geometry.y = 0; + window.window->layout->update(geometry); + } + + if(window.onResize) window.onResize(); + break; + } + + case WM_GETMINMAXINFO: { + MINMAXINFO *mmi = (MINMAXINFO*)lparam; + //mmi->ptMinTrackSize.x = window.window->width; + //mmi->ptMinTrackSize.y = window.window->height; + //return TRUE; + } + case WM_ERASEBKGND: { if(window.window->brush == 0) break; RECT rc; diff --git a/bsnes/phoenix/windows/windows.hpp b/bsnes/phoenix/windows/windows.hpp index 9b3b046f..8ea6764d 100755 --- a/bsnes/phoenix/windows/windows.hpp +++ b/bsnes/phoenix/windows/windows.hpp @@ -107,10 +107,14 @@ struct Widget : Object { struct Window : Widget { nall::function onClose; + nall::function onMove; + nall::function onResize; void create(unsigned x, unsigned y, unsigned width, unsigned height, const nall::string &text = ""); void setLayout(Layout &layout); + void setResizable(bool resizable); void setDefaultFont(Font &font); void setFont(Font &font); + Geometry frameGeometry(); Geometry geometry(); void setGeometry(unsigned x, unsigned y, unsigned width, unsigned height); void setBackgroundColor(uint8_t red, uint8_t green, uint8_t blue); @@ -125,12 +129,14 @@ struct Window : Widget { struct Data; Data *window; static Window None; +//private: + Geometry frameMargin(); void resize(unsigned width, unsigned height); }; struct Layout : Widget { virtual void setParent(Window &parent); - virtual void setParent(Window &parent, Widget &child); + virtual void append(Widget &widget); virtual void update(Geometry &geometry) = 0; Layout(); //private: diff --git a/bsnes/snes/snes.hpp b/bsnes/snes/snes.hpp index 46b54125..2cea1fad 100755 --- a/bsnes/snes/snes.hpp +++ b/bsnes/snes/snes.hpp @@ -1,7 +1,7 @@ namespace SNES { namespace Info { static const char Name[] = "bsnes"; - static const char Version[] = "075.09"; + static const char Version[] = "075.10"; static const unsigned SerializerVersion = 18; } }