diff --git a/common/controlsigs.h b/common/controlsigs.h index 944afa9b3..5ed705ff4 100644 --- a/common/controlsigs.h +++ b/common/controlsigs.h @@ -11,6 +11,7 @@ #define uiDateTimePickerSignature 0x44545069 #define uiEditableComboboxSignature 0x45644362 #define uiEntrySignature 0x456E7472 +#define uiFixedSignature 0x46697864 #define uiFontButtonSignature 0x466F6E42 #define uiFormSignature 0x466F726D #define uiGridSignature 0x47726964 diff --git a/darwin/CMakeLists.txt b/darwin/CMakeLists.txt index bbab3c9ab..8e33d04d9 100644 --- a/darwin/CMakeLists.txt +++ b/darwin/CMakeLists.txt @@ -19,6 +19,7 @@ list(APPEND _LIBUI_SOURCES darwin/drawtext.m darwin/editablecombo.m darwin/entry.m + darwin/fixed.m darwin/fontbutton.m darwin/fontmatch.m darwin/fonttraits.m diff --git a/darwin/fixed.m b/darwin/fixed.m new file mode 100644 index 000000000..e2e4b5c8e --- /dev/null +++ b/darwin/fixed.m @@ -0,0 +1,248 @@ +// 19 may 2018 +#import "uipriv_darwin.h" + +@interface uiprivFixedChild : NSObject +@property uiControl *c; +@property int x; +@property int y; +- (NSView *)view; +@end + +@interface uiprivFixedView : NSView { + uiFixed *b; + NSMutableArray *children; +} +- (id)initFixed:(uiFixed *)bb; +- (void)onDestroy; +- (void)syncEnableStates:(int)enabled; +- (void)append:(uiControl *)c x:(int)x y:(int)y; +- (void)move:(uiControl *)c x:(int)x y:(int)y; +- (void)reloadPositions; +- (void)size:(uiControl *)c width:(int *)width height:(int *)height; +- (void)setSize:(uiControl *)c width:(int)width height:(int)height; +@end + +struct uiFixed { + uiDarwinControl c; + uiprivFixedView *view; +}; + +@implementation uiprivFixedChild + +- (NSView *)view +{ + return (NSView *) uiControlHandle(self.c); +} + +@end + +@implementation uiprivFixedView + +- (id)initFixed:(uiFixed *)bb +{ + self = [super initWithFrame:NSZeroRect]; + if (self != nil) { + self->b = bb; + self->children = [NSMutableArray new]; + } + + return self; +} + +- (BOOL)isFlipped +{ + return YES; +} + +- (void)onDestroy +{ + uiprivFixedChild *bc; + + for (bc in self->children) { + uiControlSetParent(bc.c, NULL); + uiDarwinControlSetSuperview(uiDarwinControl(bc.c), nil); + uiControlDestroy(bc.c); + } + [self->children release]; +} + +- (void)syncEnableStates:(int)enabled +{ + uiprivFixedChild *bc; + + for (bc in self->children) + uiDarwinControlSyncEnableState(uiDarwinControl(bc.c), enabled); +} + +- (void)append:(uiControl *)c x:(int)x y:(int)y +{ + uiprivFixedChild *bc; + + bc = [uiprivFixedChild new]; + bc.c = c; + bc.x = x; + bc.y = y; + + uiControlSetParent(bc.c, uiControl(self->b)); + uiDarwinControlSetSuperview(uiDarwinControl(bc.c), self); + uiDarwinControlSyncEnableState(uiDarwinControl(bc.c), uiControlEnabledToUser(uiControl(self->b))); + + [self->children addObject:bc]; + + [self reloadPositions]; + + [bc release]; // we don't need the initial reference now +} + +- (void)move:(uiControl *)c x:(int)x y:(int)y +{ + uiprivFixedChild *fc; + + for (fc in self->children) { + if (fc.c == c) { + fc.x = x; + fc.y = y; + [self reloadPositions]; + return; + } + } +} + +- (void)reloadPositions +{ + uiprivFixedChild *fc; + CGPoint pos; + NSView *view; + + for (fc in self->children) { + if (!uiControlVisible(fc.c)) + continue; + pos = CGPointMake(fc.x, fc.y); + view = [fc view]; + [view setFrame:(CGRect){.origin = pos, .size=view.frame.size}]; + } +} + +- (void)size:(uiControl *)c width:(int *)width height:(int *)height +{ + uiprivFixedChild *fc; + NSView *view; + + for (fc in self->children) { + if (!uiControlVisible(fc.c)) + continue; + if (fc.c == c) { + view = [fc view]; + *width = view.frame.size.width; + *height = view.frame.size.height; + } + } +} + +- (void)setSize:(uiControl *)c width:(int)width height:(int)height +{ + uiprivFixedChild *fc; + NSView *view; + + for (fc in self->children) { + if (!uiControlVisible(fc.c)) + continue; + if (fc.c == c) { + view = [fc view]; + + CGRect bounds = view.bounds; + bounds.size.width = width; + bounds.size.height = height; + view.bounds = bounds; + + CGRect frame = view.frame; + frame.size.width = width; + frame.size.height = height; + view.frame = frame; + } + } +} + +@end + +static void uiFixedDestroy(uiControl *c) +{ + uiFixed *b = uiFixed(c); + + [b->view onDestroy]; + [b->view release]; + uiFreeControl(uiControl(b)); +} + +uiDarwinControlDefaultHandle(uiFixed, view) +uiDarwinControlDefaultParent(uiFixed, view) +uiDarwinControlDefaultSetParent(uiFixed, view) +uiDarwinControlDefaultToplevel(uiFixed, view) +uiDarwinControlDefaultVisible(uiFixed, view) +uiDarwinControlDefaultShow(uiFixed, view) +uiDarwinControlDefaultHide(uiFixed, view) +uiDarwinControlDefaultEnabled(uiFixed, view) +uiDarwinControlDefaultEnable(uiFixed, view) +uiDarwinControlDefaultDisable(uiFixed, view) + +static void uiFixedSyncEnableState(uiDarwinControl *c, int enabled) +{ + uiFixed *b = uiFixed(c); + + if (uiDarwinShouldStopSyncEnableState(uiDarwinControl(b), enabled)) + return; + [b->view syncEnableStates:enabled]; +} + +uiDarwinControlDefaultSetSuperview(uiFixed, view) + +uiDarwinControlDefaultHuggingPriority(uiFixed, view) +uiDarwinControlDefaultSetHuggingPriority(uiFixed, view) +uiDarwinControlDefaultHugsTrailingEdge(uiFixed, view) +uiDarwinControlDefaultHugsBottom(uiFixed, view) +uiDarwinControlDefaultChildEdgeHuggingChanged(uiFixed, view) + +static void uiFixedChildVisibilityChanged(uiDarwinControl *c) +{ + uiFixed *b = uiFixed(c); + + [b->view reloadPositions]; +} + +void uiFixedAppend(uiFixed *b, uiControl *c, int x, int y) +{ + // LONGTERM on other platforms + // or at leat allow this and implicitly turn it into a spacer + if (c == NULL) + uiprivUserBug("You cannot add NULL to a uiFixed."); + [b->view append:c x:x y:y]; +} + +void uiFixedMove(uiFixed *b, uiControl *c, int x, int y) +{ + // LONGTERM on other platforms + // or at leat allow this and implicitly turn it into a spacer + if (c == NULL) + uiprivUserBug("You cannot move NULL to a uiFixed."); + [b->view move:c x:x y:y]; +} + +void uiFixedSize(uiFixed *b, uiControl *c, int *width, int *height) +{ + [b->view size:c width:width height:height]; +} + +void uiFixedSetSize(uiFixed *b, uiControl *c, int width, int height) +{ + [b->view setSize:c width:width height:height]; +} + +uiFixed *uiNewFixed(void) +{ + uiFixed *f; + uiDarwinNewControl(uiFixed, f); + + f->view = [[uiprivFixedView alloc] initFixed:f]; + + return f; +} diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index b68f97067..651a045cb 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -18,6 +18,16 @@ _add_example(controlgallery ${_EXAMPLE_RESOURCES_RC} ) +_add_example(layout-test + layout-test/main.c + ${_EXAMPLE_RESOURCES_RC} +) + +set_target_properties(layout-test PROPERTIES + OUTPUT_NAME layout-test + WIN32_EXECUTABLE FALSE +) + _add_example(histogram histogram/main.c ${_EXAMPLE_RESOURCES_RC} @@ -57,6 +67,7 @@ add_custom_target(examples controlgallery histogram cpp-multithread + layout-test drawtext timer datetime) diff --git a/examples/layout-test/main.c b/examples/layout-test/main.c new file mode 100644 index 000000000..459722465 --- /dev/null +++ b/examples/layout-test/main.c @@ -0,0 +1,46 @@ +#include +#include +#include "../../ui.h" + +int main(void) +{ + uiInitOptions options; + const char *err; + + memset(&options, 0, sizeof (uiInitOptions)); + err = uiInit(&options); + if (err != NULL) { + fprintf(stderr, "error initializing libui: %s", err); + uiFreeInitError(err); + return 1; + } + + uiWindow *mainwin = uiNewWindow("Layout test", 640, 480, 1); + + uiFixed *fixed = uiNewFixed(); + + uiWindowSetChild(mainwin, uiControl(fixed)); + + uiLabel *label = uiNewLabel("Hello"); + + uiFixedAppend(fixed, uiControl(label), 150, 200); + + uiButton *button = uiNewButton("Button"); + + uiFixedAppend(fixed, uiControl(button), 50, 50); + + uiControlShow(uiControl(mainwin)); + + + int width, height; + + uiFixedMove(fixed, uiControl(button), 0, 100); + + uiFixedSize(fixed, uiControl(button), &width, &height); + printf("Width: %d, Height: %d\n", width, height); + + //uiFixedSetSize(fixed, uiControl(button), 50, 50); + + uiMain(); + return 0; +} diff --git a/ui.h b/ui.h index b5fb9a271..cade271e4 100644 --- a/ui.h +++ b/ui.h @@ -1123,6 +1123,14 @@ _UI_EXTERN int uiGridPadded(uiGrid *g); _UI_EXTERN void uiGridSetPadded(uiGrid *g, int padded); _UI_EXTERN uiGrid *uiNewGrid(void); +typedef struct uiFixed uiFixed; +#define uiFixed(this) ((uiFixed *) (this)) +_UI_EXTERN void uiFixedAppend(uiFixed *g, uiControl *c, int x, int y); +_UI_EXTERN void uiFixedMove(uiFixed *g, uiControl *c, int x, int y); +_UI_EXTERN void uiFixedSize(uiFixed *b, uiControl *control, int *width, int *height); +_UI_EXTERN void uiFixedSetSize(uiFixed *b, uiControl *control, int width, int height); +_UI_EXTERN uiFixed *uiNewFixed(void); + #ifdef __cplusplus } #endif diff --git a/ui_unix.h b/ui_unix.h index ed0192605..f53c84524 100644 --- a/ui_unix.h +++ b/ui_unix.h @@ -15,6 +15,8 @@ typedef struct uiUnixControl uiUnixControl; struct uiUnixControl { uiControl c; uiControl *parent; + int width; + int height; gboolean addedBefore; gboolean explicitlyHidden; void (*SetContainer)(uiUnixControl *, GtkContainer *, gboolean); diff --git a/ui_windows.h b/ui_windows.h index 69dda3666..87652be07 100644 --- a/ui_windows.h +++ b/ui_windows.h @@ -217,6 +217,9 @@ _UI_EXTERN int uiWindowsWindowTextWidth(HWND hwnd); // TODO point out this should only be used in a resize cycle _UI_EXTERN void uiWindowsEnsureMoveWindowDuringResize(HWND hwnd, int x, int y, int width, int height); +_UI_EXTERN void uiWindowsResizeWindow(HWND hwnd, int width, int height); +_UI_EXTERN void uiWindowsMoveWindow(HWND hwnd, int x, int y); + // TODO document _UI_EXTERN void uiWindowsRegisterWM_COMMANDHandler(HWND hwnd, BOOL (*handler)(uiControl *, HWND, WORD, LRESULT *), uiControl *c); _UI_EXTERN void uiWindowsUnregisterWM_COMMANDHandler(HWND hwnd); diff --git a/unix/CMakeLists.txt b/unix/CMakeLists.txt index 788731465..29dac80ff 100644 --- a/unix/CMakeLists.txt +++ b/unix/CMakeLists.txt @@ -23,6 +23,7 @@ list(APPEND _LIBUI_SOURCES unix/drawtext.c unix/editablecombo.c unix/entry.c + unix/fixed.c unix/fontbutton.c unix/fontmatch.c unix/form.c diff --git a/unix/fixed.c b/unix/fixed.c new file mode 100644 index 000000000..fc72674f3 --- /dev/null +++ b/unix/fixed.c @@ -0,0 +1,126 @@ +// 9 june 2016 +#include "uipriv_unix.h" +#include "fixedContainer.c" + +struct fixedChild { + uiControl *c; + int width; + int height; +}; + +struct uiFixed { + uiUnixControl c; + GtkWidget *widget; + GtkContainer *container; + fixedContainer *fixed; + GArray *children; +}; + +uiUnixControlAllDefaultsExceptDestroy(uiFixed) + +#define ctrl(g, i) &g_array_index(g->children, struct fixedChild, i) + +static void uiFixedDestroy(uiControl *c) +{ + uiFixed *g = uiFixed(c); + struct fixedChild *gc; + guint i; + + // free all controls + for (i = 0; i < g->children->len; i++) { + gc = ctrl(g, i); + uiControlSetParent(gc->c, NULL); + uiUnixControlSetContainer(uiUnixControl(gc->c), g->container, TRUE); + uiControlDestroy(gc->c); + } + g_array_free(g->children, TRUE); + // and then ourselves + g_object_unref(g->widget); + uiFreeControl(uiControl(g)); +} + +#define TODO_MASSIVE_HACK(c) \ + if (!uiUnixControl(c)->addedBefore) { \ + g_object_ref_sink(GTK_WIDGET(uiControlHandle(uiControl(c)))); \ + gtk_widget_show(GTK_WIDGET(uiControlHandle(uiControl(c)))); \ + uiUnixControl(c)->addedBefore = TRUE; \ + } + +static void uiFixedSizeCallback(GtkWidget *widget, GtkAllocation *allocation, struct fixedChild *data) +{ + if (allocation->height == 0 || allocation->width == 0) + return; + data->height = allocation->height; + data->width = allocation->width; +} + +static GtkWidget *prepare(struct fixedChild *gc, uiControl *c) +{ + GtkWidget *widget; + + gc->c = c; + widget = GTK_WIDGET(uiControlHandle(gc->c)); + return widget; +} + +void uiFixedAppend(uiFixed *g, uiControl *c, int x, int y) +{ + struct fixedChild gc; + GtkWidget *widget; + + widget = prepare(&gc, c); + uiControlSetParent(gc.c, uiControl(g)); + TODO_MASSIVE_HACK(uiUnixControl(gc.c)); + fixedContainer_put(g->fixed, widget, x, y); + g_array_append_val(g->children, gc); + + g_signal_connect(widget, "size-allocate", G_CALLBACK(uiFixedSizeCallback), &gc); +} + +void uiFixedMove(uiFixed *g, uiControl *c, int x, int y) +{ + fixedContainer_move(g->fixed, GTK_WIDGET(uiControlHandle(c)), x, y); +} + +void uiFixedSize(uiFixed *g, uiControl *c, int *width, int *height) +{ + struct fixedChild *gc; + guint i; + + for (i = 0; i < g->children->len; i++) { + gc = ctrl(g, i); + if (gc->c == c) { + if (gc->width == 0 || gc->height == 0) { + GtkRequisition natural_size; + gtk_widget_get_preferred_size(GTK_WIDGET(uiControlHandle(c)), NULL, &natural_size); + if (natural_size.width == 0 || natural_size.height == 0) + return; + *width = natural_size.width; + *height = natural_size.height; + } else { + *width = gc->width; + *height = gc->height; + } + } + } +} + +void uiFixedSetSize(uiFixed *g, uiControl *c, int width, int height) +{ + gtk_widget_set_size_request(GTK_WIDGET(uiControlHandle(c)), width, height); +} + +uiFixed *uiNewFixed(void) +{ + uiFixed *g; + + uiUnixNewControl(uiFixed, g); + + g->widget = fixedContainer_new(); + g->container = GTK_CONTAINER(g->widget); + g->fixed = FIXEDCONTAINER(g->widget); + + g->children = g_array_new(FALSE, TRUE, sizeof (struct fixedChild)); + + return g; +} diff --git a/unix/fixedContainer.c b/unix/fixedContainer.c new file mode 100644 index 000000000..258b5f957 --- /dev/null +++ b/unix/fixedContainer.c @@ -0,0 +1,178 @@ +#include + +GType gtk_fixed_get_type (void) G_GNUC_CONST; + +#define FIXEDCONTAINER_TYPE (fixedContainer_get_type()) +#define FIXEDCONTAINER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), FIXEDCONTAINER_TYPE, fixedContainer)) +#define IS_FIXEDCONTAINER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), FIXEDCONTAINER_TYPE)) +#define FIXEDCONTAINER_CLASS(class) (G_TYPE_CHECK_CLASS_CAST((class), FIXEDCONTAINER_TYPE, fixedContainerClass)) +#define IS_FIXEDCONTAINER_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE((class), FIXEDCONTAINER_TYPE)) +#define FIXEDCONTAINER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FIXEDCONTAINER_TYPE, fixedContainerClass)) + +typedef struct fixedContainer fixedContainer; +typedef struct fixedContainerClass fixedContainerClass; +typedef struct fixedContainerChild fixedContainerChild; + +struct fixedContainer { + GtkContainer parent_instance; + GList *children; +}; + +struct fixedContainerClass { + GtkContainerClass parent_class; +}; + +struct fixedContainerChild { + GtkWidget *widget; + gint x; + gint y; +}; + +G_DEFINE_TYPE(fixedContainer, fixedContainer, GTK_TYPE_CONTAINER) + +static fixedContainerChild* get_child (fixedContainer *fixed, GtkWidget *widget) +{ + GList *children; + + for (children = fixed->children; children; children = children->next) { + fixedContainerChild *child; + + child = children->data; + + if (child->widget == widget) + return child; + } + + return NULL; +} + +void fixedContainer_put(fixedContainer *fixed, GtkWidget *widget, gint x, gint y) +{ + g_return_if_fail (IS_FIXEDCONTAINER (fixed)); + g_return_if_fail (GTK_IS_WIDGET (widget)); + + fixedContainerChild *child_info = g_new(fixedContainerChild, 1); + child_info->widget = widget; + child_info->x = x; + child_info->y = y; + + gtk_widget_set_parent(widget, GTK_WIDGET(fixed)); + + fixed->children = g_list_append(fixed->children, child_info); +} + +void fixedContainer_move(fixedContainer *fixed, GtkWidget *widget, gint x, gint y) +{ + fixedContainerChild *child = get_child(fixed, widget); + g_return_if_fail (IS_FIXEDCONTAINER (fixed)); + g_return_if_fail (gtk_widget_get_parent (child->widget) == GTK_WIDGET (fixed)); + + child->x = x; + child->y = y; + + if (gtk_widget_get_visible (child->widget) && gtk_widget_get_visible (GTK_WIDGET (fixed))) + gtk_widget_queue_resize (GTK_WIDGET (fixed)); +} + +static void fixedContainer_init(fixedContainer *c) +{ + c->children = NULL; + gtk_widget_set_has_window(GTK_WIDGET(c), FALSE); +} + +static void fixedContainer_dispose(GObject *obj) +{ + g_list_free(FIXEDCONTAINER(obj)->children); + G_OBJECT_CLASS(fixedContainer_parent_class)->dispose(obj); +} + +static void fixedContainer_finalize(GObject *obj) +{ + G_OBJECT_CLASS(fixedContainer_parent_class)->finalize(obj); +} + +static void fixedContainer_add(GtkContainer *container, GtkWidget *widget) +{ + fixedContainer_put(FIXEDCONTAINER(container), widget, 0, 0); +} + +static void fixedContainer_remove(GtkContainer *container, GtkWidget *widget) +{ + fixedContainer *fixed = FIXEDCONTAINER (container); + fixedContainerChild *child; + GtkWidget *widget_container = GTK_WIDGET (container); + GList *children; + + for (children = fixed->children; children; children = children->next) { + child = children->data; + + if (child->widget == widget) { + gboolean was_visible = gtk_widget_get_visible (widget); + + gtk_widget_unparent (widget); + + fixed->children = g_list_remove_link (fixed->children, children); + g_list_free (children); + g_free (child); + + if (was_visible && gtk_widget_get_visible (widget_container)) + gtk_widget_queue_resize (widget_container); + + break; + } + } +} + +static void fixedContainer_size_allocate(GtkWidget *widget, GtkAllocation *allocation) +{ + fixedContainer *fixed = FIXEDCONTAINER (widget); + fixedContainerChild *child; + GtkAllocation child_allocation; + GtkRequisition child_requisition; + GList *children; + + for (children = fixed->children; children; children = children->next) { + child = children->data; + + if (!gtk_widget_get_visible (child->widget)) + continue; + + gtk_widget_get_preferred_size (child->widget, &child_requisition, NULL); + child_allocation.x = child->x; + child_allocation.y = child->y; + + child_allocation.width = child_requisition.width; + child_allocation.height = child_requisition.height; + gtk_widget_size_allocate (child->widget, &child_allocation); + } +} + +static void fixedContainer_forall(GtkContainer *container, gboolean includeInternals, GtkCallback callback, gpointer callback_data) +{ + fixedContainer *fixed = FIXEDCONTAINER (container); + fixedContainerChild *child; + GList *children; + + children = fixed->children; + while (children) { + child = children->data; + children = children->next; + + (* callback) (child->widget, callback_data); + } +} + +static void fixedContainer_class_init(fixedContainerClass *class) +{ + G_OBJECT_CLASS(class)->dispose = fixedContainer_dispose; + G_OBJECT_CLASS(class)->finalize = fixedContainer_finalize; + GTK_WIDGET_CLASS(class)->size_allocate = fixedContainer_size_allocate; + GTK_CONTAINER_CLASS(class)->add = fixedContainer_add; + GTK_CONTAINER_CLASS(class)->remove = fixedContainer_remove; + GTK_CONTAINER_CLASS(class)->forall = fixedContainer_forall; +} + +GtkWidget* fixedContainer_new(void) +{ + return g_object_new(FIXEDCONTAINER_TYPE, NULL); +} diff --git a/unix/image.c b/unix/image.c index 3b5db020c..37a84da67 100644 --- a/unix/image.c +++ b/unix/image.c @@ -25,6 +25,7 @@ uiImage *uiNewImage(double width, double height) i->width = width; i->height = height; i->images = g_ptr_array_new_with_free_func(freeImageRep); + return i; } diff --git a/unix/util.c b/unix/util.c index f3929ccbd..b9a289f59 100644 --- a/unix/util.c +++ b/unix/util.c @@ -7,4 +7,4 @@ void uiprivSetMargined(GtkContainer *c, int margined) gtk_container_set_border_width(c, uiprivGTKXMargin); else gtk_container_set_border_width(c, 0); -} +} \ No newline at end of file diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index 16beefa7a..21c362b5c 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -27,6 +27,7 @@ list(APPEND _LIBUI_SOURCES windows/editablecombo.cpp windows/entry.cpp windows/events.cpp + windows/fixed.cpp windows/fontbutton.cpp windows/fontdialog.cpp windows/fontmatch.cpp diff --git a/windows/fixed.cpp b/windows/fixed.cpp new file mode 100644 index 000000000..e9b7b1f85 --- /dev/null +++ b/windows/fixed.cpp @@ -0,0 +1,178 @@ +// 16 may 2015 +#include "uipriv_windows.hpp" +#include + +struct fixedChild { + uiControl *c; + int x; + int y; +}; + +struct uiFixed { + uiWindowsControl c; + HWND hwnd; + std::vector *controls; +}; + +static void fixedRelayout(uiFixed *g) +{ + if (g->controls->size() == 0) + return; + for (struct fixedChild &fc : *(g->controls)) { + if (!uiControlVisible(fc.c)) + continue; + uiWindowsMoveWindow((HWND) uiControlHandle(fc.c), fc.x, fc.y); + } +} + +static void fixedInitialLayout(uiFixed *g) +{ + if (g->controls->size() == 0) + return; + int minimumWidth, minimumHeight; + for (struct fixedChild &fc : *(g->controls)) { + if (!uiControlVisible(fc.c)) + continue; + uiWindowsControlMinimumSize(uiWindowsControl(fc.c), &minimumWidth, &minimumHeight); + uiWindowsEnsureMoveWindowDuringResize((HWND) uiControlHandle(fc.c), fc.x, fc.y, minimumWidth, minimumHeight); + } +} + +static void uiFixedDestroy(uiControl *c) +{ + uiFixed *g = uiFixed(c); + + for (const struct fixedChild &fc : *(g->controls)) { + uiControlSetParent(fc.c, NULL); + uiControlDestroy(fc.c); + } + delete g->controls; + uiWindowsEnsureDestroyWindow(g->hwnd); + uiFreeControl(uiControl(g)); +} + +uiWindowsControlDefaultHandle(uiFixed) +uiWindowsControlDefaultParent(uiFixed) +uiWindowsControlDefaultSetParent(uiFixed) +uiWindowsControlDefaultToplevel(uiFixed) +uiWindowsControlDefaultVisible(uiFixed) +uiWindowsControlDefaultShow(uiFixed) +uiWindowsControlDefaultHide(uiFixed) +uiWindowsControlDefaultEnabled(uiFixed) +uiWindowsControlDefaultEnable(uiFixed) +uiWindowsControlDefaultDisable(uiFixed) + +static void uiFixedSyncEnableState(uiWindowsControl *c, int enabled) +{ + uiFixed *g = uiFixed(c); + + if (uiWindowsShouldStopSyncEnableState(uiWindowsControl(g), enabled)) + return; + for (const struct fixedChild &fc : *(g->controls)) + uiWindowsControlSyncEnableState(uiWindowsControl(fc.c), enabled); +} + +uiWindowsControlDefaultSetParentHWND(uiFixed) + +static void uiFixedMinimumSize(uiWindowsControl *c, int *width, int *height) +{ + uiFixed *g = uiFixed(c); + + *width = 0; + *height = 0; + + for (const struct fixedChild &fc : *(g->controls)) { + if (!uiControlVisible(fc.c)) + continue; + if (fc.x > *width) + *width = fc.x; + if (fc.y > *height) + *height = fc.y; + } +} + +static void uiFixedMinimumSizeChanged(uiWindowsControl *c) +{ + uiFixed *g = uiFixed(c); + + if (uiWindowsControlTooSmall(uiWindowsControl(g))) { + uiWindowsControlContinueMinimumSizeChanged(uiWindowsControl(g)); + return; + } + + fixedRelayout(g); +} + +uiWindowsControlDefaultLayoutRect(uiFixed) +uiWindowsControlDefaultAssignControlIDZOrder(uiFixed) + +static void uiFixedChildVisibilityChanged(uiWindowsControl *c) +{ + // TODO eliminate the redundancy + uiWindowsControlMinimumSizeChanged(c); +} + +static void fixedArrangeChildren(uiFixed *g) +{ + LONG_PTR controlID; + HWND insertAfter; + + controlID = 100; + insertAfter = NULL; + for (const struct fixedChild &fc : *(g->controls)) + uiWindowsControlAssignControlIDZOrder(uiWindowsControl(fc.c), &controlID, &insertAfter); +} + +void uiFixedSize(uiFixed *g, uiControl *control, int *width, int *height) { + RECT r; + uiWindowsEnsureGetWindowRect((HWND) uiControlHandle(control), &r); + *width = r.right-r.left; + *height = r.bottom-r.top; +} + +void uiFixedSetSize(uiFixed *g, uiControl *control, int width, int height) { + uiWindowsResizeWindow((HWND) uiControlHandle(control), width, height); +} + +void uiFixedAppend(uiFixed *g, uiControl *child, int x, int y) +{ + struct fixedChild fc; + + fc.c = child; + uiControlSetParent(fc.c, uiControl(g)); + uiWindowsControlSetParentHWND(uiWindowsControl(fc.c), g->hwnd); + fc.x = x; + fc.y = y; + g->controls->push_back(fc); + fixedArrangeChildren(g); + fixedInitialLayout(g); +} + +void uiFixedMove(uiFixed *g, uiControl *child, int x, int y) +{ + for (struct fixedChild &fc : *(g->controls)) { + if (fc.c == child) { + fc.x = x; + fc.y = y; + uiWindowsControlMinimumSizeChanged(uiWindowsControl(g)); + return; + } + } +} + +static void onResize(uiWindowsControl *c) +{ + +} + +uiFixed *uiNewFixed(void) +{ + uiFixed *g; + + uiWindowsNewControl(uiFixed, g); + + g->hwnd = uiWindowsMakeContainer(uiWindowsControl(g), onResize); + g->controls = new std::vector; + + return g; +} diff --git a/windows/winpublic.cpp b/windows/winpublic.cpp index 397a3b54c..47c15ce83 100644 --- a/windows/winpublic.cpp +++ b/windows/winpublic.cpp @@ -35,6 +35,16 @@ void uiWindowsEnsureMoveWindowDuringResize(HWND hwnd, int x, int y, int width, i logLastError(L"error moving window"); } +void uiWindowsResizeWindow(HWND hwnd, int width, int height) { + if (SetWindowPos(hwnd, NULL, 0, 0, width, height, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOMOVE) == 0) + logLastError(L"error resizing window"); +} + +void uiWindowsMoveWindow(HWND hwnd, int x, int y) { + if (SetWindowPos(hwnd, NULL, x, y, 0, 0, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOSIZE) == 0) + logLastError(L"error moving window"); +} + // do these function even error out in any case other than invalid parameters?! I thought all windows had rects void uiWindowsEnsureGetClientRect(HWND hwnd, RECT *r) {