mirror of https://github.com/bsnes-emu/bsnes.git
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.
This commit is contained in:
parent
2c61ce2522
commit
7dda70baa4
|
@ -87,8 +87,10 @@ struct Window : Object {
|
|||
nall::function<void ()> 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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<GtkColumn> column;
|
||||
bool checkable;
|
||||
signed selection;
|
||||
|
||||
Data() {
|
||||
store = 0;
|
||||
checkable = false;
|
||||
selection = -1;
|
||||
}
|
||||
};
|
||||
|
||||
void Object::unused() {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -121,6 +121,8 @@ struct Window : Object {
|
|||
nall::function<void ()> 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:
|
||||
|
|
|
@ -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!
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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("");
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
return { abs(rc.left), abs(rc.top), (rc.right - rc.left) - 640, statusHeight + (rc.bottom - rc.top) - 480 };
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -107,10 +107,14 @@ struct Widget : Object {
|
|||
|
||||
struct Window : Widget {
|
||||
nall::function<bool ()> onClose;
|
||||
nall::function<void ()> onMove;
|
||||
nall::function<void ()> 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:
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue