From a2001d1b9ced299d05a643a032380529a1df2e60 Mon Sep 17 00:00:00 2001 From: Dave Davenport Date: Wed, 28 Dec 2016 19:42:14 +0100 Subject: [PATCH] Add window widget, play with logic for sizing (broken) --- Makefile.am | 2 + include/view-internal.h | 2 + include/widgets/widget-internal.h | 9 ++ include/widgets/window.h | 37 +++++++ lexer/theme-lexer.l | 2 +- source/view.c | 75 +++++++------- source/widgets/box.c | 9 +- source/widgets/listview.c | 6 +- source/widgets/widget.c | 58 +++++++++++ source/widgets/window.c | 160 ++++++++++++++++++++++++++++++ 10 files changed, 316 insertions(+), 44 deletions(-) create mode 100644 include/widgets/window.h create mode 100644 source/widgets/window.c diff --git a/Makefile.am b/Makefile.am index 5b9389ae..e4fd6ecc 100644 --- a/Makefile.am +++ b/Makefile.am @@ -31,6 +31,7 @@ rofi_SOURCES=\ source/history.c\ source/theme.c\ source/widgets/box.c\ + source/widgets/window.c\ source/widgets/widget.c\ source/widgets/textbox.c\ source/widgets/listview.c\ @@ -62,6 +63,7 @@ rofi_SOURCES=\ include/history.h\ include/theme.h\ include/widgets/box.h\ + include/widgets/window.h\ include/widgets/widget.h\ include/widgets/widget-internal.h\ include/widgets/textbox.h\ diff --git a/include/view-internal.h b/include/view-internal.h index 14f8bb06..9438c22c 100644 --- a/include/view-internal.h +++ b/include/view-internal.h @@ -1,5 +1,6 @@ #ifndef ROFI_VIEW_INTERNAL_H #define ROFI_VIEW_INTERNAL_H +#include "widgets/window.h" #include "widgets/widget.h" #include "widgets/textbox.h" #include "widgets/separator.h" @@ -24,6 +25,7 @@ struct RofiViewState /** Flag indicating if view needs to be refiltered. */ int refilter; + window *main_window; /** Main #box widget holding different elements. */ box *main_box; /** #box widget packing the input bar widgets. */ diff --git a/include/widgets/widget-internal.h b/include/widgets/widget-internal.h index 940738fc..efb43f78 100644 --- a/include/widgets/widget-internal.h +++ b/include/widgets/widget-internal.h @@ -57,4 +57,13 @@ struct _widget void widget_init ( widget *widget , const char *name, const char *class_name ); void widget_set_state ( widget *widget, const char *state ); + +int widget_padding_get_left ( const widget *wid ); +int widget_padding_get_right ( const widget *wid ); +int widget_padding_get_top ( const widget *wid ); +int widget_padding_get_bottom ( const widget *wid ); +int widget_padding_get_remaining_width ( const widget *wid ); +int widget_padding_get_remaining_height ( const widget *wid ); +int widget_padding_get_padding_height ( const widget *wid ); +int widget_padding_get_padding_width ( const widget *wid ); #endif // WIDGET_INTERNAL_H diff --git a/include/widgets/window.h b/include/widgets/window.h new file mode 100644 index 00000000..6d8a90a1 --- /dev/null +++ b/include/widgets/window.h @@ -0,0 +1,37 @@ +#ifndef ROFI_WINDOW_H +#define ROFI_WINDOW_H + +#include "widget.h" + +/** + * @defgroup window window + * @ingroup widget + * + * + * @{ + */ + +/** + * Abstract handle to the window widget internal state. + */ +typedef struct _window window; + +/** + * @param name The name of the widget. + * @param type The packing direction of the newly created window. + * + * @returns a newly created window, free with #widget_free + */ +window * window_create ( const char *name ); + +/** + * @param window Handle to the window widget. + * @param child Handle to the child widget to pack. + * + * Add a widget to the window. + */ +void window_add ( window *window, widget *child ); + +int window_get_border_width ( const window *window ); +/*@}*/ +#endif // ROFI_WINDOW_H diff --git a/lexer/theme-lexer.l b/lexer/theme-lexer.l index 0ae8315f..01732733 100644 --- a/lexer/theme-lexer.l +++ b/lexer/theme-lexer.l @@ -23,7 +23,7 @@ WHITESPACE [[:space:]] WORD [[:alnum:]-]+ STRING [[:print:]]+ HEX [[:xdigit:]] -NUMBER [[:digit:]] +NUMBER [[:digit:]-] %x PROPERTIES %x NAMESTR diff --git a/source/view.c b/source/view.c index 6238b470..bd8c4f2e 100644 --- a/source/view.c +++ b/source/view.c @@ -298,9 +298,7 @@ static void rofi_view_window_update_size ( RofiViewState * state ) CacheState.edit_draw = cairo_create ( CacheState.edit_surf ); // Should wrap main window in a widget. - int width = state->width - 2*state->border - state->pad.left - state->pad.right; - int height = state->height - 2*state->border - state->pad.top- state->pad.bottom; - widget_resize ( WIDGET ( state->main_box ), width, height ); + widget_resize ( WIDGET ( state->main_window ), state->width, state->height ); } static gboolean rofi_view_reload_idle ( G_GNUC_UNUSED gpointer data ) @@ -388,7 +386,7 @@ void rofi_view_free ( RofiViewState *state ) } // Do this here? // Wait for final release? - widget_free ( WIDGET ( state->main_box ) ); + widget_free ( WIDGET ( state->main_window ) ); widget_free ( WIDGET ( state->overlay ) ); g_free ( state->line_map ); @@ -775,7 +773,7 @@ static void update_callback ( textbox *t, unsigned int index, void *udata, TextB void rofi_view_update ( RofiViewState *state ) { - if ( !widget_need_redraw ( WIDGET ( state->main_box ) ) && !widget_need_redraw ( WIDGET ( state->overlay ) ) ) { + if ( !widget_need_redraw ( WIDGET ( state->main_window ) ) && !widget_need_redraw ( WIDGET ( state->overlay ) ) ) { return; } TICK (); @@ -792,36 +790,17 @@ void rofi_view_update ( RofiViewState *state ) } cairo_paint ( d ); cairo_set_operator ( d, CAIRO_OPERATOR_OVER ); - color_background ( d ); - rofi_theme_get_color ( "@window" , "window" , NULL, "background", d ); - cairo_paint ( d ); } else { - // Paint the background. - color_background ( d ); - rofi_theme_get_color ( "@window", "window" , NULL, "background", d ); + // Paint the background transparent. + cairo_set_source_rgba ( d, 0,0,0,0.0); cairo_paint ( d ); } TICK_N ( "Background" ); - color_border ( d ); - rofi_theme_get_color ( "@window", "window" , NULL, "foreground", d ); - - int bw = rofi_theme_get_integer ( "@window", "window", NULL, "border-width" , config.menu_bw); - if ( bw > 0 ) { - cairo_save ( d ); - cairo_set_line_width ( d, bw ); - cairo_rectangle ( d, - bw / 2.0, - bw / 2.0, - state->width - bw, - state->height - bw ); - cairo_stroke ( d ); - cairo_restore ( d ); - } // Always paint as overlay over the background. cairo_set_operator ( d, CAIRO_OPERATOR_OVER ); - widget_draw ( WIDGET ( state->main_box ), d ); + widget_draw ( WIDGET ( state->main_window ), d ); if ( state->overlay ) { widget_draw ( WIDGET ( state->overlay ), d ); @@ -886,7 +865,7 @@ static void rofi_view_mouse_navigation ( RofiViewState *state, xcb_button_press_ xcb_button_press_event_t rel = *xbe; rel.event_x -= state->pad.left; rel.event_y -= state->pad.top; - if ( widget_clicked ( WIDGET ( state->main_box ), &rel ) ) { + if ( widget_clicked ( WIDGET ( state->main_window ), &rel ) ) { return; } } @@ -1278,9 +1257,7 @@ void rofi_view_itterrate ( RofiViewState *state, xcb_generic_event_t *ev, xkb_st CacheState.edit_surf = cairo_xcb_surface_create ( xcb->connection, CacheState.edit_pixmap, visual, state->width, state->height ); CacheState.edit_draw = cairo_create ( CacheState.edit_surf ); - int width = state->width - 2*state->border - state->pad.left - state->pad.right; - int height = state->height - 2*state->border - state->pad.top- state->pad.bottom; - widget_resize ( WIDGET ( state->main_box ), width, height ); + widget_resize ( WIDGET ( state->main_window ), state->width, state->height ); } } break; @@ -1303,7 +1280,7 @@ void rofi_view_itterrate ( RofiViewState *state, xcb_generic_event_t *ev, xkb_st xcb_motion_notify_event_t xme = *( (xcb_motion_notify_event_t *) ev ); xme.event_x -= state->pad.left; xme.event_y -= state->pad.top; - if ( widget_motion_notify ( WIDGET ( state->main_box ), &xme ) ) { + if ( widget_motion_notify ( WIDGET ( state->main_window ), &xme ) ) { return; } break; @@ -1377,13 +1354,33 @@ static int rofi_view_calculate_height ( RofiViewState *state ) } if ( state->filtered_lines == 0 && !config.fixed_num_lines ) { widget_disable ( WIDGET ( state->input_bar_separator ) ); + widget_disable ( WIDGET ( state->list_view) ); } else { widget_enable ( WIDGET ( state->input_bar_separator ) ); + widget_enable ( WIDGET ( state->list_view) ); } height = listview_get_desired_height ( state->list_view ); + // Why not a factor 2 here? + height += window_get_border_width ( state->main_window ); height += box_get_fixed_pixels ( state->main_box ); - height += 2 * state->border +state->pad.top+state->pad.bottom; + // How to merge this.... + int perc =0; + widget *main_window = WIDGET ( state->main_window ); + if ( main_window->pad.top >= 0 ){ + height += main_window->pad.top; + } else { + perc -= main_window->pad.top; + } + if ( main_window->pad.bottom >= 0 ){ + height += main_window->pad.bottom; + } else { + perc -= main_window->pad.bottom; + } + if ( perc > 0){ + height = (100*height)/(100-perc); + } + printf("listview: %d\n", listview_get_desired_height ( state->list_view )); return height; } @@ -1438,8 +1435,6 @@ RofiViewState *rofi_view_create ( Mode *sw, state->pad = (Padding){config.padding,config.padding, config.padding, config.padding, FALSE}; state->pad = rofi_theme_get_padding ( "@window", "window", NULL, "padding", state->pad); - //state->border = rofi_theme_get_integer ("@window", "window", NULL, "padding" , config.padding ); - state->border += rofi_theme_get_integer ("@window", "window", NULL, "border-width" , config.menu_bw); // Request the lines to show. state->num_lines = mode_get_num_entries ( sw ); @@ -1449,8 +1444,9 @@ RofiViewState *rofi_view_create ( Mode *sw, // Get active monitor size. TICK_N ( "Get active monitor" ); + state->main_window = window_create ( "window" ); state->main_box = box_create ( "mainbox.box", BOX_VERTICAL ); - widget_move ( WIDGET ( state->main_box ), state->border+state->pad.left, state->border+state->pad.top ); + window_add ( state->main_window, WIDGET ( state->main_box ) ); // we need this at this point so we can get height. rofi_view_calculate_window_and_element_width ( state ); @@ -1541,7 +1537,7 @@ RofiViewState *rofi_view_create ( Mode *sw, rofi_view_update ( state ); xcb_map_window ( xcb->connection, CacheState.main_window ); - widget_queue_redraw ( WIDGET ( state->main_box ) ); + widget_queue_redraw ( WIDGET ( state->main_window ) ); xcb_flush ( xcb->connection ); if ( xcb->sncontext != NULL ) { sn_launchee_context_complete ( xcb->sncontext ); @@ -1557,11 +1553,12 @@ int rofi_view_error_dialog ( const char *msg, int markup ) state->finalize = process_result; state->pad = (Padding){config.padding,config.padding, config.padding, config.padding, FALSE}; state->pad = rofi_theme_get_padding ( "@window", "window", NULL, "padding", state->pad); - // state->border = rofi_theme_get_integer ( "@window", "window", NULL, "padding" , config.padding ); state->border += rofi_theme_get_integer ( "@window", "window", NULL, "border-width" , config.menu_bw); rofi_view_calculate_window_and_element_width ( state ); + state->main_window = window_create ( "window" ); state->main_box = box_create ( "mainbox.box", BOX_VERTICAL); + window_add ( state->main_window, WIDGET ( state->main_box ) ); widget_move ( WIDGET ( state->main_box ), state->border+state->pad.left, state->border+state->pad.top ); state->text = textbox_create ( "message", ( TB_AUTOHEIGHT | TB_WRAP ) + ( ( markup ) ? TB_MARKUP : 0 ), NORMAL, ( msg != NULL ) ? msg : "" ); @@ -1579,7 +1576,7 @@ int rofi_view_error_dialog ( const char *msg, int markup ) // Display it. xcb_map_window ( xcb->connection, CacheState.main_window ); - widget_queue_redraw ( WIDGET ( state->main_box ) ); + widget_queue_redraw ( WIDGET ( state->main_window ) ); if ( xcb->sncontext != NULL ) { sn_launchee_context_complete ( xcb->sncontext ); diff --git a/source/widgets/box.c b/source/widgets/box.c index e1b8e92a..742926a3 100644 --- a/source/widgets/box.c +++ b/source/widgets/box.c @@ -75,7 +75,10 @@ static void vert_calculate_size ( box *b ) } int rem_width = b->widget.w - b->widget.pad.left-b->widget.pad.right; int rem_height = b->widget.h - b->widget.pad.top-b->widget.pad.bottom; - b->max_size += MAX ( 0, ( ( active_widgets - 1 ) * b->spacing ) ); + if ( active_widgets > 0 ){ + b->max_size += ( active_widgets - 1 ) * b->spacing; + } + printf("%d %d\n", rem_height, b->max_size); if ( b->max_size > rem_height ) { g_log ( LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Widgets to large (height) for box: %d %d", b->max_size, b->widget.h ); return; @@ -107,6 +110,7 @@ static void vert_calculate_size ( box *b ) } rem -= expanding_widgets_size; index++; + b->max_size += widget_padding_get_padding_height ( child); } else if ( child->end ) { bottom -= widget_get_height ( child ); @@ -122,6 +126,7 @@ static void vert_calculate_size ( box *b ) } } } + b->max_size += b->widget.pad.top+b->widget.pad.bottom; } static void hori_calculate_size ( box *b ) { @@ -175,6 +180,7 @@ static void hori_calculate_size ( box *b ) } rem -= expanding_widgets_size; index++; + b->max_size += widget_padding_get_padding_width ( child); } else if ( child->end ) { right -= widget_get_width ( child ); @@ -190,6 +196,7 @@ static void hori_calculate_size ( box *b ) } } } + b->max_size += b->widget.pad.left+b->widget.pad.right; } static void box_draw ( widget *wid, cairo_t *draw ) diff --git a/source/widgets/listview.c b/source/widgets/listview.c index 0e2b24b3..cf245145 100644 --- a/source/widgets/listview.c +++ b/source/widgets/listview.c @@ -248,7 +248,7 @@ static void listview_resize ( widget *wid, short w, short h ) listview *lv = (listview *) wid; lv->widget.w = MAX ( 0, w ); lv->widget.h = MAX ( 0, h ); - int height = lv->widget.h - lv->widget.pad.top-lv->widget.pad.bottom; + int height = lv->widget.h - lv->widget.pad.top-lv->widget.pad.bottom; lv->max_rows = ( lv->spacing + height ) / ( lv->element_height + lv->spacing ); lv->max_elements = lv->max_rows * lv->menu_columns; @@ -451,7 +451,7 @@ void listview_nav_page_next ( listview *lv ) unsigned int listview_get_desired_height ( listview *lv ) { - if ( lv == NULL ) { + if ( lv == NULL || lv->widget.enabled == FALSE ) { return 0; } int h = lv->menu_lines; @@ -459,7 +459,7 @@ unsigned int listview_get_desired_height ( listview *lv ) h = MIN ( lv->menu_lines, lv->req_elements ); } if ( h == 0 ) { - return 0; + return lv->widget.pad.top+lv->widget.pad.bottom; } return h * lv->element_height + ( h - 1 ) * lv->spacing+lv->widget.pad.top+lv->widget.pad.bottom; } diff --git a/source/widgets/widget.c b/source/widgets/widget.c index ae6ef06e..66b937ab 100644 --- a/source/widgets/widget.c +++ b/source/widgets/widget.c @@ -207,3 +207,61 @@ void widget_set_name ( widget *wid, const char *name ) } wid->name = g_strdup ( name ); } + +int widget_padding_get_left ( const widget *wid ) +{ + if ( wid->pad.left < 0 ){ + return (wid->w*-wid->pad.left)/100; + } + return wid->pad.left; +} +int widget_padding_get_right ( const widget *wid ) +{ + if ( wid->pad.right < 0 ){ + return (wid->w*-wid->pad.right)/100; + } + return wid->pad.right; +} +int widget_padding_get_top ( const widget *wid ) +{ + if ( wid->pad.top < 0 ){ + return (wid->h*-wid->pad.top)/100; + } + return wid->pad.top; +} +int widget_padding_get_bottom ( const widget *wid ) +{ + if ( wid->pad.bottom < 0 ){ + return (wid->h*-wid->pad.bottom)/100; + } + return wid->pad.bottom; +} + +int widget_padding_get_remaining_width ( const widget *wid ) +{ + int width = wid->w; + width -= widget_padding_get_left ( wid ); + width -= widget_padding_get_right ( wid ); + return width; +} +int widget_padding_get_remaining_height ( const widget *wid ) +{ + int height = wid->h; + height -= widget_padding_get_top ( wid ); + height -= widget_padding_get_bottom ( wid ); + return height; +} +int widget_padding_get_padding_height ( const widget *wid ) +{ + int height = 0; + height += widget_padding_get_top ( wid ); + height += widget_padding_get_bottom ( wid ); + return height; +} +int widget_padding_get_padding_width ( const widget *wid ) +{ + int width = 0; + width += widget_padding_get_left ( wid ); + width += widget_padding_get_right ( wid ); + return width; +} diff --git a/source/widgets/window.c b/source/widgets/window.c new file mode 100644 index 00000000..3373d4f7 --- /dev/null +++ b/source/widgets/window.c @@ -0,0 +1,160 @@ +/** + * rofi + * + * MIT/X11 License + * Modified 2016 Qball Cow + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include "widgets/widget.h" +#include "widgets/widget-internal.h" +#include "widgets/window.h" +#include "theme.h" +#include "settings.h" + +#define LOG_DOMAIN "Widgets.Window" +const char *WINDOW_CLASS_NAME = "@window"; + +/** + * @param window Handle to the window widget. + * @param spacing The spacing to apply. + * + * Set the spacing to apply between the children in pixels. + */ +void window_set_spacing ( window * window, unsigned int spacing ); + +struct _window +{ + widget widget; + widget *child; + int border_width; +}; + +static void window_update ( widget *wid ); + + +static void window_draw ( widget *wid, cairo_t *draw ) +{ + window *b = (window *) wid; + + cairo_save ( draw ); + rofi_theme_get_color ( "@window", "window" , NULL, "foreground", draw ); + cairo_set_line_width ( draw, b->border_width ); + cairo_rectangle ( draw, + b->border_width / 2.0, + b->border_width / 2.0, + wid->w - b->border_width, + wid->h - b->border_width ); + cairo_stroke ( draw ); + cairo_restore ( draw ); + widget_draw ( b->child, draw ); +} + +static void window_free ( widget *wid ) +{ + window *b = (window *) wid; + + widget_free ( b->child ); + g_free ( b ); +} + +void window_add ( window *window, widget *child ) +{ + if ( window == NULL ) { + return; + } + window->child = child; + child->parent = WIDGET ( window ); + widget_update ( WIDGET ( window ) ); +} + +static void window_resize ( widget *widget, short w, short h ) +{ + window *b = (window *) widget; + if ( b->widget.w != w || b->widget.h != h ) { + b->widget.w = w; + b->widget.h = h; + widget_update ( widget ); + } +} + +static gboolean window_clicked ( widget *wid, xcb_button_press_event_t *xbe, G_GNUC_UNUSED void *udata ) +{ + window *b = (window *) wid; + if ( widget_intersect ( b->child, xbe->event_x, xbe->event_y ) ) { + xcb_button_press_event_t rel = *xbe; + rel.event_x -= b->child->x; + rel.event_y -= b->child->y; + return widget_clicked ( b->child, &rel ); + } + return FALSE; +} +static gboolean window_motion_notify ( widget *wid, xcb_motion_notify_event_t *xme ) +{ + window *b = (window *) wid; + if ( widget_intersect ( b->child, xme->event_x, xme->event_y ) ) { + xcb_motion_notify_event_t rel = *xme; + rel.event_x -= b->child->x; + rel.event_y -= b->child->y; + return widget_motion_notify ( b->child, &rel ); + } + return FALSE; +} + +window * window_create ( const char *name ) +{ + window *b = g_malloc0 ( sizeof ( window ) ); + // Initialize widget. + widget_init ( WIDGET(b), name, WINDOW_CLASS_NAME); + b->widget.draw = window_draw; + b->widget.free = window_free; + b->widget.resize = window_resize; + b->widget.update = window_update; + b->widget.clicked = window_clicked; + b->widget.motion_notify = window_motion_notify; + b->widget.enabled = TRUE; + b->border_width = rofi_theme_get_integer ( + b->widget.class_name, b->widget.name, NULL, "border-width" , config.menu_bw); + + return b; +} + +static void window_update ( widget *wid ) +{ + window *b = (window *) wid; + if ( b->child && b->child->enabled ){ + widget_resize ( WIDGET ( b->child ), + widget_padding_get_remaining_width (WIDGET(b))-2*b->border_width, + widget_padding_get_remaining_height (WIDGET(b))-2*b->border_width + ); + widget_move ( WIDGET ( b->child ), + b->border_width+widget_padding_get_left (WIDGET(b)), + b->border_width+widget_padding_get_top (WIDGET(b)) + ); + } +} + +int window_get_border_width ( const window *window ) +{ + return window->border_width*2; +}