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:
Tim Allen 2011-02-07 20:20:07 +11:00
parent 2c61ce2522
commit 7dda70baa4
23 changed files with 383 additions and 143 deletions

View File

@ -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 {

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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() {

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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:

View File

@ -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!

View File

@ -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;

View File

@ -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();
}
}

View File

@ -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() {

View File

@ -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("");
}

View File

@ -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 {

View File

@ -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 };
}

View File

@ -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;

View File

@ -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:

View File

@ -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;
}
}