From 3c64507f02b36a541c9893dd21b0a45e19a8ba9e Mon Sep 17 00:00:00 2001 From: Dave Davenport Date: Sat, 8 Oct 2016 18:57:59 +0200 Subject: [PATCH] Qtk widgets (#478) * Initial support for using boxes for packing widgets. * Make CapiTalIzation more consistent * Move widgets to subdirectory * Removed nolonger needed resize function. * Small cleanup * When switching mode, reuse view, don't rebuild it. Reduces artifacts. * Needs cleaning up. * Remove separate prompt argument, use display_name * Simplify run-switcher code. * Add notion of parents, and trigger parent update on resize. * Use widget_get_height wrapper, avoid crash * Size separator width based on height. (indent) * TODO add margin to box. * First draft version of listview. * Add scrollbar to listview widget * Add padding option to box * Move access to global config out of widget. (1/2 done) * Remove printf * Make listview not access global config * Add click support, implement on mode buttons. * Re-introduce mouse handling on list-view. * TODO draggin on scrollbar. * Fix multi-select * Fix warnings * Fix include --- Makefile.am | 29 +- include/view-internal.h | 31 +- include/view.h | 5 +- include/widget.h | 73 --- include/widgets/box.h | 26 + include/widgets/listview.h | 90 +++ include/{ => widgets}/scrollbar.h | 13 +- include/widgets/separator.h | 24 + include/{ => widgets}/textbox.h | 34 +- include/widgets/widget.h | 115 ++++ source/dialogs/combi.c | 3 + source/dialogs/dmenu.c | 10 +- source/dialogs/drun.c | 2 +- source/dialogs/help-keys.c | 2 +- source/dialogs/ssh.c | 2 +- source/dialogs/window.c | 2 +- source/helper.c | 4 +- source/rofi.c | 89 ++- source/view.c | 879 +++++++++--------------------- source/widget.c | 58 -- source/widgets/box.c | 291 ++++++++++ source/widgets/listview.c | 488 +++++++++++++++++ source/{ => widgets}/scrollbar.c | 28 +- source/widgets/separator.c | 84 +++ source/{ => widgets}/textbox.c | 69 ++- source/widgets/widget.c | 142 +++++ test/textbox-test.c | 6 +- test/widget-test.c | 4 +- 28 files changed, 1675 insertions(+), 928 deletions(-) delete mode 100644 include/widget.h create mode 100644 include/widgets/box.h create mode 100644 include/widgets/listview.h rename include/{ => widgets}/scrollbar.h (86%) create mode 100644 include/widgets/separator.h rename include/{ => widgets}/textbox.h (90%) create mode 100644 include/widgets/widget.h delete mode 100644 source/widget.c create mode 100644 source/widgets/box.c create mode 100644 source/widgets/listview.c rename source/{ => widgets}/scrollbar.c (83%) create mode 100644 source/widgets/separator.c rename source/{ => widgets}/textbox.c (91%) create mode 100644 source/widgets/widget.c diff --git a/Makefile.am b/Makefile.am index bdd1a83c..a97eb4d9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -23,11 +23,14 @@ rofi_SOURCES=\ source/keyb.c\ config/config.c\ source/helper.c\ - source/widget.c\ - source/textbox.c\ source/timings.c\ source/history.c\ - source/scrollbar.c\ + source/widgets/box.c\ + source/widgets/widget.c\ + source/widgets/textbox.c\ + source/widgets/listview.c\ + source/widgets/scrollbar.c\ + source/widgets/separator.c\ source/xrmoptions.c\ source/x11-helper.c\ source/dialogs/run.c\ @@ -50,9 +53,12 @@ rofi_SOURCES=\ include/helper.h\ include/timings.h\ include/history.h\ - include/widget.h\ - include/textbox.h\ - include/scrollbar.h\ + include/widgets/box.h\ + include/widgets/widget.h\ + include/widgets/textbox.h\ + include/widgets/listview.h\ + include/widgets/scrollbar.h\ + include/widgets/separator.h\ include/xrmoptions.h\ include/x11-helper.h\ include/dialogs/ssh.h\ @@ -181,20 +187,19 @@ textbox_test_LDADD=\ widget_test_LDADD=$(textbox_test_LDADD) widget_test_CFLAGS=$(textbox_test_CFLAGS) widget_test_SOURCES=\ - source/widget.c\ + source/widgets/widget.c\ test/widget-test.c textbox_test_SOURCES=\ - source/widget.c\ - source/textbox.c\ + source/widgets/widget.c\ + source/widgets/textbox.c\ config/config.c\ include/keyb.h\ include/rofi.h\ include/mode.h\ include/mode-private.h\ include/settings.h\ - include/widget.h\ - include/textbox.h\ - include/widget.h\ + include/widgets/widget.h\ + include/widgets/textbox.h\ include/xrmoptions.h\ include/helper.h\ test/textbox-test.c diff --git a/include/view-internal.h b/include/view-internal.h index 0c693065..1a9e5a30 100644 --- a/include/view-internal.h +++ b/include/view-internal.h @@ -1,8 +1,10 @@ #ifndef ROFI_VIEW_INTERNAL_H #define ROFI_VIEW_INTERNAL_H -#include "widget.h" -#include "textbox.h" -#include "scrollbar.h" +#include "widgets/widget.h" +#include "widgets/textbox.h" +#include "widgets/separator.h" +#include "widgets/listview.h" +#include "widgets/box.h" #include "keyb.h" #include "x11-helper.h" @@ -16,27 +18,21 @@ struct RofiViewState { Mode *sw; - unsigned int menu_lines; - unsigned int max_elements; - unsigned int max_rows; - unsigned int columns; - - unsigned int element_width; - int top_offset; // Update/Refilter list. - int update; int refilter; int rchanged; - unsigned int cur_page; + box *main_box; // Entries + box *input_bar; + separator *input_bar_separator; + + textbox *prompt; textbox *text; - textbox *prompt_tb; - textbox *message_tb; textbox *case_indicator; - textbox **boxes; - scrollbar *scrollbar; + + listview *list_view; // Small overlay. textbox *overlay; int *distance; @@ -45,7 +41,6 @@ struct RofiViewState unsigned int num_lines; // Selected element. - unsigned int selected; unsigned int filtered_lines; // Last offset in paginating. unsigned int last_offset; @@ -58,11 +53,11 @@ struct RofiViewState // Return state unsigned int selected_line; MenuReturn retv; - int line_height; unsigned int border; workarea mon; // Sidebar view + box *sidebar_bar; unsigned int num_modi; textbox **modi; diff --git a/include/view.h b/include/view.h index fbcd374c..93b04d8e 100644 --- a/include/view.h +++ b/include/view.h @@ -40,9 +40,9 @@ typedef enum * * @returns The command issued (see MenuReturn) */ -RofiViewState *rofi_view_create ( Mode *sw, const char *input, char *prompt, const char *message, MenuFlags flags, void ( *finalize )( +RofiViewState *rofi_view_create ( Mode *sw, const char *input, const char *message, MenuFlags flags, void ( *finalize )( RofiViewState * ) ) -__attribute__ ( ( nonnull ( 1, 2, 3, 6 ) ) ); +__attribute__ ( ( nonnull ( 1, 2, 5 ) ) ); /** * @param state The Menu Handle @@ -162,5 +162,6 @@ void rofi_view_set_overlay ( RofiViewState *state, const char *text ); */ void rofi_view_hide ( void ); void rofi_view_reload ( void ); +void rofi_view_switch_mode ( RofiViewState *state, Mode *mode ); /**@}*/ #endif diff --git a/include/widget.h b/include/widget.h deleted file mode 100644 index 522288d5..00000000 --- a/include/widget.h +++ /dev/null @@ -1,73 +0,0 @@ -#ifndef ROFI_WIDGET_H -#define ROFI_WIDGET_H - -#include -/** - * @defgroup Widgets Widgets - * - * Generic Widget class - * - * @{ - */ -typedef struct _Widget -{ - /** X position relative to parent */ - short x; - /** Y position relative to parent */ - short y; - /** Width of the widget */ - short w; - /** Height of the widget */ - short h; - /** enabled or not */ - gboolean enabled; - /** Function prototypes */ - - void ( *draw )( struct _Widget *widget, cairo_t *draw ); - void ( *free )( struct _Widget *widget ); -} Widget; - -/** Macro to get widget from an implementation (e.g. textbox/scrollbar) */ -#define WIDGET( a ) ( a != NULL ? (Widget *) ( a ) : NULL ) - -/** - * @param widget The widget to check - * @param x The X position relative to parent window - * @param y the Y position relative to parent window - * - * Check if x,y falls within the widget. - * - * @return TRUE if x,y falls within the widget - */ -int widget_intersect ( const Widget *widget, int x, int y ); - -/** - * @param widget The widget to move - * @param x The new X position relative to parent window - * @param y The new Y position relative to parent window - * - * Moves the widget. - */ -void widget_move ( Widget *widget, short x, short y ); - -gboolean widget_enabled ( Widget *widget ); -void widget_disable ( Widget *widget ); -void widget_enable ( Widget *widget ); - -/** - * @param tb Handle to the widget - * @param draw The cairo object used to draw itself. - * - * Render the textbox. - */ -void widget_draw ( Widget *widget, cairo_t *d ); - -/** - * @param tb Handle to the widget - * - * Free the widget and all allocated memory. - */ -void widget_free ( Widget *widget ); - -/*@}*/ -#endif // ROFI_WIDGET_H diff --git a/include/widgets/box.h b/include/widgets/box.h new file mode 100644 index 00000000..e797e51e --- /dev/null +++ b/include/widgets/box.h @@ -0,0 +1,26 @@ +#ifndef ROFI_HBOX_H +#define ROFI_HBOX_H + +#include "widget.h" + +/** + * @defgroup box box + * @ingroup widgets + * + * @{ + */ +typedef struct _box box; +typedef enum +{ + BOX_HORIZONTAL, + BOX_VERTICAL +} boxType; + +box * box_create ( boxType type, short x, short y, short w, short h ); + +void box_add ( box *box, widget *child, gboolean expand, gboolean end ); + +int box_get_fixed_pixels ( box *box ); +void box_set_padding ( box * box, unsigned int padding ); +/*@}*/ +#endif // ROFI_HBOX_H diff --git a/include/widgets/listview.h b/include/widgets/listview.h new file mode 100644 index 00000000..65bb5237 --- /dev/null +++ b/include/widgets/listview.h @@ -0,0 +1,90 @@ +#ifndef ROFI_LISTVIEW_H +#define ROFI_LISTVIEW_H + +/** + * @defgroup listview listview + * @ingroup widgets + * + * @{ + */ + +typedef struct _listview listview; + +typedef enum +{ + LISTVIEW_SCROLL_PER_PAGE, + LISTVIEW_SCROLL_CONTINIOUS +} ScrollType; + +typedef void ( *listview_update_callback )( textbox *tb, unsigned int entry, void *udata, TextBoxFontType type, gboolean full ); +typedef void ( *listview_mouse_activated_cb )( listview *, xcb_button_press_event_t *, void * ); + +listview *listview_create ( listview_update_callback cb, void *udata, unsigned int eh ); + +void listview_set_num_elements ( listview *lv, unsigned int rows ); +void listview_set_selected ( listview *lv, unsigned int selected ); +unsigned int listview_get_selected ( listview *lv ); +unsigned int listview_get_desired_height ( listview *lv ); + +/** + * @param state The listview handle + * + * Move the selection one row up. + * - Wrap around. + */ +void listview_nav_up ( listview *lv ); +/** + * @param lv listview handle. + * + * Move the selection one row down. + * - Wrap around. + */ +void listview_nav_down ( listview *lv ); +/** + * @param state The listview handle + * + * Move the selection one column to the right. + * - No wrap around. + * - Do not move to top row when at start. + */ +void listview_nav_right ( listview *lv ); +/** + * @param state The listview handle + * + * Move the selection one column to the left. + * - No wrap around. + */ +void listview_nav_left ( listview *lv ); +/** + * @param state The listview handle + * + * Move the selection one page down. + * - No wrap around. + * - Clip at top/bottom + */ +void listview_nav_page_next ( listview *lv ); + +/** + * @param state The current RofiViewState + * + * Move the selection one page up. + * - No wrap around. + * - Clip at top/bottom + */ +void listview_nav_page_prev ( listview *lv ); +/** + * Configuration. + */ +void listview_set_padding ( listview *lv, unsigned int padding ); +void listview_set_max_lines ( listview *lv, unsigned int lines ); +void listview_set_max_columns ( listview *lv, unsigned int columns ); +void listview_set_fixed_num_lines ( listview *lv, gboolean enabled ); +void listview_set_hide_scrollbar ( listview *lv, gboolean enabled ); +void listview_set_scrollbar_width ( listview *lv, unsigned int width ); +void listview_set_cycle ( listview *lv, gboolean cycle ); +void listview_set_scroll_type ( listview *lv, ScrollType type ); +void listview_set_mouse_activated_cb ( listview *lv, listview_mouse_activated_cb cb, void *udata ); +void listview_set_multi_select ( listview *lv, gboolean enable ); +/* @} */ + +#endif // ROFI_LISTVIEW_H diff --git a/include/scrollbar.h b/include/widgets/scrollbar.h similarity index 86% rename from include/scrollbar.h rename to include/widgets/scrollbar.h index 7b3a1205..148be6a9 100644 --- a/include/scrollbar.h +++ b/include/widgets/scrollbar.h @@ -5,7 +5,7 @@ /** * @defgroup Scrollbar Scrollbar - * @ingroup Widgets + * @ingroup widgets * * @{ */ @@ -14,7 +14,7 @@ */ typedef struct _scrollbar { - Widget widget; + widget widget; unsigned int length; unsigned int pos; unsigned int pos_length; @@ -64,14 +64,5 @@ void scrollbar_set_max_value ( scrollbar *sb, unsigned int max ); */ unsigned int scrollbar_clicked ( const scrollbar *sb, int y ); -/** - * @param sb scrollbar object - * @param w new width in pixels - * @param h new height in pixels - * - * Resize the scrollbar. - */ -void scrollbar_resize ( scrollbar *sb, int w, int h ); - /*@}*/ #endif // ROFI_SCROLLBAR_H diff --git a/include/widgets/separator.h b/include/widgets/separator.h new file mode 100644 index 00000000..f1bf7044 --- /dev/null +++ b/include/widgets/separator.h @@ -0,0 +1,24 @@ +#ifndef ROFI_SEPARATOR_H +#define ROFI_SEPARATOR_H +#include +#include "widget.h" + +/** + * @defgroup separator separator + * @ingroup widgets + * + * @{ + */ +typedef struct _separator separator; + +/** + * @param h The height of the separator. + * + * Create a horizontal separator with height h. + * + * @returns a new separator, free with ::widget_free + */ +separator *separator_create ( short h ); + +/*@}*/ +#endif // ROFI_SEPARATOR_H diff --git a/include/textbox.h b/include/widgets/textbox.h similarity index 90% rename from include/textbox.h rename to include/widgets/textbox.h index ab704e9d..759220bf 100644 --- a/include/textbox.h +++ b/include/widgets/textbox.h @@ -12,14 +12,13 @@ /** * @defgroup Textbox Textbox - * @ingroup Widgets + * @ingroup widgets * * @{ */ - typedef struct { - Widget widget; + widget widget; unsigned long flags; short cursor; Color color_fg, color_bg; @@ -39,16 +38,16 @@ typedef struct typedef enum { - TB_AUTOHEIGHT = 1 << 0, - TB_AUTOWIDTH = 1 << 1, - TB_LEFT = 1 << 16, - TB_RIGHT = 1 << 17, - TB_CENTER = 1 << 18, - TB_EDITABLE = 1 << 19, - TB_MARKUP = 1 << 20, - TB_WRAP = 1 << 21, - TB_PASSWORD = 1 << 22, - TB_INDICATOR = 1 << 23, + TB_AUTOHEIGHT = 1 << 0, + TB_AUTOWIDTH = 1 << 1, + TB_LEFT = 1 << 16, + TB_RIGHT = 1 << 17, + TB_CENTER = 1 << 18, + TB_EDITABLE = 1 << 19, + TB_MARKUP = 1 << 20, + TB_WRAP = 1 << 21, + TB_PASSWORD = 1 << 22, + TB_INDICATOR = 1 << 23, } TextboxFlags; typedef enum @@ -146,15 +145,6 @@ void textbox_cleanup ( void ); */ int textbox_get_height ( const textbox *tb ); -/** - * @param tb Handle to the textbox - * - * Get the width of the textbox - * - * @returns the width of the textbox in pixels. - */ -int textbox_get_width ( const textbox *tb ); - /** * @param tb Handle to the textbox * diff --git a/include/widgets/widget.h b/include/widgets/widget.h new file mode 100644 index 00000000..2e92f316 --- /dev/null +++ b/include/widgets/widget.h @@ -0,0 +1,115 @@ +#ifndef ROFI_WIDGET_H +#define ROFI_WIDGET_H +#include +#include +#include +#include +/** + * @defgroup widgets widgets + * + * Generic widget class + * + * @{ + */ +typedef struct _widget widget; +typedef gboolean ( *widget_clicked_cb )( widget *, xcb_button_press_event_t *, void * ); +struct _widget +{ + /** X position relative to parent */ + short x; + /** Y position relative to parent */ + short y; + /** Width of the widget */ + short w; + /** Height of the widget */ + short h; + /** enabled or not */ + gboolean enabled; + /** Information about packing. */ + gboolean expand; + gboolean end; + + struct _widget *parent; + /** Internal */ + gboolean need_redraw; + /** Function prototypes */ + int ( *get_width )( struct _widget * ); + int ( *get_height )( struct _widget * ); + + void ( *draw )( struct _widget *widget, cairo_t *draw ); + void ( *resize )( struct _widget *, short, short ); + void ( *update )( struct _widget * ); + + // Signals. + widget_clicked_cb clicked; + void *clicked_cb_data; + + // Free + void ( *free )( struct _widget *widget ); +}; + +/** Macro to get widget from an implementation (e.g. textbox/scrollbar) */ +#define WIDGET( a ) ( ( a ) != NULL ? (widget *) ( a ) : NULL ) + +/** + * @param widget The widget to check + * @param x The X position relative to parent window + * @param y the Y position relative to parent window + * + * Check if x,y falls within the widget. + * + * @return TRUE if x,y falls within the widget + */ +int widget_intersect ( const widget *widget, int x, int y ); + +/** + * @param widget The widget to move + * @param x The new X position relative to parent window + * @param y The new Y position relative to parent window + * + * Moves the widget. + */ +void widget_move ( widget *widget, short x, short y ); + +gboolean widget_enabled ( widget *widget ); +void widget_disable ( widget *widget ); +void widget_enable ( widget *widget ); + +/** + * @param tb Handle to the widget + * @param draw The cairo object used to draw itself. + * + * Render the textbox. + */ +void widget_draw ( widget *widget, cairo_t *d ); + +/** + * @param tb Handle to the widget + * + * Free the widget and all allocated memory. + */ +void widget_free ( widget *widget ); + +/** + * @param widget The widget toresize + * @param w The new width + * @param h The new height + * + * Resizes the widget. + */ +void widget_resize ( widget *widget, short w, short h ); + +int widget_get_height ( widget *widget ); +int widget_get_width ( widget *widget ); + +void widget_update ( widget *widget ); +void widget_queue_redraw ( widget *widget ); +gboolean widget_need_redraw ( widget *wid ); + +gboolean widget_clicked ( widget *wid, xcb_button_press_event_t *xbe ); + +// Signal! +void widget_set_clicked_handler ( widget *wid, widget_clicked_cb cb, void *udata ); + +/*@}*/ +#endif // ROFI_WIDGET_H diff --git a/source/dialogs/combi.c b/source/dialogs/combi.c index a64ed5eb..ed4e7131 100644 --- a/source/dialogs/combi.c +++ b/source/dialogs/combi.c @@ -169,6 +169,9 @@ static ModeMode combi_mode_result ( Mode *sw, int mretv, char **input, unsigned return MODE_EXIT; } } + if ( mretv & MENU_QUICK_SWITCH ) { + return mretv & MENU_LOWER_MASK; + } for ( unsigned i = 0; i < pd->num_switchers; i++ ) { if ( selected_line >= pd->starts[i] && diff --git a/source/dialogs/dmenu.c b/source/dialogs/dmenu.c index 7f00edda..4b8b3169 100644 --- a/source/dialogs/dmenu.c +++ b/source/dialogs/dmenu.c @@ -41,7 +41,7 @@ #include #include "rofi.h" #include "settings.h" -#include "textbox.h" +#include "widgets/textbox.h" #include "dialogs/dmenu.h" #include "helper.h" #include "xrmoptions.h" @@ -72,8 +72,6 @@ static inline void bittoggle ( uint32_t *array, unsigned int index ) typedef struct { /** Settings */ - // Prompt - char *prompt; // Separator. char separator; @@ -364,7 +362,6 @@ static int dmenu_mode_init ( Mode *sw ) mode_set_private_data ( sw, g_malloc0 ( sizeof ( DmenuModePrivateData ) ) ); DmenuModePrivateData *pd = (DmenuModePrivateData *) mode_get_private_data ( sw ); - pd->prompt = "dmenu "; pd->separator = '\n'; pd->selected_line = UINT32_MAX; @@ -373,8 +370,6 @@ static int dmenu_mode_init ( Mode *sw ) // Input data separator. find_arg_char ( "-sep", &( pd->separator ) ); - // Check prompt - find_arg_str ( "-p", &( pd->prompt ) ); find_arg_uint ( "-selected-row", &( pd->selected_line ) ); // By default we print the unescaped line back. pd->format = "s"; @@ -663,7 +658,8 @@ int dmenu_switcher_dialog ( void ) g_free ( input ); return TRUE; } - RofiViewState *state = rofi_view_create ( &dmenu_mode, input, pd->prompt, pd->message, menu_flags, dmenu_finalize ); + find_arg_str ( "-p", &( dmenu_mode.display_name ) ); + RofiViewState *state = rofi_view_create ( &dmenu_mode, input, pd->message, menu_flags, dmenu_finalize ); // @TODO we should do this better. if ( async ) { rofi_view_set_overlay ( state, "Loading.. " ); diff --git a/source/dialogs/drun.c b/source/dialogs/drun.c index 8a9768f3..6e89180a 100644 --- a/source/dialogs/drun.c +++ b/source/dialogs/drun.c @@ -42,7 +42,7 @@ #include "rofi.h" #include "settings.h" #include "helper.h" -#include "textbox.h" +#include "widgets/textbox.h" #include "history.h" #include "dialogs/drun.h" diff --git a/source/dialogs/help-keys.c b/source/dialogs/help-keys.c index dc0955e6..d5e586bf 100644 --- a/source/dialogs/help-keys.c +++ b/source/dialogs/help-keys.c @@ -42,7 +42,7 @@ #include "helper.h" #include "xrmoptions.h" #include "dialogs/help-keys.h" -#include "textbox.h" +#include "widgets/textbox.h" typedef struct { diff --git a/source/dialogs/ssh.c b/source/dialogs/ssh.c index 397893dc..f11ba3a1 100644 --- a/source/dialogs/ssh.c +++ b/source/dialogs/ssh.c @@ -198,7 +198,7 @@ static char **read_hosts_file ( char ** retv, unsigned int *length ) // Reading one line per time. while ( getline ( &buffer, &buffer_length, fd ) > 0 ) { // Evaluate one line. - unsigned int index = 0, ti = 0; + unsigned int index = 0, ti = 0; char *token = buffer; // Tokenize it. diff --git a/source/dialogs/window.c b/source/dialogs/window.c index cfcaa1b3..f7786dbd 100644 --- a/source/dialogs/window.c +++ b/source/dialogs/window.c @@ -47,7 +47,7 @@ #include "rofi.h" #include "settings.h" #include "helper.h" -#include "textbox.h" +#include "widgets/textbox.h" #include "x11-helper.h" #include "dialogs/window.h" diff --git a/source/helper.c b/source/helper.c index 9e420c91..ef402ed5 100644 --- a/source/helper.c +++ b/source/helper.c @@ -194,7 +194,7 @@ static GRegex * create_regex ( const char *input, int case_sensitive ) { #define R( s ) g_regex_new ( s, G_REGEX_OPTIMIZE | ( ( case_sensitive ) ? 0 : G_REGEX_CASELESS ), 0, NULL ) GRegex * retv = NULL; - gchar *r; + gchar *r; switch ( config.matching_method ) { case MM_GLOB: @@ -234,7 +234,7 @@ GRegex **tokenize ( const char *input, int case_sensitive ) } char *saveptr = NULL, *token; - GRegex **retv = NULL; + GRegex **retv = NULL; if ( !config.tokenize ) { retv = g_malloc0 ( sizeof ( GRegex* ) * 2 ); retv[0] = (GRegex *) create_regex ( input, case_sensitive ); diff --git a/source/rofi.c b/source/rofi.c index 6ca7ed11..b35128c0 100644 --- a/source/rofi.c +++ b/source/rofi.c @@ -54,7 +54,7 @@ #include "mode.h" #include "rofi.h" #include "helper.h" -#include "textbox.h" +#include "widgets/textbox.h" #include "x11-helper.h" #include "xrmoptions.h" #include "dialogs/dialogs.h" @@ -73,9 +73,9 @@ struct xkb_stuff xkb = { .keymap = NULL, .state = NULL, .compose = { - .table = NULL, - .state = NULL - } + .table = NULL, + .state = NULL +} }; char *config_path = NULL; // Array of modi. @@ -157,24 +157,6 @@ static void teardown ( int pfd ) // Cleanup pid file. remove_pid_file ( pfd ); } - -static void __run_switcher_internal ( ModeMode mode, char *input ) -{ - char *prompt = g_strdup_printf ( "%s:", mode_get_display_name ( modi[mode] ) ); - curr_switcher = mode; - RofiViewState * state = rofi_view_create ( modi[mode], input, prompt, NULL, MENU_NORMAL, process_result ); - g_free ( prompt ); - if ( state ) { - rofi_view_set_active ( state ); - } - else { - rofi_view_set_active ( NULL ); - - if ( rofi_view_get_active () == NULL ) { - g_main_loop_quit ( main_loop ); - } - } -} static void run_switcher ( ModeMode mode ) { // Otherwise check if requested mode is enabled. @@ -184,9 +166,14 @@ static void run_switcher ( ModeMode mode ) return; } } - char *input = g_strdup ( config.filter ); - __run_switcher_internal ( mode, input ); - g_free ( input ); + curr_switcher = mode; + RofiViewState * state = rofi_view_create ( modi[mode], config.filter, NULL, MENU_NORMAL, process_result ); + if ( state ) { + rofi_view_set_active ( state ); + } + if ( rofi_view_get_active () == NULL ) { + g_main_loop_quit ( main_loop ); + } } void process_result ( RofiViewState *state ) { @@ -197,8 +184,9 @@ void process_result ( RofiViewState *state ) MenuReturn mretv = rofi_view_get_return_value ( state ); char *input = g_strdup ( rofi_view_get_user_input ( state ) ); ModeMode retv = mode_result ( sw, mretv, &input, selected_line ); + g_free ( input ); - ModeMode mode = curr_switcher; + ModeMode mode = curr_switcher; // Find next enabled if ( retv == NEXT_DIALOG ) { mode = ( mode + 1 ) % num_modi; @@ -224,9 +212,11 @@ void process_result ( RofiViewState *state ) /** * Load in the new mode. */ - __run_switcher_internal ( mode, input ); + rofi_view_switch_mode ( state, modi[mode] ); + rofi_view_set_active ( state ); + curr_switcher = mode; + return; } - g_free ( input ); } rofi_view_free ( state ); } @@ -466,22 +456,22 @@ static gboolean main_loop_x11_event_handler ( xcb_generic_event_t *ev, G_GNUC_UN xkb.state = xkb_x11_state_new_from_device ( xkb.keymap, xcb->connection, xkb.device_id ); break; case XCB_XKB_STATE_NOTIFY: - { - xcb_xkb_state_notify_event_t *ksne = (xcb_xkb_state_notify_event_t *) ev; - guint modmask; - xkb_state_update_mask ( xkb.state, - ksne->baseMods, - ksne->latchedMods, - ksne->lockedMods, - ksne->baseGroup, - ksne->latchedGroup, - ksne->lockedGroup ); - modmask = x11_get_current_mask ( &xkb ); - if ( modmask == 0 ) { - abe_trigger_release ( ); + { + xcb_xkb_state_notify_event_t *ksne = (xcb_xkb_state_notify_event_t *) ev; + guint modmask; + xkb_state_update_mask ( xkb.state, + ksne->baseMods, + ksne->latchedMods, + ksne->lockedMods, + ksne->baseGroup, + ksne->latchedGroup, + ksne->lockedGroup ); + modmask = x11_get_current_mask ( &xkb ); + if ( modmask == 0 ) { + abe_trigger_release ( ); + } + break; } - break; - } } return G_SOURCE_CONTINUE; } @@ -543,19 +533,18 @@ static gboolean startup ( G_GNUC_UNUSED gpointer data ) * Create window (without showing) */ __create_window ( window_flags ); - - // - // Sanity check - if ( config_sanity_check ( ) ) { - return G_SOURCE_REMOVE; - } - TICK_N ( "Config sanity check" ); + TICK_N ( "Create Window" ); // Parse the keybindings. if ( !parse_keys_abe () ) { // Error dialog return G_SOURCE_REMOVE; } TICK_N ( "Parse ABE" ); + // Sanity check + if ( config_sanity_check ( ) ) { + return G_SOURCE_REMOVE; + } + TICK_N ( "Config sanity check" ); // Dmenu mode. if ( dmenu_mode == TRUE ) { // force off sidebar mode: diff --git a/source/view.c b/source/view.c index db4d0c62..0e1f0c4a 100644 --- a/source/view.c +++ b/source/view.c @@ -51,8 +51,6 @@ #include "xcb-internal.h" #include "xkb-internal.h" #include "helper.h" -#include "textbox.h" -#include "scrollbar.h" #include "x11-helper.h" #include "xrmoptions.h" #include "dialogs/dialogs.h" @@ -64,16 +62,12 @@ #include "xcb.h" -const char *const separator_style_none = "none"; -const char *const separator_style_dash = "dash"; -static void rofi_view_calculate_height ( RofiViewState *state, int rows ); +static int rofi_view_calculate_height ( RofiViewState *state ); GThreadPool *tpool = NULL; RofiViewState *current_active_menu = NULL; -static void rofi_view_resize ( RofiViewState *state ); - struct { xcb_window_t main_window; @@ -178,6 +172,15 @@ static void menu_capture_screenshot ( void ) g_date_time_unref ( now ); } +static void rofi_view_update_prompt ( RofiViewState *state ) +{ + if ( state->prompt ) { + char *prompt = g_strdup_printf ( "%s:", mode_get_display_name ( state->sw ) ); + textbox_text ( state->prompt, prompt ); + g_free ( prompt ); + } +} + /** * Calculates the window position */ @@ -190,7 +193,7 @@ static void rofi_view_calculate_window_position ( RofiViewState *state ) } if ( !config.fixed_num_lines && ( config.location == WL_CENTER || config.location == WL_EAST || config.location == WL_WEST ) ) { - state->y = CacheState.mon.y + CacheState.mon.h / 2 - state->top_offset; + state->y = CacheState.mon.y + CacheState.mon.h / 2 - widget_get_height ( WIDGET ( state->input_bar ) ); } else { // Default location is center. @@ -237,13 +240,12 @@ static void rofi_view_window_update_size ( RofiViewState * state ) // Display it. xcb_configure_window ( xcb->connection, CacheState.main_window, mask, vals ); cairo_xcb_surface_set_size ( CacheState.surface, state->width, state->height ); + widget_resize ( WIDGET ( state->main_box ), state->width - 2 * state->border, state->height - 2 * state->border ); } static gboolean rofi_view_reload_idle ( G_GNUC_UNUSED gpointer data ) { if ( current_active_menu ) { - // @TODO check - current_active_menu->update = TRUE; current_active_menu->reload = TRUE; current_active_menu->refilter = TRUE; xcb_clear_area ( xcb->connection, CacheState.main_window, 1, 0, 0, 1, 1 ); @@ -263,7 +265,6 @@ void rofi_view_reload ( void ) void rofi_view_queue_redraw ( void ) { if ( current_active_menu ) { - current_active_menu->update = TRUE; xcb_clear_area ( xcb->connection, CacheState.main_window, 1, 0, 0, 1, 1 ); xcb_flush ( xcb->connection ); } @@ -284,9 +285,10 @@ void rofi_view_set_active ( RofiViewState *state ) { if ( current_active_menu != NULL && state != NULL ) { g_queue_push_head ( &( CacheState.views ), state ); + // TODO check. + current_active_menu = state; g_log ( LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "stack view." ); rofi_view_window_update_size ( current_active_menu ); - rofi_view_resize ( current_active_menu ); rofi_view_queue_redraw (); return; } @@ -294,7 +296,6 @@ void rofi_view_set_active ( RofiViewState *state ) g_log ( LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "pop view." ); current_active_menu = g_queue_pop_head ( &( CacheState.views ) ); rofi_view_window_update_size ( current_active_menu ); - rofi_view_resize ( current_active_menu ); rofi_view_queue_redraw (); return; } @@ -306,15 +307,14 @@ void rofi_view_set_selected_line ( RofiViewState *state, unsigned int selected_l { state->selected_line = selected_line; // Find the line. - state->selected = 0; - for ( unsigned int i = 0; ( ( state->selected_line ) ) < UINT32_MAX && !state->selected && i < state->filtered_lines; i++ ) { + unsigned int selected = 0; + for ( unsigned int i = 0; ( ( state->selected_line ) ) < UINT32_MAX && !selected && i < state->filtered_lines; i++ ) { if ( state->line_map[i] == ( state->selected_line ) ) { - state->selected = i; + selected = i; break; } } - - state->update = TRUE; + listview_set_selected ( state->list_view, selected ); xcb_clear_area ( xcb->connection, CacheState.main_window, 1, 0, 0, 1, 1 ); xcb_flush ( xcb->connection ); } @@ -323,26 +323,14 @@ void rofi_view_free ( RofiViewState *state ) { // Do this here? // Wait for final release? - widget_free ( WIDGET ( state->text ) ); - widget_free ( WIDGET ( state->prompt_tb ) ); - widget_free ( WIDGET ( state->case_indicator ) ); - widget_free ( WIDGET ( state->scrollbar ) ); + widget_free ( WIDGET ( state->main_box ) ); widget_free ( WIDGET ( state->overlay ) ); - for ( unsigned int i = 0; i < state->max_elements; i++ ) { - widget_free ( WIDGET ( state->boxes[i] ) ); - } - - g_free ( state->boxes ); g_free ( state->line_map ); g_free ( state->distance ); // Free the switcher boxes. // When state is free'ed we should no longer need these. if ( config.sidebar_mode == TRUE ) { - for ( unsigned int j = 0; j < state->num_modi; j++ ) { - widget_free ( WIDGET ( state->modi[j] ) ); - state->modi[j] = NULL; - } g_free ( state->modi ); state->num_modi = 0; } @@ -362,8 +350,9 @@ unsigned int rofi_view_get_selected_line ( const RofiViewState *state ) unsigned int rofi_view_get_next_position ( const RofiViewState *state ) { unsigned int next_pos = state->selected_line; - if ( ( state->selected + 1 ) < state->num_lines ) { - ( next_pos ) = state->line_map[state->selected + 1]; + unsigned int selected = listview_get_selected ( state->list_view ); + if ( ( selected + 1 ) < state->num_lines ) { + ( next_pos ) = state->line_map[selected + 1]; } return next_pos; } @@ -373,103 +362,27 @@ unsigned int rofi_view_get_completed ( const RofiViewState *state ) return state->quit; } -static void rofi_view_resize ( RofiViewState *state ) -{ - if ( ( state->menu_flags & MENU_ERROR_DIALOG ) == MENU_ERROR_DIALOG ) { - // Resize of error dialog. - int entrybox_width = state->width - ( 2 * ( state->border ) ); - textbox_moveresize ( state->text, state->text->widget.x, state->text->widget.y, entrybox_width, state->line_height ); - state->rchanged = TRUE; - state->update = TRUE; - return; - } - unsigned int sbw = config.line_margin + 8; - widget_move ( WIDGET ( state->scrollbar ), state->width - state->border - sbw, state->top_offset ); - if ( config.sidebar_mode == TRUE ) { - int width = ( state->width - ( 2 * ( state->border ) + ( state->num_modi - 1 ) * config.line_margin ) ) / state->num_modi; - for ( unsigned int j = 0; j < state->num_modi; j++ ) { - textbox_moveresize ( state->modi[j], - state->border + j * ( width + config.line_margin ), state->height - state->line_height - state->border, - width, state->line_height ); - widget_draw ( WIDGET ( state->modi[j] ), CacheState.draw ); - } - } - int entrybox_width = state->width - ( 2 * ( state->border ) ); - int offset = 0; - int width = textbox_get_width ( state->case_indicator ); - entrybox_width -= width + textbox_get_width ( state->prompt_tb ); - offset = width; - textbox_moveresize ( state->text, state->text->widget.x, state->text->widget.y, entrybox_width, state->line_height ); - widget_move ( WIDGET ( state->case_indicator ), state->width - state->border - offset, state->border ); - /** - * Resize in Height - */ -/* if ( state->num_lines > 0 ) */ { - unsigned int last_length = state->max_elements; - int element_height = state->line_height * config.element_height + config.line_margin; - // Calculated new number of boxes. - int h = ( state->height - state->top_offset - state->border ); - // We always have one line margin extra space, as the last one is removed in the final tally. - h += config.line_margin; - if ( config.sidebar_mode == TRUE ) { - h -= state->line_height + config.line_margin; - } - if ( h < 0 ) { - fprintf ( stderr, - "Current padding %u (on each side) does not fit within visible window %d.\n", - config.padding, - state->height ); - h = ( state->height - state->top_offset - state->height / 3 ); - if ( config.sidebar_mode == TRUE ) { - h -= state->line_height + config.line_margin; - } - } - state->max_rows = MAX ( 0, ( h / element_height ) ); - state->menu_lines = state->max_rows; - state->max_elements = state->max_rows * config.menu_columns; - // Free boxes no longer needed. - for ( unsigned int i = state->max_elements; i < last_length; i++ ) { - widget_free ( WIDGET ( state->boxes[i] ) ); - } - // resize array. - state->boxes = g_realloc ( state->boxes, state->max_elements * sizeof ( textbox* ) ); - - int y_offset = state->top_offset; - int x_offset = state->border; - // Add newly added boxes. - for ( unsigned int i = last_length; i < state->max_elements; i++ ) { - state->boxes[i] = textbox_create ( ( state->menu_flags & MENU_INDICATOR ) ? TB_INDICATOR : 0, x_offset, y_offset, - state->element_width, element_height, NORMAL, "" ); - } - scrollbar_resize ( state->scrollbar, -1, ( state->max_rows ) * ( element_height ) - config.line_margin ); - } - - state->rchanged = TRUE; - state->update = TRUE; -} - void rofi_view_itterrate ( RofiViewState *state, xcb_generic_event_t *event, xkb_stuff *xkb ) { uint8_t type = event->response_type & ~0x80; switch ( type ) { case XCB_EXPOSE: - state->update = TRUE; + widget_queue_redraw ( WIDGET ( state->main_box ) ); break; case XCB_CONFIGURE_NOTIFY: { xcb_configure_notify_event_t *xce = (xcb_configure_notify_event_t *) event; if ( xce->window == CacheState.main_window ) { if ( state->x != xce->x || state->y != xce->y ) { - state->x = xce->x; - state->y = xce->y; - state->update = TRUE; + state->x = xce->x; + state->y = xce->y; } if ( state->width != xce->width || state->height != xce->height ) { state->width = xce->width; state->height = xce->height; cairo_xcb_surface_set_size ( CacheState.surface, state->width, state->height ); - rofi_view_resize ( state ); + widget_resize ( WIDGET ( state->main_box ), state->width - 2 * state->border, state->height - 2 * state->border ); } } break; @@ -674,42 +587,6 @@ void __create_window ( MenuFlags menu_flags ) } } -/** - * @param state Internal state of the menu. - * - * Calculate the number of rows, columns and elements to display based on the - * configuration and available data. - */ -static void rofi_view_calculate_rows_columns ( RofiViewState *state ) -{ - state->columns = config.menu_columns; - state->max_elements = MIN ( state->menu_lines * state->columns, state->num_lines ); - - // Calculate the number or rows. We do this by getting the num_lines rounded up to X columns - // (num elements is better name) then dividing by columns. - state->max_rows = MIN ( state->menu_lines, (unsigned int) ( ( state->num_lines + ( state->columns - state->num_lines % - state->columns ) % - state->columns ) / ( state->columns ) ) ); - state->max_rows = MAX ( 0, state->max_rows ); - - if ( config.fixed_num_lines == TRUE ) { - state->max_elements = state->menu_lines * state->columns; - state->max_rows = state->menu_lines; - // If it would fit in one column, only use one column. - if ( state->num_lines < state->max_elements ) { - state->columns = - ( state->num_lines + ( state->max_rows - state->num_lines % state->max_rows ) % state->max_rows ) / state->max_rows; - // Minimum 1 column - state->columns = MAX ( 1, state->columns ); - state->max_elements = state->menu_lines * state->columns; - } - // Sanitize. - if ( state->columns == 0 ) { - state->columns = 1; - } - } -} - /** * @param state Internal state of the menu. * @@ -723,142 +600,17 @@ static void rofi_view_calculate_window_and_element_width ( RofiViewState *state else if ( config.menu_width < 0 ) { double fw = textbox_get_estimated_char_width ( ); state->width = -( fw * config.menu_width ); - state->width += 2 * state->border + 4; // 4 = 2*SIDE_MARGIN + state->width += 2 * state->border; } else{ // Calculate as float to stop silly, big rounding down errors. state->width = config.menu_width < 101 ? ( CacheState.mon.w / 100.0f ) * ( float ) config.menu_width : config.menu_width; } - - if ( state->columns > 0 ) { - state->element_width = state->width - ( 2 * ( state->border ) ); - // Divide by the # columns - state->element_width = ( state->element_width - ( state->columns - 1 ) * config.line_margin ) / state->columns; - } } /** * Nav helper functions, to avoid duplicate code. */ -/** - * @param state The current RofiViewState - * - * Move the selection one page down. - * - No wrap around. - * - Clip at top/bottom - */ -inline static void rofi_view_nav_page_next ( RofiViewState *state ) -{ - // If no lines, do nothing. - if ( state->filtered_lines == 0 ) { - return; - } - state->selected += ( state->max_elements ); - if ( state->selected >= state->filtered_lines ) { - state->selected = state->filtered_lines - 1; - } - state->update = TRUE; -} - -/** - * @param state The current RofiViewState - * - * Move the selection one page up. - * - No wrap around. - * - Clip at top/bottom - */ -inline static void rofi_view_nav_page_prev ( RofiViewState * state ) -{ - if ( state->selected < state->max_elements ) { - state->selected = 0; - } - else{ - state->selected -= ( state->max_elements ); - } - state->update = TRUE; -} -/** - * @param state The current RofiViewState - * - * Move the selection one column to the right. - * - No wrap around. - * - Do not move to top row when at start. - */ -inline static void rofi_view_nav_right ( RofiViewState *state ) -{ - // If no lines, do nothing. - if ( state->filtered_lines == 0 ) { - return; - } - if ( ( state->selected + state->max_rows ) < state->filtered_lines ) { - state->selected += state->max_rows; - state->update = TRUE; - } - else if ( state->selected < ( state->filtered_lines - 1 ) ) { - // We do not want to move to last item, UNLESS the last column is only - // partially filled, then we still want to move column and select last entry. - // First check the column we are currently in. - int col = state->selected / state->max_rows; - // Check total number of columns. - int ncol = state->filtered_lines / state->max_rows; - // If there is an extra column, move. - if ( col != ncol ) { - state->selected = state->filtered_lines - 1; - state->update = TRUE; - } - } -} - -/** - * @param state The current RofiViewState - * - * Move the selection one column to the left. - * - No wrap around. - */ -inline static void rofi_view_nav_left ( RofiViewState *state ) -{ - if ( state->selected >= state->max_rows ) { - state->selected -= state->max_rows; - state->update = TRUE; - } -} - -/** - * @param state The current RofiViewState - * - * Move the selection one row up. - * - Wrap around. - */ -inline static void rofi_view_nav_up ( RofiViewState *state ) -{ - // If no lines or don't cycle, do nothing. - if ( state->filtered_lines == 0 || ( state->selected == 0 && !config.cycle ) ) { - return; - } - - // Wrap around. - if ( state->selected == 0 ) { - state->selected = state->filtered_lines; - } - state->selected--; - state->update = TRUE; -} - -/** - * @param state The current RofiViewState - * - * Move the selection one row down. - * - Wrap around. - */ -inline static void rofi_view_nav_down ( RofiViewState *state ) -{ - // If no lines or don't cycle, do nothing. - if ( state->filtered_lines == 0 || ( state->selected == state->filtered_lines - 1 && !config.cycle ) ) { - return; - } - state->selected = state->selected < state->filtered_lines - 1 ? MIN ( state->filtered_lines - 1, state->selected + 1 ) : 0; - state->update = TRUE; -} /** * @param state The current RofiViewState @@ -869,7 +621,7 @@ static void rofi_view_nav_row_tab ( RofiViewState *state ) { if ( state->filtered_lines == 1 ) { state->retv = MENU_OK; - ( state->selected_line ) = state->line_map[state->selected]; + ( state->selected_line ) = state->line_map[listview_get_selected ( state->list_view )]; state->quit = 1; return; } @@ -881,7 +633,7 @@ static void rofi_view_nav_row_tab ( RofiViewState *state ) state->quit = TRUE; } else { - rofi_view_nav_down ( state ); + listview_nav_down ( state->list_view ); } state->prev_action = ROW_TAB; } @@ -892,13 +644,16 @@ static void rofi_view_nav_row_tab ( RofiViewState *state ) */ inline static void rofi_view_nav_row_select ( RofiViewState *state ) { + if ( state->list_view ) { + return; + } + unsigned int selected = listview_get_selected ( state->list_view ); // If a valid item is selected, return that.. - if ( state->selected < state->filtered_lines ) { - char *str = mode_get_completion ( state->sw, state->line_map[state->selected] ); + if ( selected < state->filtered_lines ) { + char *str = mode_get_completion ( state->sw, state->line_map[selected] ); textbox_text ( state->text, str ); g_free ( str ); textbox_cursor_end ( state->text ); - state->update = TRUE; state->refilter = TRUE; } } @@ -910,8 +665,8 @@ inline static void rofi_view_nav_row_select ( RofiViewState *state ) */ inline static void rofi_view_nav_first ( RofiViewState * state ) { - state->selected = 0; - state->update = TRUE; +// state->selected = 0; + listview_set_selected ( state->list_view, 0 ); } /** @@ -925,142 +680,49 @@ inline static void rofi_view_nav_last ( RofiViewState * state ) if ( state->filtered_lines == 0 ) { return; } - state->selected = state->filtered_lines - 1; - state->update = TRUE; + //state->selected = state->filtered_lines - 1; + listview_set_selected ( state->list_view, -1 ); } -static unsigned int rofi_scroll_per_page ( RofiViewState * state ) +static void update_callback ( textbox *t, unsigned int index, void *udata, TextBoxFontType type, gboolean full ) { - int offset = 0; - - // selected row is always visible. - // If selected is visible do not scroll. - if ( ( ( state->selected - ( state->last_offset ) ) < ( state->max_elements ) ) && ( state->selected >= ( state->last_offset ) ) ) { - offset = state->last_offset; - } - else{ - // Do paginating - unsigned int page = ( state->max_elements > 0 ) ? ( state->selected / state->max_elements ) : 0; - offset = page * state->max_elements; - state->last_offset = offset; - if ( page != state->cur_page ) { - state->cur_page = page; - state->rchanged = TRUE; - } - // Set the position - scrollbar_set_handle ( state->scrollbar, page * state->max_elements ); - } - return offset; -} - -static unsigned int rofi_scroll_continious ( RofiViewState * state ) -{ - unsigned int middle = ( state->menu_lines - ( ( state->menu_lines & 1 ) == 0 ) ) / 2; - unsigned int offset = 0; - if ( state->selected > middle ) { - if ( state->selected < ( state->filtered_lines - ( state->menu_lines - middle ) ) ) { - offset = state->selected - middle; - } - // Don't go below zero. - else if ( state->filtered_lines > state->menu_lines ) { - offset = state->filtered_lines - state->menu_lines; - } - } - if ( offset != state->cur_page ) { - state->rchanged = TRUE; - scrollbar_set_handle ( state->scrollbar, offset ); - state->cur_page = offset; - } - return offset; -} - -static void rofi_view_draw ( RofiViewState *state, cairo_t *d ) -{ - unsigned int i, offset = 0; - if ( config.scroll_method == 1 ) { - offset = rofi_scroll_continious ( state ); - } - else { - offset = rofi_scroll_per_page ( state ); - } - // Re calculate the boxes and sizes, see if we can move this in the menu_calc*rowscolumns - // Get number of remaining lines to display. - unsigned int a_lines = MIN ( ( state->filtered_lines - offset ), state->max_elements ); - - // Calculate number of columns - unsigned int columns = ( a_lines + ( state->max_rows - a_lines % state->max_rows ) % state->max_rows ) / state->max_rows; - columns = MIN ( columns, state->columns ); - - // Update the handle length. - // Calculate number of visible rows. - unsigned int max_elements = MIN ( a_lines, state->max_rows * columns ); - - scrollbar_set_handle_length ( state->scrollbar, columns * state->max_rows ); - widget_draw ( WIDGET ( state->scrollbar ), d ); - // Element width. - unsigned int element_width = state->width - ( 2 * ( state->border ) ); - if ( state->scrollbar != NULL ) { - element_width -= state->scrollbar->widget.w; - } - if ( columns > 0 ) { - element_width = ( element_width - ( columns - 1 ) * config.line_margin ) / columns; - } - - int element_height = state->line_height * config.element_height; - int y_offset = state->top_offset; - int x_offset = state->border; - - if ( state->rchanged ) { - char *input = mode_preprocess_input ( state->sw, state->text->text ); + RofiViewState *state = (RofiViewState *) udata; + if ( full ) { + char *input = mode_preprocess_input ( state->sw, state->text->text ); + // TODO: MOVE THIS, expensive. GRegex **tokens = tokenize ( input, config.case_sensitive ); g_free ( input ); - // Move, resize visible boxes and show them. - for ( i = 0; i < max_elements && ( i + offset ) < state->filtered_lines; i++ ) { - unsigned int ex = ( ( i ) / state->max_rows ) * ( element_width + config.line_margin ); - unsigned int ey = ( ( i ) % state->max_rows ) * ( element_height + config.line_margin ); - // Move it around. - textbox_moveresize ( state->boxes[i], ex + x_offset, ey + y_offset, element_width, element_height ); - { - TextBoxFontType type = ( ( ( i % state->max_rows ) & 1 ) == 0 ) ? NORMAL : ALT; - int fstate = 0; - char *text = mode_get_display_value ( state->sw, state->line_map[i + offset], &fstate, TRUE ); - TextBoxFontType tbft = fstate | ( ( i + offset ) == state->selected ? HIGHLIGHT : type ); - textbox_font ( state->boxes[i], tbft ); - textbox_text ( state->boxes[i], text ); + int fstate = 0; + char *text = mode_get_display_value ( state->sw, state->line_map[index], &fstate, TRUE ); + type |= fstate; + textbox_font ( t, type ); + // Move into list view. + textbox_text ( t, text ); - PangoAttrList *list = textbox_get_pango_attributes ( state->boxes[i] ); - if ( list != NULL ) { - pango_attr_list_ref ( list ); - } - else{ - list = pango_attr_list_new (); - } - token_match_get_pango_attr ( tokens, textbox_get_visible_text ( state->boxes[i] ), list ); - textbox_set_pango_attributes ( state->boxes[i], list ); - pango_attr_list_unref ( list ); - g_free ( text ); - } - widget_draw ( WIDGET ( state->boxes[i] ), d ); + PangoAttrList *list = textbox_get_pango_attributes ( t ); + if ( list != NULL ) { + pango_attr_list_ref ( list ); } + else{ + list = pango_attr_list_new (); + } + token_match_get_pango_attr ( tokens, textbox_get_visible_text ( t ), list ); + textbox_set_pango_attributes ( t, list ); + pango_attr_list_unref ( list ); + g_free ( text ); tokenize_free ( tokens ); - state->rchanged = FALSE; } - else{ - // Only do basic redrawing + highlight of row. - for ( i = 0; i < max_elements && ( i + offset ) < state->filtered_lines; i++ ) { - TextBoxFontType type = ( ( ( i % state->max_rows ) & 1 ) == 0 ) ? NORMAL : ALT; - int fstate = 0; - mode_get_display_value ( state->sw, state->line_map[i + offset], &fstate, FALSE ); - TextBoxFontType tbft = fstate | ( ( i + offset ) == state->selected ? HIGHLIGHT : type ); - textbox_font ( state->boxes[i], tbft ); - widget_draw ( WIDGET ( state->boxes[i] ), d ); - } + else { + int fstate = 0; + mode_get_display_value ( state->sw, state->line_map[index], &fstate, FALSE ); + type |= fstate; + textbox_font ( t, type ); } } void rofi_view_update ( RofiViewState *state ) { - if ( !state->update ) { + if ( !widget_need_redraw ( WIDGET ( state->main_box ) ) && !widget_need_redraw ( WIDGET ( state->overlay ) ) ) { return; } TICK (); @@ -1105,59 +767,11 @@ void rofi_view_update ( RofiViewState *state ) // Always paint as overlay over the background. cairo_set_operator ( d, CAIRO_OPERATOR_OVER ); - if ( state->max_elements > 0 ) { - rofi_view_draw ( state, d ); - } - if ( state->prompt_tb ) { - widget_draw ( WIDGET ( state->prompt_tb ), d ); - } - if ( state->text ) { - widget_draw ( WIDGET ( state->text ), d ); - } - if ( state->case_indicator ) { - widget_draw ( WIDGET ( state->case_indicator ), d ); - } - if ( state->message_tb ) { - widget_draw ( WIDGET ( state->message_tb ), d ); - } - color_separator ( d ); + widget_draw ( WIDGET ( state->main_box ), d ); - if ( strcmp ( config.separator_style, separator_style_none ) ) { - if ( strcmp ( config.separator_style, separator_style_dash ) == 0 ) { - const double dashes[1] = { 4 }; - cairo_set_dash ( d, dashes, 1, 0.0 ); - } - // Don't show line, if there are no rows below it. - if ( state->max_rows > 0 ) { - cairo_move_to ( d, state->border, state->line_height + ( state->border ) * 1 + config.line_margin + 1 ); - cairo_line_to ( d, state->width - state->border, state->line_height + ( state->border ) * 1 + config.line_margin + 1 ); - cairo_stroke ( d ); - } - if ( state->message_tb ) { - cairo_move_to ( d, state->border, state->top_offset - ( config.line_margin ) - 1 ); - cairo_line_to ( d, state->width - state->border, state->top_offset - ( config.line_margin ) - 1 ); - cairo_stroke ( d ); - } - - if ( config.sidebar_mode == TRUE ) { - cairo_move_to ( d, state->border, state->height - state->line_height - ( state->border ) * 1 - 1 - config.line_margin ); - cairo_line_to ( d, - state->width - state->border, - state->height - state->line_height - ( state->border ) * 1 - 1 - config.line_margin ); - cairo_stroke ( d ); - } - } - if ( config.sidebar_mode == TRUE ) { - for ( unsigned int j = 0; j < state->num_modi; j++ ) { - if ( state->modi[j] != NULL ) { - widget_draw ( WIDGET ( state->modi[j] ), d ); - } - } - } if ( state->overlay ) { widget_draw ( WIDGET ( state->overlay ), d ); } - state->update = FALSE; // Draw to actual window. cairo_set_source_surface ( CacheState.draw, surf, 0, 0 ); @@ -1196,7 +810,6 @@ static void rofi_view_paste ( RofiViewState *state, xcb_selection_notify_event_t textbox_insert ( state->text, state->text->cursor, text, dl ); textbox_cursor ( state->text, state->text->cursor + g_utf8_strlen ( text, -1 ) ); // Force a redraw and refiltering of the text. - state->update = TRUE; state->refilter = TRUE; } g_free ( text ); @@ -1211,58 +824,26 @@ static void rofi_view_mouse_navigation ( RofiViewState *state, xcb_button_press_ // Scroll event if ( xbe->detail > 3 ) { if ( xbe->detail == 4 ) { - rofi_view_nav_up ( state ); + listview_nav_up ( state->list_view ); } else if ( xbe->detail == 5 ) { - rofi_view_nav_down ( state ); + listview_nav_down ( state->list_view ); } else if ( xbe->detail == 6 ) { - rofi_view_nav_left ( state ); + listview_nav_left ( state->list_view ); } else if ( xbe->detail == 7 ) { - rofi_view_nav_right ( state ); + listview_nav_right ( state->list_view ); } return; } else { - if ( state->scrollbar && widget_intersect ( &( state->scrollbar->widget ), xbe->event_x, xbe->event_y ) ) { - state->selected = scrollbar_clicked ( state->scrollbar, xbe->event_y ); - state->update = TRUE; + xcb_button_press_event_t rel = *xbe; + rel.event_x -= config.padding; + rel.event_y -= config.padding; + if ( widget_clicked ( WIDGET ( state->main_box ), &rel ) ) { return; } - for ( unsigned int i = 0; config.sidebar_mode == TRUE && i < state->num_modi; i++ ) { - if ( widget_intersect ( &( state->modi[i]->widget ), xbe->event_x, xbe->event_y ) ) { - ( state->selected_line ) = 0; - state->retv = MENU_QUICK_SWITCH | ( i & MENU_LOWER_MASK ); - state->quit = TRUE; - state->skip_absorb = TRUE; - return; - } - } - for ( unsigned int i = 0; i < state->max_elements; i++ ) { - if ( widget_intersect ( &( state->boxes[i]->widget ), xbe->event_x, xbe->event_y ) ) { - int control = x11_modifier_active ( xbe->state, X11MOD_CONTROL ); - // Only allow items that are visible to be selected. - if ( ( state->last_offset + i ) >= state->filtered_lines ) { - break; - } - // - state->selected = state->last_offset + i; - state->update = TRUE; - if ( ( xbe->time - state->last_button_press ) < 200 || control ) { - state->retv = MENU_OK; - if ( control ) { - state->retv |= MENU_CUSTOM_ACTION; - } - ( state->selected_line ) = state->line_map[state->selected]; - // Quit - state->quit = TRUE; - state->skip_absorb = TRUE; - } - state->last_button_press = xbe->time; - break; - } - } } } static void _rofi_view_reload_row ( RofiViewState *state ) @@ -1346,28 +927,17 @@ static void rofi_view_refilter ( RofiViewState *state ) } state->filtered_lines = state->num_lines; } - if ( state->filtered_lines > 0 ) { - state->selected = MIN ( state->selected, state->filtered_lines - 1 ); - } - else { - state->selected = 0; - } + listview_set_num_elements ( state->list_view, state->filtered_lines ); if ( config.auto_select == TRUE && state->filtered_lines == 1 && state->num_lines > 1 ) { - ( state->selected_line ) = state->line_map[state->selected]; + ( state->selected_line ) = state->line_map[listview_get_selected ( state->list_view )]; state->retv = MENU_OK; state->quit = TRUE; } - scrollbar_set_max_value ( state->scrollbar, state->filtered_lines ); - if ( current_active_menu && config.fixed_num_lines == FALSE && ( CacheState.flags & MENU_NORMAL_WINDOW ) == 0 ) { - int columns = config.menu_columns; - // Calculate the number or rows. We do this by getting the num_lines rounded up to X columns - // (num elements is better name) then dividing by columns. - unsigned int max_rows = MIN ( config.menu_lines, (unsigned int) ( ( state->filtered_lines + ( columns - - state->filtered_lines % columns ) % columns ) / ( columns ) ) ); - if ( max_rows != state->max_rows ) { - rofi_view_calculate_height ( state, max_rows ); - rofi_view_resize ( state ); + if ( config.fixed_num_lines == FALSE && ( CacheState.flags & MENU_NORMAL_WINDOW ) == 0 ) { + int height = rofi_view_calculate_height ( state ); + if ( height != state->height ) { + state->height = height; rofi_view_calculate_window_position ( state ); rofi_view_window_update_size ( state ); g_log ( LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Resize based on re-filter" ); @@ -1375,7 +945,6 @@ static void rofi_view_refilter ( RofiViewState *state ) } state->refilter = FALSE; state->rchanged = TRUE; - state->update = TRUE; TICK_N ( "Filter done" ); } /** @@ -1413,7 +982,6 @@ gboolean rofi_view_trigger_action ( RofiViewState *state, KeyBindingAction actio case TOGGLE_SORT: config.levenshtein_sort = !config.levenshtein_sort; state->refilter = TRUE; - state->update = TRUE; textbox_text ( state->case_indicator, get_matching_state () ); break; case MODE_PREVIOUS: @@ -1432,13 +1000,14 @@ gboolean rofi_view_trigger_action ( RofiViewState *state, KeyBindingAction actio config.case_sensitive = !config.case_sensitive; ( state->selected_line ) = 0; state->refilter = TRUE; - state->update = TRUE; textbox_text ( state->case_indicator, get_matching_state () ); break; // Special delete entry command. case DELETE_ENTRY: - if ( state->selected < state->filtered_lines ) { - ( state->selected_line ) = state->line_map[state->selected]; + { + unsigned int selected = listview_get_selected ( state->list_view ); + if ( selected < state->filtered_lines ) { + ( state->selected_line ) = state->line_map[selected]; state->retv = MENU_ENTRY_DELETE; state->quit = TRUE; } @@ -1446,6 +1015,7 @@ gboolean rofi_view_trigger_action ( RofiViewState *state, KeyBindingAction actio ret = FALSE; } break; + } case CUSTOM_1: case CUSTOM_2: case CUSTOM_3: @@ -1465,38 +1035,41 @@ gboolean rofi_view_trigger_action ( RofiViewState *state, KeyBindingAction actio case CUSTOM_17: case CUSTOM_18: case CUSTOM_19: + { state->selected_line = UINT32_MAX; - if ( state->selected < state->filtered_lines ) { - ( state->selected_line ) = state->line_map[state->selected]; + unsigned int selected = listview_get_selected ( state->list_view ); + if ( selected < state->filtered_lines ) { + ( state->selected_line ) = state->line_map[selected]; } state->retv = MENU_QUICK_SWITCH | ( ( action - CUSTOM_1 ) & MENU_LOWER_MASK ); state->quit = TRUE; break; + } // If you add a binding here, make sure to add it to rofi_view_keyboard_navigation too case CANCEL: state->retv = MENU_CANCEL; state->quit = TRUE; break; case ROW_UP: - rofi_view_nav_up ( state ); + listview_nav_up ( state->list_view ); break; case ROW_TAB: rofi_view_nav_row_tab ( state ); break; case ROW_DOWN: - rofi_view_nav_down ( state ); + listview_nav_down ( state->list_view ); break; case ROW_LEFT: - rofi_view_nav_left ( state ); + listview_nav_left ( state->list_view ); break; case ROW_RIGHT: - rofi_view_nav_right ( state ); + listview_nav_right ( state->list_view ); break; case PAGE_PREV: - rofi_view_nav_page_prev ( state ); + listview_nav_page_prev ( state->list_view ); break; case PAGE_NEXT: - rofi_view_nav_page_next ( state ); + listview_nav_page_next ( state->list_view ); break; case ROW_FIRST: rofi_view_nav_first ( state ); @@ -1526,19 +1099,18 @@ gboolean rofi_view_trigger_action ( RofiViewState *state, KeyBindingAction actio if ( rc == 1 ) { // Entry changed. state->refilter = TRUE; - state->update = TRUE; } else if ( rc == 2 ) { // Movement. - state->update = TRUE; } break; } case ACCEPT_ALT: { + unsigned int selected = listview_get_selected ( state->list_view ); state->selected_line = UINT32_MAX; - if ( state->selected < state->filtered_lines ) { - ( state->selected_line ) = state->line_map[state->selected]; + if ( selected < state->filtered_lines ) { + ( state->selected_line ) = state->line_map[selected]; state->retv = MENU_OK; } else { @@ -1559,9 +1131,10 @@ gboolean rofi_view_trigger_action ( RofiViewState *state, KeyBindingAction actio case ACCEPT_ENTRY: { // If a valid item is selected, return that.. + unsigned int selected = listview_get_selected ( state->list_view ); state->selected_line = UINT32_MAX; - if ( state->selected < state->filtered_lines ) { - ( state->selected_line ) = state->line_map[state->selected]; + if ( selected < state->filtered_lines ) { + ( state->selected_line ) = state->line_map[selected]; state->retv = MENU_OK; } else { @@ -1619,7 +1192,6 @@ static void rofi_view_handle_keypress ( RofiViewState *state, xkb_stuff *xkb, xc if ( key != XKB_KEY_NoSymbol ) { KeyBindingAction action; - action = abe_find_action ( modstate, key ); if ( rofi_view_trigger_action ( state, action ) ) { return; @@ -1628,7 +1200,6 @@ static void rofi_view_handle_keypress ( RofiViewState *state, xkb_stuff *xkb, xc if ( ( len > 0 ) && ( textbox_append_char ( state->text, pad, len ) ) ) { state->refilter = TRUE; - state->update = TRUE; return; } } @@ -1652,12 +1223,11 @@ static void rofi_view_mainloop_iter ( RofiViewState *state, xcb_generic_event_t if ( config.click_to_exit == TRUE ) { state->mouse_seen = TRUE; } - xcb_motion_notify_event_t *xme = (xcb_motion_notify_event_t *) ev; - if ( state->scrollbar != NULL && - xme->event_x >= state->scrollbar->widget.x && xme->event_x < ( state->scrollbar->widget.x + state->scrollbar->widget.w ) ) { - state->selected = scrollbar_clicked ( state->scrollbar, xme->event_y ); - state->update = TRUE; - } +// xcb_motion_notify_event_t *xme = (xcb_motion_notify_event_t *) ev; + // if ( state->scrollbar != NULL && + // xme->event_x >= state->scrollbar->widget.x && xme->event_x < ( state->scrollbar->widget.x + state->scrollbar->widget.w ) ) { + // state->selected = scrollbar_clicked ( state->scrollbar, xme->event_y ); + // } break; } case XCB_BUTTON_PRESS: @@ -1714,29 +1284,55 @@ static void rofi_view_mainloop_iter ( RofiViewState *state, xcb_generic_event_t rofi_view_update ( state ); } -static void rofi_view_calculate_height ( RofiViewState *state, int rows ) +static int rofi_view_calculate_height ( RofiViewState *state ) { - int element_height = state->line_height * config.element_height + config.line_margin; - state->height = state->top_offset + ( element_height ) * ( rows ); - // If multiple rows, subtrackt last line margin. - if ( rows > 0 ) { - state->height -= config.line_margin; + unsigned int height = 0; + if ( config.menu_lines == 0 || config.fullscreen == TRUE ) { + height = CacheState.mon.h; + return height; } - state->height += state->border; - // Add entry - if ( config.sidebar_mode == TRUE ) { - state->height += state->line_height + 2 * config.line_margin + 2; + if ( state->filtered_lines == 0 && !config.fixed_num_lines ) { + widget_disable ( WIDGET ( state->input_bar_separator ) ); } + else { + widget_enable ( WIDGET ( state->input_bar_separator ) ); + } + height = listview_get_desired_height ( state->list_view ); + height += box_get_fixed_pixels ( state->main_box ); + height += 2 * state->border; + return height; +} - // Sidebar or fullscreen mode. - if ( config.menu_lines == 0 || config.fullscreen ) { - state->height = CacheState.mon.h; +static gboolean rofi_view_modi_clicked_cb ( widget *textbox, G_GNUC_UNUSED xcb_button_press_event_t *xbe, void *udata ) +{ + RofiViewState *state = ( RofiViewState *) udata; + for ( unsigned int i = 0; i < state->num_modi; i++ ) { + if ( WIDGET ( state->modi[i] ) == textbox ) { + state->retv = MENU_QUICK_SWITCH | ( i & MENU_LOWER_MASK ); + state->quit = TRUE; + state->skip_absorb = TRUE; + return TRUE; + } } + return FALSE; +} +// @TODO don't like this construction. +static void rofi_view_listview_mouse_activated_cb ( listview *lv, xcb_button_press_event_t *xce, void *udata ) +{ + RofiViewState *state = (RofiViewState *) udata; + int control = x11_modifier_active ( xce->state, X11MOD_CONTROL ); + state->retv = MENU_OK; + if ( control ) { + state->retv |= MENU_CUSTOM_ACTION; + } + ( state->selected_line ) = state->line_map[listview_get_selected ( lv )]; + // Quit + state->quit = TRUE; + state->skip_absorb = TRUE; } RofiViewState *rofi_view_create ( Mode *sw, const char *input, - char *prompt, const char *message, MenuFlags menu_flags, void ( *finalize )( RofiViewState * ) ) @@ -1752,9 +1348,7 @@ RofiViewState *rofi_view_create ( Mode *sw, state->skip_absorb = FALSE; //We want to filter on the first run. state->refilter = TRUE; - state->update = FALSE; state->rchanged = TRUE; - state->cur_page = -1; state->border = config.padding + config.menu_bw; state->x11_event_loop = rofi_view_mainloop_iter; state->finalize = finalize; @@ -1783,100 +1377,107 @@ RofiViewState *rofi_view_create ( Mode *sw, // Get active monitor size. TICK_N ( "Get active monitor" ); + state->main_box = box_create ( BOX_VERTICAL, + state->border, state->border, + state->width - 2 * state->border, state->height - 2 * state->border ); + box_set_padding ( state->main_box, config.line_margin ); + // we need this at this point so we can get height. - state->line_height = textbox_get_estimated_char_height (); - int element_height = state->line_height * config.element_height; - state->case_indicator = textbox_create ( TB_AUTOWIDTH, ( state->border ), ( state->border ), - 0, state->line_height, NORMAL, "*" ); - state->top_offset = state->border * 1 + state->line_height + 2 + config.line_margin * 2; - // Height of a row. - if ( config.menu_lines == 0 || config.fullscreen ) { - // Autosize it. - int h = CacheState.mon.h - state->top_offset - config.padding; - if ( config.sidebar_mode == TRUE ) { - h -= state->line_height + config.line_margin; - } - int r = MAX ( 1, ( h ) / ( element_height + config.line_margin ) ); - state->menu_lines = r; - // If in this mode, the number of lines are fixed! - config.fixed_num_lines = TRUE; - } - else { - state->menu_lines = config.menu_lines; - } - rofi_view_calculate_rows_columns ( state ); + unsigned int line_height = textbox_get_estimated_char_height (); rofi_view_calculate_window_and_element_width ( state ); + state->input_bar = box_create ( BOX_HORIZONTAL, 0, 0, state->width - state->border, line_height ); + box_set_padding ( state->input_bar, config.line_margin ); + state->input_bar_separator = separator_create ( 2 ); + + if ( ( config.location == WL_EAST_SOUTH || config.location == WL_SOUTH || config.location == WL_SOUTH_WEST ) ) { + box_add ( state->main_box, WIDGET ( state->input_bar_separator ), FALSE, TRUE ); + box_add ( state->main_box, WIDGET ( state->input_bar ), FALSE, TRUE ); + } + else { + box_add ( state->main_box, WIDGET ( state->input_bar ), FALSE, FALSE ); + box_add ( state->main_box, WIDGET ( state->input_bar_separator ), FALSE, FALSE ); + } + + state->case_indicator = textbox_create ( TB_AUTOWIDTH, 0, 0, 0, line_height, NORMAL, "*" ); + box_add ( state->input_bar, WIDGET ( state->case_indicator ), FALSE, TRUE ); + // Prompt box. - state->prompt_tb = textbox_create ( TB_AUTOWIDTH, ( state->border ), ( state->border ), - 0, state->line_height, NORMAL, prompt ); + state->prompt = textbox_create ( TB_AUTOWIDTH, 0, 0, 0, line_height, NORMAL, "" ); + rofi_view_update_prompt ( state ); + box_add ( state->input_bar, WIDGET ( state->prompt ), FALSE, FALSE ); + // Entry box - int entrybox_width = state->width - ( 2 * ( state->border ) ) - textbox_get_width ( state->prompt_tb ) - - textbox_get_width ( state->case_indicator ); TextboxFlags tfl = TB_EDITABLE; tfl |= ( ( menu_flags & MENU_PASSWORD ) == MENU_PASSWORD ) ? TB_PASSWORD : 0; - state->text = textbox_create ( tfl, - ( state->border ) + textbox_get_width ( state->prompt_tb ), ( state->border ), - entrybox_width, state->line_height, NORMAL, input ); + state->text = textbox_create ( tfl, 0, 0, 0, line_height, NORMAL, input ); - // Move indicator to end. - widget_move ( WIDGET ( state->case_indicator ), state->border + textbox_get_width ( state->prompt_tb ) + entrybox_width, - state->border ); + box_add ( state->input_bar, WIDGET ( state->text ), TRUE, FALSE ); textbox_text ( state->case_indicator, get_matching_state () ); - state->message_tb = NULL; if ( message ) { - state->message_tb = textbox_create ( TB_AUTOHEIGHT | TB_MARKUP | TB_WRAP, - ( state->border ), state->top_offset, state->width - ( 2 * ( state->border ) ), - -1, NORMAL, message ); - state->top_offset += textbox_get_height ( state->message_tb ); - state->top_offset += config.line_margin * 2 + 2; + textbox *message_tb = textbox_create ( TB_AUTOHEIGHT | TB_MARKUP | TB_WRAP, 0, 0, + state->width - ( 2 * ( state->border ) ), -1, NORMAL, message ); + box_add ( state->main_box, WIDGET ( message_tb ), FALSE, FALSE ); + box_add ( state->main_box, WIDGET ( separator_create ( 2 ) ), FALSE, FALSE ); } - state->overlay = textbox_create ( TB_AUTOWIDTH, 0, 0, 20, state->line_height, URGENT, "blaat" ); + state->overlay = textbox_create ( TB_AUTOWIDTH, 0, 0, 20, line_height, URGENT, "blaat" ); widget_disable ( WIDGET ( state->overlay ) ); - // filtered list display - state->boxes = g_malloc0_n ( state->max_elements, sizeof ( textbox* ) ); - int y_offset = state->top_offset; - int x_offset = state->border; + state->list_view = listview_create ( update_callback, state, config.element_height ); + // Set configuration + listview_set_multi_select ( state->list_view, ( state->menu_flags & MENU_INDICATOR ) == MENU_INDICATOR ); + listview_set_padding ( state->list_view, config.line_margin ); + listview_set_max_lines ( state->list_view, config.menu_lines ); + listview_set_max_columns ( state->list_view, config.menu_columns ); + listview_set_fixed_num_lines ( state->list_view, config.fixed_num_lines ); + listview_set_hide_scrollbar ( state->list_view, !config.hide_scrollbar ); + listview_set_scrollbar_width ( state->list_view, config.scrollbar_width ); + listview_set_cycle ( state->list_view, config.cycle ); + listview_set_scroll_type ( state->list_view, config.scroll_method ); + listview_set_mouse_activated_cb ( state->list_view, rofi_view_listview_mouse_activated_cb, state ); - for ( unsigned int i = 0; i < state->max_elements; i++ ) { - state->boxes[i] = textbox_create ( ( state->menu_flags & MENU_INDICATOR ) ? TB_INDICATOR : 0, x_offset, y_offset, - state->element_width, element_height, NORMAL, "" ); - } - if ( !config.hide_scrollbar ) { - unsigned int sbw = config.line_margin + config.scrollbar_width; - state->scrollbar = scrollbar_create ( state->width - state->border - sbw, state->top_offset, - sbw, ( state->max_rows - 1 ) * ( element_height + config.line_margin ) + element_height ); + box_add ( state->main_box, WIDGET ( state->list_view ), TRUE, FALSE ); + + // Only enable widget when sidebar is enabled. + if ( config.sidebar_mode ) { + state->sidebar_bar = box_create ( BOX_HORIZONTAL, 0, 0, state->width - 2 * state->border, line_height ); + box_set_padding ( state->sidebar_bar, config.line_margin ); + box_add ( state->main_box, WIDGET ( separator_create ( 2 ) ), FALSE, TRUE ); + box_add ( state->main_box, WIDGET ( state->sidebar_bar ), FALSE, TRUE ); + state->num_modi = rofi_get_num_enabled_modi (); + state->modi = g_malloc0 ( state->num_modi * sizeof ( textbox * ) ); + for ( unsigned int j = 0; j < state->num_modi; j++ ) { + const Mode * mode = rofi_get_mode ( j ); + state->modi[j] = textbox_create ( TB_CENTER, 0, 0, 0, 0, ( mode == state->sw ) ? HIGHLIGHT : NORMAL, + mode_get_display_name ( mode ) ); + box_add ( state->sidebar_bar, WIDGET ( state->modi[j] ), TRUE, FALSE ); + widget_set_clicked_handler ( WIDGET ( state->modi[j] ), rofi_view_modi_clicked_cb, state ); + } + } + + // Height of a row. + if ( config.menu_lines == 0 || config.fullscreen ) { + state->height = CacheState.mon.h; + // Autosize it. + config.fixed_num_lines = TRUE; } - scrollbar_set_max_value ( state->scrollbar, state->num_lines ); // filtered list state->line_map = g_malloc0_n ( state->num_lines, sizeof ( unsigned int ) ); state->distance = (int *) g_malloc0_n ( state->num_lines, sizeof ( int ) ); - rofi_view_calculate_height ( state, state->max_rows ); + state->height = rofi_view_calculate_height ( state ); // Move the window to the correct x,y position. rofi_view_calculate_window_position ( state ); - if ( config.sidebar_mode == TRUE ) { - state->num_modi = rofi_get_num_enabled_modi (); - int width = ( state->width - ( 2 * ( state->border ) + ( state->num_modi - 1 ) * config.line_margin ) ) / state->num_modi; - state->modi = g_malloc0 ( state->num_modi * sizeof ( textbox * ) ); - for ( unsigned int j = 0; j < state->num_modi; j++ ) { - const Mode * mode = rofi_get_mode ( j ); - state->modi[j] = textbox_create ( TB_CENTER, state->border + j * ( width + config.line_margin ), - state->height - state->line_height - state->border, width, state->line_height, - ( mode == state->sw ) ? HIGHLIGHT : NORMAL, mode_get_display_name ( mode ) ); - } - } rofi_view_window_update_size ( state ); - state->selected = 0; + // Update. + //state->selected = 0; - state->quit = FALSE; - state->update = TRUE; + state->quit = FALSE; rofi_view_refilter ( state ); rofi_view_update ( state ); @@ -1885,7 +1486,6 @@ RofiViewState *rofi_view_create ( Mode *sw, if ( xcb->sncontext != NULL ) { sn_launchee_context_complete ( xcb->sncontext ); } - // TODO move resize window into the 'active window' part. return state; } @@ -1893,7 +1493,6 @@ int rofi_view_error_dialog ( const char *msg, int markup ) { RofiViewState *state = __rofi_view_state_create (); state->retv = MENU_CANCEL; - state->update = TRUE; state->border = config.padding + config.menu_bw; state->x11_event_loop = rofi_view_mainloop_iter; state->menu_flags = MENU_ERROR_DIALOG; @@ -1912,15 +1511,17 @@ int rofi_view_error_dialog ( const char *msg, int markup ) } rofi_view_calculate_window_and_element_width ( state ); - state->max_elements = 0; - + state->main_box = box_create ( BOX_VERTICAL, + state->border, state->border, + state->width - 2 * state->border, state->height - 2 * state->border ); state->text = textbox_create ( ( TB_AUTOHEIGHT | TB_WRAP ) + ( ( markup ) ? TB_MARKUP : 0 ), ( state->border ), ( state->border ), ( state->width - ( 2 * ( state->border ) ) ), 1, NORMAL, ( msg != NULL ) ? msg : "" ); - state->line_height = textbox_get_height ( state->text ); + box_add ( state->main_box, WIDGET ( state->text ), TRUE, FALSE ); + unsigned int line_height = textbox_get_height ( state->text ); // resize window vertically to suit - state->height = state->line_height + ( state->border ) * 2; + state->height = line_height + ( state->border ) * 2; // Calculte window position. rofi_view_calculate_window_position ( state ); @@ -1937,7 +1538,6 @@ int rofi_view_error_dialog ( const char *msg, int markup ) // Set it has current window. rofi_view_set_active ( state ); - // TODO move resize window into the 'active window' part. return TRUE; } @@ -2026,13 +1626,30 @@ void rofi_view_set_overlay ( RofiViewState *state, const char *text ) } if ( text == NULL ) { widget_disable ( WIDGET ( state->overlay ) ); - state->update = TRUE; return; } widget_enable ( WIDGET ( state->overlay ) ); textbox_text ( state->overlay, text ); - unsigned int x_offset = state->width - ( 2 * state->border ) - textbox_get_width ( state->case_indicator ); - x_offset -= textbox_get_width ( state->overlay ); + unsigned int x_offset = state->width - ( 2 * state->border ) - widget_get_width ( WIDGET ( state->case_indicator ) ); + x_offset -= widget_get_width ( WIDGET ( state->overlay ) ); widget_move ( WIDGET ( state->overlay ), x_offset, state->border ); - state->update = TRUE; +} + +void rofi_view_switch_mode ( RofiViewState *state, Mode *mode ) +{ + state->sw = mode; + // Update prompt; + if ( state->prompt ) { + rofi_view_update_prompt ( state ); + if ( config.sidebar_mode ) { + for ( unsigned int j = 0; j < state->num_modi; j++ ) { + const Mode * mode = rofi_get_mode ( j ); + textbox_font ( state->modi[j], ( mode == state->sw ) ? HIGHLIGHT : NORMAL ); + } + } + } + rofi_view_restart ( state ); + state->reload = TRUE; + state->refilter = TRUE; + rofi_view_update ( state ); } diff --git a/source/widget.c b/source/widget.c deleted file mode 100644 index 6d697467..00000000 --- a/source/widget.c +++ /dev/null @@ -1,58 +0,0 @@ -#include -#include "widget.h" - -int widget_intersect ( const Widget *widget, int x, int y ) -{ - if ( widget == NULL ) { - return FALSE; - } - - if ( x >= ( widget->x ) && x < ( widget->x + widget->w ) ) { - if ( y >= ( widget->y ) && y < ( widget->y + widget->h ) ) { - return TRUE; - } - } - return FALSE; -} - -void widget_move ( Widget *widget, short x, short y ) -{ - if ( widget != NULL ) { - widget->x = x; - widget->y = y; - } -} - -gboolean widget_enabled ( Widget *widget ) -{ - if ( widget != NULL ) { - return widget->enabled; - } - return FALSE; -} - -void widget_enable ( Widget *widget ) -{ - if ( widget ) { - widget->enabled = TRUE; - } -} -void widget_disable ( Widget *widget ) -{ - if ( widget ) { - widget->enabled = FALSE; - } -} -void widget_draw ( Widget *widget, cairo_t *d ) -{ - // Check if enabled and if draw is implemented. - if ( widget && widget->enabled && widget->draw ) { - widget->draw ( widget, d ); - } -} -void widget_free ( Widget *widget ) -{ - if ( widget ) { - widget->free ( widget ); - } -} diff --git a/source/widgets/box.c b/source/widgets/box.c new file mode 100644 index 00000000..d7fad23e --- /dev/null +++ b/source/widgets/box.c @@ -0,0 +1,291 @@ +/** + * 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/box.h" + +#define LOG_DOMAIN "Widgets.Box" + +struct _box +{ + widget widget; + boxType type; + int max_size; + // Padding between elements + int padding; + + GList *children; +}; + +static void box_update ( widget *wid ); + +static void vert_calculate_size ( box *b ) +{ + int expanding_widgets = 0; + int active_widgets = 0; + b->max_size = 0; + for ( GList *iter = g_list_first ( b->children ); iter != NULL; iter = g_list_next ( iter ) ) { + widget * child = (widget *) iter->data; + if ( !child->enabled ) { + continue; + } + active_widgets++; + if ( child->expand == TRUE ) { + expanding_widgets++; + continue; + } + b->max_size += child->h; + } + b->max_size += MAX ( 0, ( ( active_widgets - 1 ) * b->padding ) ); + if ( b->max_size > b->widget.h ) { + g_log ( LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Widgets to large (height) for box: %d %d", b->max_size, b->widget.h ); + return; + } + if ( active_widgets > 0 ) { + int bottom = b->widget.h; + int top = 0; + double rem = b->widget.h - b->max_size; + int index = 0; + for ( GList *iter = g_list_first ( b->children ); iter != NULL; iter = g_list_next ( iter ) ) { + widget * child = (widget *) iter->data; + if ( child->enabled == FALSE ) { + continue; + } + if ( child->expand == TRUE ) { + // Re-calculate to avoid round issues leaving one pixel left. + int expanding_widgets_size = ( rem ) / ( expanding_widgets - index ); + if ( child->end ) { + bottom -= expanding_widgets_size; + widget_move ( child, child->x, bottom ); + widget_resize ( child, b->widget.w, expanding_widgets_size ); + bottom -= b->padding; + } + else { + widget_move ( child, child->x, top ); + top += expanding_widgets_size; + widget_resize ( child, b->widget.w, expanding_widgets_size ); + top += b->padding; + } + rem -= expanding_widgets_size; + index++; + } + else if ( child->end ) { + bottom -= widget_get_height ( child ); + widget_move ( child, child->x, bottom ); + widget_resize ( child, b->widget.w, child->h ); + bottom -= b->padding; + } + else { + widget_move ( child, child->x, top ); + top += widget_get_height ( child ); + widget_resize ( child, b->widget.w, child->h ); + top += b->padding; + } + } + } +} +static void hori_calculate_size ( box *b ) +{ + int expanding_widgets = 0; + int active_widgets = 0; + b->max_size = 0; + for ( GList *iter = g_list_first ( b->children ); iter != NULL; iter = g_list_next ( iter ) ) { + widget * child = (widget *) iter->data; + if ( !child->enabled ) { + continue; + } + active_widgets++; + if ( child->expand == TRUE ) { + expanding_widgets++; + continue; + } + // Size used by fixed width widgets. + b->max_size += child->w; + } + b->max_size += MAX ( 0, ( ( active_widgets - 1 ) * b->padding ) ); + if ( b->max_size > b->widget.w ) { + g_log ( LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Widgets to large (width) for box: %d %d", b->max_size, b->widget.w ); + return; + } + if ( active_widgets > 0 ) { + int right = b->widget.w; + int left = 0; + double rem = b->widget.w - b->max_size; + int index = 0; + for ( GList *iter = g_list_first ( b->children ); iter != NULL; iter = g_list_next ( iter ) ) { + widget * child = (widget *) iter->data; + if ( child->enabled == FALSE ) { + continue; + } + if ( child->expand == TRUE ) { + // Re-calculate to avoid round issues leaving one pixel left. + int expanding_widgets_size = ( rem ) / ( expanding_widgets - index ); + if ( child->end ) { + right -= expanding_widgets_size; + widget_move ( child, right, child->y ); + widget_resize ( child, expanding_widgets_size, b->widget.h ); + right -= b->padding; + } + else { + widget_move ( child, left, child->y ); + left += expanding_widgets_size; + widget_resize ( child, expanding_widgets_size, b->widget.h ); + left += b->padding; + } + rem -= expanding_widgets_size; + index++; + } + else if ( child->end ) { + right -= widget_get_width ( child ); + widget_move ( child, right, child->y ); + widget_resize ( child, child->w, b->widget.h ); + right -= b->padding; + } + else { + widget_move ( child, left, child->y ); + left += widget_get_width ( child ); + widget_resize ( child, child->w, b->widget.h ); + left += b->padding; + } + } + } +} + +static void box_draw ( widget *wid, cairo_t *draw ) +{ + box *b = (box *) wid; + // Store current state. + cairo_save ( draw ); + // Define a clipmask so we won't draw outside out widget. + cairo_rectangle ( draw, wid->x, wid->y, wid->w, wid->h ); + cairo_clip ( draw ); + // Set new x/y possition. + cairo_translate ( draw, wid->x, wid->y ); + for ( GList *iter = g_list_first ( b->children ); iter != NULL; iter = g_list_next ( iter ) ) { + widget * child = (widget *) iter->data; + widget_draw ( child, draw ); + } + cairo_restore ( draw ); +} + +static void box_free ( widget *wid ) +{ + box *b = (box *) wid; + + for ( GList *iter = g_list_first ( b->children ); iter != NULL; iter = g_list_next ( iter ) ) { + widget * child = (widget *) iter->data; + widget_free ( child ); + } + + g_free ( b ); +} + +void box_add ( box *box, widget *child, gboolean expand, gboolean end ) +{ + child->expand = expand; + child->end = end; + child->parent = WIDGET ( box ); + if ( end ) { + box->children = g_list_prepend ( box->children, (void *) child ); + } + else { + box->children = g_list_append ( box->children, (void *) child ); + } + widget_update ( WIDGET ( box ) ); +} + +static void box_resize ( widget *widget, short w, short h ) +{ + box *b = (box *) widget; + if ( b->widget.w != w || b->widget.h != h ) { + b->widget.w = w; + b->widget.h = h; + widget_update ( widget ); + } +} + +static gboolean box_clicked ( widget *wid, xcb_button_press_event_t *xbe, G_GNUC_UNUSED void *udata ) +{ + box *b = (box *) wid; + for ( GList *iter = g_list_first ( b->children ); iter != NULL; iter = g_list_next ( iter ) ) { + widget * child = (widget *) iter->data; + if ( !child->enabled ) { + continue; + } + if ( widget_intersect ( child, xbe->event_x, xbe->event_y ) ) { + xcb_button_press_event_t rel = *xbe; + rel.event_x -= child->x; + rel.event_y -= child->y; + return widget_clicked ( child, &rel ); + } + } + return FALSE; +} + +box * box_create ( boxType type, short x, short y, short w, short h ) +{ + box *b = g_malloc0 ( sizeof ( box ) ); + b->type = type; + b->widget.x = x; + b->widget.y = y; + b->widget.w = w; + b->widget.h = h; + b->widget.draw = box_draw; + b->widget.free = box_free; + b->widget.resize = box_resize; + b->widget.update = box_update; + b->widget.clicked = box_clicked; + b->widget.enabled = TRUE; + + return b; +} + +static void box_update ( widget *wid ) +{ + box *b = (box *) wid; + switch ( b->type ) + { + case BOX_VERTICAL: + vert_calculate_size ( b ); + break; + case BOX_HORIZONTAL: + default: + hori_calculate_size ( b ); + } +} +int box_get_fixed_pixels ( box *box ) +{ + return box->max_size; +} + +void box_set_padding ( box * box, unsigned int padding ) +{ + if ( box ) { + box->padding = padding; + widget_queue_redraw ( WIDGET ( box ) ); + } +} diff --git a/source/widgets/listview.c b/source/widgets/listview.c new file mode 100644 index 00000000..a5184a18 --- /dev/null +++ b/source/widgets/listview.c @@ -0,0 +1,488 @@ +/** + * MIT/X11 License + * Modified (c) 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 +#include +#include +#include + +struct _listview +{ + widget widget; + // RChanged + // Text needs to be repainted. + unsigned int rchanged; + // Administration + + unsigned int cur_page; + unsigned int last_offset; + unsigned int selected; + + unsigned int element_height; + unsigned int element_width; + unsigned int max_rows; + unsigned int max_elements; + + // + unsigned int cur_columns; + unsigned int req_elements; + unsigned int cur_elements; + + unsigned int padding; + unsigned int menu_lines; + unsigned int menu_columns; + unsigned int fixed_num_lines; + gboolean cycle; + gboolean multi_select; + + ScrollType scroll_type; + + textbox **boxes; + scrollbar *scrollbar; + + listview_update_callback callback; + void *udata; + + xcb_timestamp_t last_click; + listview_mouse_activated_cb mouse_activated; + void *mouse_activated_data; +}; + +static void listview_free ( widget *widget ) +{ + listview *lv = (listview *) widget; + g_free ( lv ); +} +static unsigned int scroll_per_page ( listview * lv ) +{ + int offset = 0; + + // selected row is always visible. + // If selected is visible do not scroll. + if ( ( ( lv->selected - ( lv->last_offset ) ) < ( lv->max_elements ) ) && ( lv->selected >= ( lv->last_offset ) ) ) { + offset = lv->last_offset; + } + else{ + // Do paginating + unsigned int page = ( lv->max_elements > 0 ) ? ( lv->selected / lv->max_elements ) : 0; + offset = page * lv->max_elements; + if ( page != lv->cur_page ) { + lv->cur_page = page; + lv->rchanged = TRUE; + } + // Set the position + scrollbar_set_handle ( lv->scrollbar, page * lv->max_elements ); + } + return offset; +} + +static unsigned int scroll_continious ( listview *lv ) +{ + unsigned int middle = ( lv->max_rows - ( ( lv->max_rows & 1 ) == 0 ) ) / 2; + unsigned int offset = 0; + if ( lv->selected > middle ) { + if ( lv->selected < ( lv->req_elements - ( lv->max_rows - middle ) ) ) { + offset = lv->selected - middle; + } + // Don't go below zero. + else if ( lv->req_elements > lv->max_rows ) { + offset = lv->req_elements - lv->max_rows; + } + } + if ( offset != lv->cur_page ) { + scrollbar_set_handle ( lv->scrollbar, offset ); + lv->cur_page = offset; + lv->rchanged = TRUE; + } + return offset; +} + +static void update_element ( listview *lv, unsigned int tb, unsigned int index, gboolean full ) +{ + // Select drawing mode + TextBoxFontType type = ( index & 1 ) == 0 ? NORMAL : ALT; + type = ( index ) == lv->selected ? HIGHLIGHT : type; + + if ( lv->callback ) { + lv->callback ( lv->boxes[tb], index, lv->udata, type, full ); + } +} + +static void listview_draw ( widget *wid, cairo_t *draw ) +{ + unsigned int offset = 0; + listview *lv = (listview *) wid; + if ( lv->scroll_type == LISTVIEW_SCROLL_CONTINIOUS ) { + offset = scroll_continious ( lv ); + } + else { + offset = scroll_per_page ( lv ); + } + lv->last_offset = offset; + if ( lv->cur_elements > 0 && lv->max_rows > 0 ) { + cairo_save ( draw ); + // Set new x/y possition. + cairo_translate ( draw, wid->x, wid->y ); + unsigned int max = MIN ( lv->cur_elements, lv->req_elements - offset ); + if ( lv->rchanged ) { + unsigned int width = lv->widget.w - lv->padding * ( lv->cur_columns - 1 ); + if ( widget_enabled ( WIDGET ( lv->scrollbar ) ) ) { + width -= lv->padding; + width -= lv->scrollbar->widget.w; + } + unsigned int element_width = ( width ) / lv->cur_columns; + for ( unsigned int i = 0; i < max; i++ ) { + unsigned int ex = ( ( i ) / lv->max_rows ) * ( element_width + lv->padding ); + unsigned int ey = ( ( i ) % lv->max_rows ) * ( lv->element_height + lv->padding ); + textbox_moveresize ( lv->boxes[i], ex, ey, element_width, lv->element_height ); + + update_element ( lv, i, i + offset, TRUE ); + widget_draw ( WIDGET ( lv->boxes[i] ), draw ); + } + lv->rchanged = FALSE; + } + else { + for ( unsigned int i = 0; i < max; i++ ) { + update_element ( lv, i, i + offset, FALSE ); + widget_draw ( WIDGET ( lv->boxes[i] ), draw ); + } + } + widget_draw ( WIDGET ( lv->scrollbar ), draw ); + cairo_restore ( draw ); + } +} + +static void listview_recompute_elements ( listview *lv ) +{ + unsigned int newne = 0; + if ( lv->max_rows == 0 ) { + return; + } + if ( lv->req_elements < lv->max_elements ) { + newne = lv->req_elements; + lv->cur_columns = ( lv->req_elements + ( lv->max_rows - 1 ) ) / lv->max_rows; + } + else { + newne = lv->max_elements; + lv->cur_columns = lv->menu_columns; + } + for ( unsigned int i = newne; i < lv->cur_elements; i++ ) { + widget_free ( WIDGET ( lv->boxes[i] ) ); + } + lv->boxes = g_realloc ( lv->boxes, newne * sizeof ( textbox* ) ); + if ( newne > 0 ) { + for ( unsigned int i = lv->cur_elements; i < newne; i++ ) { + TextboxFlags flags = ( lv->multi_select ) ? TB_INDICATOR : 0; + lv->boxes[i] = textbox_create ( flags, 0, 0, 0, lv->element_height, NORMAL, "" ); + } + } + lv->rchanged = TRUE; + scrollbar_set_handle_length ( lv->scrollbar, lv->cur_columns * lv->max_rows ); + lv->cur_elements = newne; +} + +void listview_set_num_elements ( listview *lv, unsigned int rows ) +{ + lv->req_elements = rows; + listview_set_selected ( lv, lv->selected ); + listview_recompute_elements ( lv ); + scrollbar_set_max_value ( lv->scrollbar, lv->req_elements ); + widget_queue_redraw ( WIDGET ( lv ) ); +} + +unsigned int listview_get_selected ( listview *lv ) +{ + if ( lv != NULL ) { + return lv->selected; + } + return 0; +} + +void listview_set_selected ( listview *lv, unsigned int selected ) +{ + if ( lv && lv->req_elements > 0 ) { + lv->selected = MIN ( selected, lv->req_elements - 1 ); + widget_queue_redraw ( WIDGET ( lv ) ); + } +} + +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 ); + lv->max_rows = ( lv->padding + lv->widget.h ) / ( lv->element_height + lv->padding ); + lv->max_elements = lv->max_rows * lv->menu_columns; + + widget_move ( WIDGET ( lv->scrollbar ), lv->widget.w - lv->scrollbar->widget.w, 0 ); + widget_resize ( WIDGET ( lv->scrollbar ), lv->scrollbar->widget.w, h ); + + listview_recompute_elements ( lv ); + widget_queue_redraw ( wid ); +} + +static gboolean listview_scrollbar_clicked ( widget *sb, xcb_button_press_event_t * xce, void *udata ) +{ + listview *lv = (listview *) udata; + + unsigned int sel = scrollbar_clicked ( (scrollbar *) sb, xce->event_y ); + listview_set_selected ( lv, sel ); + + return TRUE; +} + +static gboolean listview_clicked ( widget *wid, xcb_button_press_event_t *xce, G_GNUC_UNUSED void *udata ) +{ + listview *lv = (listview *) wid; + if ( widget_enabled ( WIDGET ( lv->scrollbar ) ) && widget_intersect ( WIDGET ( lv->scrollbar ), xce->event_x, xce->event_y ) ) { + // Forward to handler of scrollbar. + xcb_button_press_event_t xce2 = *xce; + xce->event_x -= lv->scrollbar->widget.x; + xce->event_y -= lv->scrollbar->widget.y; + return widget_clicked ( WIDGET ( lv->scrollbar ), &xce2 ); + } + // Handle the boxes. + unsigned int max = MIN ( lv->cur_elements, lv->req_elements - lv->last_offset ); + for ( unsigned int i = 0; i < max; i++ ) { + widget *w = WIDGET ( lv->boxes[i] ); + if ( widget_intersect ( w, xce->event_x, xce->event_y ) ) { + if ( ( lv->last_offset + i ) == lv->selected ) { + if ( ( xce->time - lv->last_click ) < 200 ) { + // Somehow signal we accepted item. + lv->mouse_activated ( lv, xce, lv->mouse_activated_data ); + } + } + else { + listview_set_selected ( lv, lv->last_offset + i ); + } + lv->last_click = xce->time; + return TRUE; + } + } + return FALSE; +} + +listview *listview_create ( listview_update_callback cb, void *udata, unsigned int eh ) +{ + listview *lv = g_malloc0 ( sizeof ( listview ) ); + lv->widget.free = listview_free; + lv->widget.resize = listview_resize; + lv->widget.draw = listview_draw; + lv->widget.clicked = listview_clicked; + lv->widget.enabled = TRUE; + + lv->scrollbar = scrollbar_create ( 0, 0, 4, 0 ); + widget_set_clicked_handler ( WIDGET ( lv->scrollbar ), listview_scrollbar_clicked, lv ); + lv->scrollbar->widget.parent = WIDGET ( lv ); + // Calculate height of an element. + lv->element_height = textbox_get_estimated_char_height () * eh; + + lv->callback = cb; + lv->udata = udata; + return lv; +} + +/** + * Navigation commands. + */ + +void listview_nav_up ( listview *lv ) +{ + if ( lv == NULL ) { + return; + } + if ( lv->req_elements == 0 || ( lv->selected == 0 && !lv->cycle ) ) { + return; + } + if ( lv->selected == 0 ) { + lv->selected = lv->req_elements; + } + lv->selected--; + widget_queue_redraw ( WIDGET ( lv ) ); +} +void listview_nav_down ( listview *lv ) +{ + if ( lv == NULL ) { + return; + } + if ( lv->req_elements == 0 || ( lv->selected == ( lv->req_elements - 1 ) && !lv->cycle ) ) { + return; + } + lv->selected = lv->selected < lv->req_elements - 1 ? MIN ( lv->req_elements - 1, lv->selected + 1 ) : 0; + + widget_queue_redraw ( WIDGET ( lv ) ); +} + +void listview_nav_left ( listview *lv ) +{ + if ( lv == NULL ) { + return; + } + if ( lv->selected >= lv->max_rows ) { + lv->selected -= lv->max_rows; + widget_queue_redraw ( WIDGET ( lv ) ); + } +} +void listview_nav_right ( listview *lv ) +{ + if ( lv == NULL ) { + return; + } + if ( ( lv->selected + lv->max_rows ) < lv->req_elements ) { + lv->selected += lv->max_rows; + widget_queue_redraw ( WIDGET ( lv ) ); + } + else if ( lv->selected < ( lv->req_elements - 1 ) ) { + // We do not want to move to last item, UNLESS the last column is only + // partially filled, then we still want to move column and select last entry. + // First check the column we are currently in. + int col = lv->selected / lv->max_rows; + // Check total number of columns. + int ncol = lv->req_elements / lv->max_rows; + // If there is an extra column, move. + if ( col != ncol ) { + lv->selected = lv->req_elements - 1; + widget_queue_redraw ( WIDGET ( lv ) ); + } + } +} + +void listview_nav_page_prev ( listview *lv ) +{ + if ( lv == NULL ) { + return; + } + if ( lv->selected < lv->max_elements ) { + lv->selected = 0; + } + else{ + lv->selected -= ( lv->max_elements ); + } + widget_queue_redraw ( WIDGET ( lv ) ); +} +void listview_nav_page_next ( listview *lv ) +{ + if ( lv == NULL ) { + return; + } + if ( lv->req_elements == 0 ) { + return; + } + lv->selected += ( lv->max_elements ); + if ( lv->selected >= lv->req_elements ) { + lv->selected = lv->req_elements - 1; + } + widget_queue_redraw ( WIDGET ( lv ) ); +} + +unsigned int listview_get_desired_height ( listview *lv ) +{ + if ( lv == NULL ) { + return 0; + } + int h = lv->menu_lines; + if ( !( lv->fixed_num_lines ) ) { + h = MIN ( lv->menu_lines, lv->req_elements ); + } + if ( h == 0 ) { + return 0; + } + return h * lv->element_height + ( h - 1 ) * lv->padding; +} + +/** + * Configure the widget! + */ +void listview_set_padding ( listview *lv, unsigned int padding ) +{ + if ( lv ) { + lv->padding = padding; + } +} +void listview_set_max_lines ( listview *lv, unsigned int lines ) +{ + if ( lv ) { + lv->menu_lines = lines; + } +} +void listview_set_max_columns ( listview *lv, unsigned int columns ) +{ + if ( lv ) { + lv->menu_columns = columns; + } +} + +void listview_set_fixed_num_lines ( listview *lv, gboolean enabled ) +{ + if ( lv ) { + lv->fixed_num_lines = enabled; + } +} +void listview_set_hide_scrollbar ( listview *lv, gboolean enabled ) +{ + if ( lv ) { + if ( enabled ) { + widget_enable ( WIDGET ( lv->scrollbar ) ); + } + else { + widget_disable ( WIDGET ( lv->scrollbar ) ); + } + listview_recompute_elements ( lv ); + } +} +void listview_set_scrollbar_width ( listview *lv, unsigned int width ) +{ + if ( lv ) { + widget_resize ( WIDGET ( lv->scrollbar ), width, widget_get_height ( WIDGET ( lv->scrollbar ) ) ); + } +} + +void listview_set_cycle ( listview *lv, gboolean cycle ) +{ + if ( lv ) { + lv->cycle = cycle; + } +} +void listview_set_scroll_type ( listview *lv, ScrollType type ) +{ + if ( lv ) { + lv->scroll_type = type; + } +} + +void listview_set_mouse_activated_cb ( listview *lv, listview_mouse_activated_cb cb, void *udata ) +{ + if ( lv ) { + lv->mouse_activated = cb; + lv->mouse_activated_data = udata; + } +} +void listview_set_multi_select ( listview *lv, gboolean enable ) +{ + if ( lv ) { + lv->multi_select = enable; + } +} diff --git a/source/scrollbar.c b/source/widgets/scrollbar.c similarity index 83% rename from source/scrollbar.c rename to source/widgets/scrollbar.c index a53b4144..73c217e2 100644 --- a/source/scrollbar.c +++ b/source/widgets/scrollbar.c @@ -25,12 +25,12 @@ #include #include #include -#include "scrollbar.h" +#include "widgets/scrollbar.h" #include "x11-helper.h" #include "settings.h" -static void scrollbar_draw ( Widget *widget, cairo_t *draw ); -static void scrollbar_free ( Widget * ); +static void scrollbar_draw ( widget *, cairo_t * ); +static void scrollbar_free ( widget * ); scrollbar *scrollbar_create ( short x, short y, short w, short h ) { @@ -53,9 +53,9 @@ scrollbar *scrollbar_create ( short x, short y, short w, short h ) return sb; } -static void scrollbar_free ( Widget *widget ) +static void scrollbar_free ( widget *wid ) { - scrollbar *sb = (scrollbar *) widget; + scrollbar *sb = (scrollbar *) wid; g_free ( sb ); } @@ -80,9 +80,9 @@ void scrollbar_set_handle_length ( scrollbar *sb, unsigned int pos_length ) } } -static void scrollbar_draw ( Widget *widget, cairo_t *draw ) +static void scrollbar_draw ( widget *wid, cairo_t *draw ) { - scrollbar *sb = (scrollbar *) widget; + scrollbar *sb = (scrollbar *) wid; // Calculate position and size. const short bh = sb->widget.h - 0; float sec = ( ( bh ) / (float) sb->length ); @@ -97,20 +97,10 @@ static void scrollbar_draw ( Widget *widget, cairo_t *draw ) // Redraw base window color_separator ( draw ); - cairo_rectangle ( draw, sb->widget.x + config.line_margin, sb->widget.y + y, sb->widget.w - config.line_margin, height ); + cairo_rectangle ( draw, sb->widget.x, sb->widget.y + y, sb->widget.w, height ); cairo_fill ( draw ); } -void scrollbar_resize ( scrollbar *sb, int w, int h ) -{ - if ( sb != NULL ) { - if ( h > 0 ) { - sb->widget.h = h; - } - if ( w > 0 ) { - sb->widget.w = w; - } - } -} + unsigned int scrollbar_clicked ( const scrollbar *sb, int y ) { if ( sb != NULL ) { diff --git a/source/widgets/separator.c b/source/widgets/separator.c new file mode 100644 index 00000000..4cc48273 --- /dev/null +++ b/source/widgets/separator.c @@ -0,0 +1,84 @@ +/** + * MIT/X11 License + * Modified (c) 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 +#include +#include "widgets/separator.h" +#include "x11-helper.h" +#include "settings.h" + +/** + * Internal structure for the separator. + */ +struct _separator +{ + widget widget; +}; + +const char *const _separator_style_none = "none"; +const char *const _separator_style_dash = "dash"; +static void separator_draw ( widget *, cairo_t * ); +static void separator_free ( widget * ); + +separator *separator_create ( short h ) +{ + separator *sb = g_malloc0 ( sizeof ( separator ) ); + + sb->widget.x = 0; + sb->widget.y = 0; + sb->widget.w = 1; + sb->widget.h = MAX ( 1, h ); + + sb->widget.draw = separator_draw; + sb->widget.free = separator_free; + + // Enabled by default + sb->widget.enabled = TRUE; + return sb; +} + +static void separator_free ( widget *wid ) +{ + separator *sb = (separator *) wid; + g_free ( sb ); +} + +static void separator_draw ( widget *wid, cairo_t *draw ) +{ + if ( strcmp ( config.separator_style, _separator_style_none ) ) { + cairo_set_line_width ( draw, wid->h ); + color_separator ( draw ); + if ( strcmp ( config.separator_style, _separator_style_dash ) == 0 ) { + const double dashes[1] = { 4 }; + cairo_set_dash ( draw, dashes, 1, 0.0 ); + } + double half = wid->h / 2.0; + cairo_move_to ( draw, wid->x, wid->y + half ); + cairo_line_to ( draw, wid->x + wid->w, wid->y + half ); + cairo_stroke ( draw ); + } +} + diff --git a/source/textbox.c b/source/widgets/textbox.c similarity index 91% rename from source/textbox.c rename to source/widgets/textbox.c index 10d6b044..6e9fbed8 100644 --- a/source/textbox.c +++ b/source/widgets/textbox.c @@ -30,7 +30,7 @@ #include #include #include "settings.h" -#include "textbox.h" +#include "widgets/textbox.h" #include "keyb.h" #include "x11-helper.h" #include "mode.h" @@ -38,8 +38,10 @@ #define DOT_OFFSET 15 -static void textbox_draw ( Widget *, cairo_t * ); -static void textbox_free ( Widget * ); +static void textbox_draw ( widget *, cairo_t * ); +static void textbox_free ( widget * ); +static int textbox_get_width ( widget * ); +static int _textbox_get_height ( widget * ); /** * Font + font color cache. @@ -65,6 +67,7 @@ static gboolean textbox_blink ( gpointer data ) if ( tb->blink < 2 ) { tb->blink = !tb->blink; tb->update = TRUE; + widget_queue_redraw ( WIDGET ( tb ) ); rofi_view_queue_redraw ( ); } else { @@ -73,14 +76,23 @@ static gboolean textbox_blink ( gpointer data ) return TRUE; } +static void textbox_resize ( widget *wid, short w, short h ) +{ + textbox *tb = (textbox *) wid; + textbox_moveresize ( tb, tb->widget.x, tb->widget.y, w, h ); +} + textbox* textbox_create ( TextboxFlags flags, short x, short y, short w, short h, TextBoxFontType tbft, const char *text ) { textbox *tb = g_slice_new0 ( textbox ); - tb->widget.draw = textbox_draw; - tb->widget.free = textbox_free; - tb->flags = flags; + tb->widget.draw = textbox_draw; + tb->widget.free = textbox_free; + tb->widget.resize = textbox_resize; + tb->widget.get_width = textbox_get_width; + tb->widget.get_height = _textbox_get_height; + tb->flags = flags; tb->widget.x = x; tb->widget.y = y; @@ -117,6 +129,9 @@ textbox* textbox_create ( TextboxFlags flags, short x, short y, short w, short h void textbox_font ( textbox *tb, TextBoxFontType tbft ) { TextBoxFontType t = tbft & STATE_MASK; + if ( tb == NULL ) { + return; + } // ACTIVE has priority over URGENT if both set. if ( t == ( URGENT | ACTIVE ) ) { t = ACTIVE; @@ -139,6 +154,7 @@ void textbox_font ( textbox *tb, TextBoxFontType tbft ) } if ( tb->tbft != tbft ) { tb->update = TRUE; + widget_need_redraw ( WIDGET ( tb ) ); } tb->tbft = tbft; } @@ -202,9 +218,11 @@ void textbox_text ( textbox *tb, const char *text ) __textbox_update_pango_text ( tb ); if ( tb->flags & TB_AUTOWIDTH ) { textbox_moveresize ( tb, tb->widget.x, tb->widget.y, tb->widget.w, tb->widget.h ); + widget_update ( WIDGET ( tb ) ); } tb->cursor = MAX ( 0, MIN ( ( int ) g_utf8_strlen ( tb->text, -1 ), tb->cursor ) ); + widget_need_redraw ( WIDGET ( tb ) ); } // within the parent handled auto width/height modes @@ -213,7 +231,8 @@ void textbox_moveresize ( textbox *tb, int x, int y, int w, int h ) unsigned int offset = ( tb->flags & TB_INDICATOR ) ? DOT_OFFSET : 0; if ( tb->flags & TB_AUTOWIDTH ) { pango_layout_set_width ( tb->layout, -1 ); - w = textbox_get_width ( tb ); + unsigned int offset = ( tb->flags & TB_INDICATOR ) ? DOT_OFFSET : 0; + w = textbox_get_font_width ( tb ) + 2 * config.line_padding + offset; } else { // set ellipsize @@ -242,12 +261,13 @@ void textbox_moveresize ( textbox *tb, int x, int y, int w, int h ) // We always want to update this pango_layout_set_width ( tb->layout, PANGO_SCALE * ( tb->widget.w - 2 * config.line_padding - offset ) ); tb->update = TRUE; + widget_need_redraw ( WIDGET ( tb ) ); } // will also unmap the window if still displayed -static void textbox_free ( Widget *widget ) +static void textbox_free ( widget *wid ) { - textbox *tb = (textbox *) widget; + textbox *tb = (textbox *) wid; if ( tb->blink_timeout > 0 ) { g_source_remove ( tb->blink_timeout ); tb->blink_timeout = 0; @@ -356,9 +376,9 @@ static void texbox_update ( textbox *tb ) tb->update = FALSE; } } -static void textbox_draw ( Widget *widget, cairo_t *draw ) +static void textbox_draw ( widget *wid, cairo_t *draw ) { - textbox *tb = (textbox *) widget; + textbox *tb = (textbox *) wid; texbox_update ( tb ); /* Write buffer */ @@ -374,6 +394,7 @@ void textbox_cursor ( textbox *tb, int pos ) int length = ( tb->text == NULL ) ? 0 : g_utf8_strlen ( tb->text, -1 ); tb->cursor = MAX ( 0, MIN ( length, pos ) ); tb->update = TRUE; + widget_need_redraw ( WIDGET ( tb ) ); // Stop blink! tb->blink = 2; } @@ -458,10 +479,12 @@ void textbox_cursor_end ( textbox *tb ) if ( tb->text == NULL ) { tb->cursor = 0; tb->update = TRUE; + widget_need_redraw ( WIDGET ( tb ) ); return; } tb->cursor = ( int ) g_utf8_strlen ( tb->text, -1 ); tb->update = TRUE; + widget_need_redraw ( WIDGET ( tb ) ); // Stop blink! tb->blink = 2; } @@ -718,12 +741,30 @@ void textbox_cleanup ( void ) } } -int textbox_get_width ( const textbox *tb ) +int textbox_get_width ( widget *wid ) { - unsigned int offset = ( tb->flags & TB_INDICATOR ) ? DOT_OFFSET : 0; - return textbox_get_font_width ( tb ) + 2 * config.line_padding + offset; + textbox *tb = (textbox *) wid; + if ( !wid->expand ) { + if ( tb->flags & TB_AUTOWIDTH ) { + unsigned int offset = ( tb->flags & TB_INDICATOR ) ? DOT_OFFSET : 0; + return textbox_get_font_width ( tb ) + 2 * config.line_padding + offset; + } + return tb->widget.w; + } + return tb->widget.w; } +int _textbox_get_height ( widget *wid ) +{ + textbox *tb = (textbox *) wid; + if ( !wid->expand ) { + if ( tb->flags & TB_AUTOHEIGHT ) { + return textbox_get_height ( tb ); + } + return tb->widget.h; + } + return tb->widget.h; +} int textbox_get_height ( const textbox *tb ) { return textbox_get_font_height ( tb ) + 2 * config.line_padding; diff --git a/source/widgets/widget.c b/source/widgets/widget.c new file mode 100644 index 00000000..0c272eab --- /dev/null +++ b/source/widgets/widget.c @@ -0,0 +1,142 @@ +#include +#include "widgets/widget.h" + +int widget_intersect ( const widget *widget, int x, int y ) +{ + if ( widget == NULL ) { + return FALSE; + } + + if ( x >= ( widget->x ) && x < ( widget->x + widget->w ) ) { + if ( y >= ( widget->y ) && y < ( widget->y + widget->h ) ) { + return TRUE; + } + } + return FALSE; +} + +void widget_resize ( widget *widget, short w, short h ) +{ + if ( widget != NULL ) { + if ( widget->resize != NULL ) { + widget->resize ( widget, w, h ); + } + else { + widget->w = w; + widget->h = h; + } + } +} +void widget_move ( widget *widget, short x, short y ) +{ + if ( widget != NULL ) { + widget->x = x; + widget->y = y; + } +} + +gboolean widget_enabled ( widget *widget ) +{ + if ( widget != NULL ) { + return widget->enabled; + } + return FALSE; +} + +void widget_enable ( widget *widget ) +{ + if ( widget && !widget->enabled ) { + widget->enabled = TRUE; + widget_update ( widget ); + } +} +void widget_disable ( widget *widget ) +{ + if ( widget && widget->enabled ) { + widget->enabled = FALSE; + widget_update ( widget ); + } +} +void widget_draw ( widget *widget, cairo_t *d ) +{ + // Check if enabled and if draw is implemented. + if ( widget && widget->enabled && widget->draw ) { + widget->draw ( widget, d ); + widget->need_redraw = FALSE; + } +} +void widget_free ( widget *widget ) +{ + if ( widget ) { + widget->free ( widget ); + } +} + +int widget_get_height ( widget *widget ) +{ + if ( widget ) { + if ( widget->get_height ) { + return widget->get_height ( widget ); + } + return widget->h; + } + return 0; +} +int widget_get_width ( widget *widget ) +{ + if ( widget ) { + if ( widget->get_width ) { + return widget->get_width ( widget ); + } + return widget->w; + } + return 0; +} + +void widget_update ( widget *widget ) +{ + // When (desired )size of widget changes. + if ( widget ) { + if ( widget->update ) { + widget->update ( widget ); + } + // Recurse back. + if ( widget->parent && widget->parent->update ) { + widget->parent->update ( widget->parent ); + } + } +} + +void widget_queue_redraw ( widget *wid ) +{ + if ( wid ) { + widget *iter = wid; + // Find toplevel widget. + while ( iter->parent != NULL ) { + iter = iter->parent; + } + iter->need_redraw = TRUE; + } +} + +gboolean widget_need_redraw ( widget *wid ) +{ + if ( wid ) { + return wid->need_redraw; + } + return FALSE; +} +gboolean widget_clicked ( widget *wid, xcb_button_press_event_t *xbe ) +{ + if ( wid && wid->clicked ) { + return wid->clicked ( wid, xbe, wid->clicked_cb_data ); + } + return FALSE; +} +void widget_set_clicked_handler ( widget *wid, widget_clicked_cb cb, void *udata ) +{ + if ( wid ) { + wid->clicked = cb; + wid->clicked_cb_data = udata; + } +} diff --git a/test/textbox-test.c b/test/textbox-test.c index cb679695..5383e8e9 100644 --- a/test/textbox-test.c +++ b/test/textbox-test.c @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include #include #include "settings.h" @@ -70,10 +70,10 @@ int main ( G_GNUC_UNUSED int argc, G_GNUC_UNUSED char **argv ) textbox_cursor_end ( box ); TASSERT ( box->cursor == 6 ); - TASSERT ( textbox_get_width ( box ) > 0 ); + TASSERT ( widget_get_width ( WIDGET ( box ) ) > 0 ); TASSERT ( textbox_get_height ( box ) > 0 ); - TASSERT ( textbox_get_width ( box ) >= textbox_get_font_width ( box ) ); + TASSERT ( widget_get_width ( WIDGET ( box ) ) >= textbox_get_font_width ( box ) ); TASSERT ( textbox_get_height ( box ) >= textbox_get_font_height ( box ) ); TASSERT ( textbox_get_estimated_char_width ( ) > 0 ); diff --git a/test/widget-test.c b/test/widget-test.c index c49ec690..d500c414 100644 --- a/test/widget-test.c +++ b/test/widget-test.c @@ -5,7 +5,7 @@ #include #include #include -#include +#include unsigned int test =0; #define TASSERT( a ) { \ assert ( a ); \ @@ -16,7 +16,7 @@ unsigned int test =0; int main ( G_GNUC_UNUSED int argc, G_GNUC_UNUSED char **argv ) { // box 20 by 40 - Widget widget = { 10,10,20,40 }; + widget widget = { 10,10,20,40 }; // Left of box TASSERT ( widget_intersect ( &widget, 0, 0) == 0 );