X-Git-Url: https://git.stderr.nl/gitweb?a=blobdiff_plain;f=libchimara%2Fwindow.c;h=84bc75de088fdf584ba45cddd64dcd67baa47906;hb=c13096143e7d543149ae9cb4a9237171804d2321;hp=5f2bd5b349673bb2b46a755e46c3da8906c93240;hpb=0b85f1dd5993e2ed111ec2ba13bbbb4ebda06ada;p=rodin%2Fchimara.git diff --git a/libchimara/window.c b/libchimara/window.c index 5f2bd5b..84bc75d 100644 --- a/libchimara/window.c +++ b/libchimara/window.c @@ -374,6 +374,8 @@ glk_window_open(winid_t split, glui32 method, glui32 size, glui32 wintype, glui32 rock) { VALID_WINDOW_OR_NULL(split, return NULL); + g_return_val_if_fail(method == (method & (winmethod_DirMask | winmethod_DivisionMask)), NULL); + g_return_val_if_fail(!(((method & winmethod_DivisionMask) == winmethod_Proportional) && size > 100), NULL); if(split == NULL && glk_data->root_window != NULL) { @@ -559,7 +561,10 @@ glk_window_open(winid_t split, glui32 method, glui32 size, glui32 wintype, glk_data->root_window = win->window_node; } - /* Set the window as a child of the Glk widget */ + /* Set the window as a child of the Glk widget, don't trigger an arrange event */ + g_mutex_lock(glk_data->arrange_lock); + glk_data->ignore_next_arrange_event = TRUE; + g_mutex_unlock(glk_data->arrange_lock); gtk_widget_set_parent(win->frame, GTK_WIDGET(glk_data->self)); gtk_widget_queue_resize(GTK_WIDGET(glk_data->self)); @@ -601,23 +606,19 @@ remove_key_windows(GNode *node, winid_t closing_win) } /* Internal function: destroy this window's GTK widgets, window streams, - and those of all its children */ + and those of all its children. GDK threads must be locked. */ static void destroy_windows_below(winid_t win, stream_result_t *result) { switch(win->type) { case wintype_Blank: - gdk_threads_enter(); gtk_widget_unparent(win->widget); - gdk_threads_leave(); break; case wintype_TextGrid: case wintype_TextBuffer: - gdk_threads_enter(); gtk_widget_unparent(win->frame); - gdk_threads_leave(); /* TODO: Cancel all input requests */ break; @@ -712,6 +713,8 @@ glk_window_close(winid_t win, stream_result_t *result) { VALID_WINDOW(win, return); + gdk_threads_enter(); /* Prevent redraw while we're trashing the window */ + /* If any pair windows have this window or its children as a key window, set their key window to NULL */ g_node_traverse(glk_data->root_window, G_IN_ORDER, G_TRAVERSE_NON_LEAVES, -1, (GNodeTraverseFunc)remove_key_windows, win); @@ -768,9 +771,10 @@ glk_window_close(winid_t win, stream_result_t *result) g_free(win); /* Schedule a redraw */ - gdk_threads_enter(); + g_mutex_lock(glk_data->arrange_lock); + glk_data->ignore_next_arrange_event = TRUE; + g_mutex_unlock(glk_data->arrange_lock); gtk_widget_queue_resize( GTK_WIDGET(glk_data->self) ); - gdk_window_process_all_updates(); gdk_threads_leave(); } @@ -1041,7 +1045,122 @@ glk_window_get_size(winid_t win, glui32 *widthptr, glui32 *heightptr) ILLEGAL_PARAM("Unknown window type: %u", win->type); } } - + +/** + * glk_window_set_arrangement: + * @win: a pair window to rearrange. + * @method: new method of size computation. One of #winmethod_Above, + * #winmethod_Below, #winmethod_Left, or #winmethod_Right OR'ed with + * #winmethod_Fixed or #winmethod_Proportional. + * @size: new size constraint, in percentage points if @method is + * #winmethod_Proportional, otherwise in characters if @win's type is + * #wintype_TextBuffer or #wintype_TextGrid, or pixels if @win's type is + * #wintype_Graphics. + * @keywin: new key window, or %NULL to leave the key window unchanged. + * + * Changes the size of an existing split — that is, it changes the + * constraint of a given pair window. + * + * Consider the example above, where D has collapsed to zero height. Say D was a + * text buffer window. You could make a more useful layout by doing + * |[ + * #winid_t o2; + * o2 = #glk_window_get_parent(d); + * glk_window_set_arrangement(o2, #winmethod_Above | #winmethod_Fixed, 3, d); + * ]| + * That would set D (the upper child of O2) to be O2's key window, and give it a + * fixed size of 3 rows. + * + * If you later wanted to expand D, you could do + * |[ glk_window_set_arrangement(o2, #winmethod_Above | #winmethod_Fixed, 5, NULL); ]| + * That expands D to five rows. Note that, since O2's key window is already set + * to D, it is not necessary to provide the @keywin argument; you can pass %NULL + * to mean leave the key window unchanged. + * + * If you do change the key window of a pair window, the new key window + * must be a descendant of that pair window. In the current + * example, you could change O2's key window to be A, but not B. The key window + * also cannot be a pair window itself. + * + * |[ glk_window_set_arrangement(o2, #winmethod_Below | #winmethod_Fixed, 3, NULL); ]| + * This changes the constraint to be on the lower child of + * O2, which is A. The key window is still D; so A would then be three rows high + * as measured in D's font, and D would get the rest of O2's space. That may not + * be what you want. To set A to be three rows high as measured in A's font, you + * would do + * |[ glk_window_set_arrangement(o2, #winmethod_Below | #winmethod_Fixed, 3, a); ]| + * + * Or you could change O2 to a proportional split: + * |[ glk_window_set_arrangement(o2, #winmethod_Below | #winmethod_Proportional, 30, NULL); ]| + * or + * |[ glk_window_set_arrangement(o2, #winmethod_Above | #winmethod_Proportional, 70, NULL); ]| + * These do exactly the same thing, since 30% above is the same as + * 70% below. You don't need to specify a key window with a proportional + * split, so the @keywin argument is %NULL. (You could actually specify either A + * or D as the key window, but it wouldn't affect the result.) + * + * Whatever constraint you set, glk_window_get_size() will tell you the actual + * window size you got. + * + * Note that you can resize windows, but you can't flip or rotate them. You + * can't move A above D, or change O2 to a vertical split where A is left or + * right of D. + * + * To get this effect you could close one of the windows, and re-split the + * other one with glk_window_open(). + * + */ +void +glk_window_set_arrangement(winid_t win, glui32 method, glui32 size, winid_t keywin) +{ + VALID_WINDOW(win, return); + VALID_WINDOW_OR_NULL(keywin, return); + g_return_if_fail(win->type == wintype_Pair); + if(keywin) + { + g_return_if_fail(keywin->type != wintype_Pair); + g_return_if_fail(g_node_is_ancestor(win->window_node, keywin->window_node)); + } + g_return_if_fail(method == (method & (winmethod_DirMask | winmethod_DivisionMask))); + g_return_if_fail(!(((method & winmethod_DivisionMask) == winmethod_Proportional) && size > 100)); + + win->split_method = method; + win->constraint_size = size; + if(keywin) + win->key_window = keywin; + + /* Tell GTK to rearrange the windows */ + gdk_threads_enter(); + g_mutex_lock(glk_data->arrange_lock); + glk_data->ignore_next_arrange_event = TRUE; + g_mutex_unlock(glk_data->arrange_lock); + gtk_widget_queue_resize(GTK_WIDGET(glk_data->self)); + gdk_threads_leave(); +} + +/** + * glk_window_get_arrangement: + * @win: a pair window. + * @methodptr: return location for the constraint flags of @win, or %NULL. + * @sizeptr: return location for the constraint size of @win, or %NULL. + * @keywinptr: return location for the key window of @win, or %NULL. + * + * Queries the constraint of a given pair window. + */ +void +glk_window_get_arrangement(winid_t win, glui32 *methodptr, glui32 *sizeptr, winid_t *keywinptr) +{ + VALID_WINDOW(win, return); + g_return_if_fail(win->type == wintype_Pair); + + if(methodptr) + *methodptr = win->split_method; + if(sizeptr) + *sizeptr = win->constraint_size; + if(keywinptr) + *keywinptr = win->key_window; +} + /** * glk_window_move_cursor: * @win: A text grid window. @@ -1078,6 +1197,10 @@ glk_window_move_cursor(winid_t win, glui32 xpos, glui32 ypos) VALID_WINDOW(win, return); g_return_if_fail(win->type == wintype_TextGrid); + /* Don't do anything if the window is shrunk down to nothing */ + if(win->width == 0 || win->height == 0) + return; + /* Calculate actual position if cursor is moved past the right edge */ if(xpos >= win->width) {