From c23df70aebf9d6926303b4ddb8d099a4d285949f Mon Sep 17 00:00:00 2001 From: Dave Davenport Date: Thu, 25 May 2017 23:41:15 +0200 Subject: [PATCH] Allow interface to be dynamically changed. --- include/theme.h | 57 +++++++------ lexer/theme-lexer.l | 24 +++++- lexer/theme-parser.y | 21 ++++- source/theme.c | 22 +++++ source/view.c | 186 ++++++++++++++++++++++++++++--------------- source/widgets/box.c | 2 +- 6 files changed, 219 insertions(+), 93 deletions(-) diff --git a/include/theme.h b/include/theme.h index 2ebd187c..bc200953 100644 --- a/include/theme.h +++ b/include/theme.h @@ -118,6 +118,8 @@ typedef enum P_POSITION, /** Highlight */ P_HIGHLIGHT, + /** List */ + P_LIST, } PropertyType; /** @@ -156,6 +158,34 @@ typedef struct /** Color */ ThemeColor color; } ThemeHighlight; + +typedef union { + /** integer */ + int i; + /** Double */ + double f; + /** String */ + char *s; + /** boolean */ + gboolean b; + /** Color */ + ThemeColor color; + /** Padding */ + Padding padding; + /** Reference */ + struct + { + /** Name */ + char *name; + /** Cached looked up ref */ + struct Property *ref; + } link; + /** Highlight Style */ + ThemeHighlight highlight; + /** List */ + GList *list; +} PropertyValue; + /** * Property structure. */ @@ -166,31 +196,7 @@ typedef struct Property /** Type of property. */ PropertyType type; /** Value */ - union - { - /** integer */ - int i; - /** Double */ - double f; - /** String */ - char *s; - /** boolean */ - gboolean b; - /** Color */ - ThemeColor color; - /** Padding */ - Padding padding; - /** Reference */ - struct - { - /** Name */ - char *name; - /** Cached looked up ref */ - struct Property *ref; - } link; - /** Highlight Style */ - ThemeHighlight highlight; - } value; + PropertyValue value; } Property; /** * ThemeWidget. @@ -445,6 +451,7 @@ ThemeWidget *rofi_theme_find_widget ( const char *name, const char *state, gbool */ Property *rofi_theme_find_property ( ThemeWidget *widget, PropertyType type, const char *property, gboolean exact ); +GList *rofi_theme_get_list ( const widget *widget, const char * property, const char *defaults); /** * Checks if a theme is set, or is empty. * @returns TRUE when empty. diff --git a/lexer/theme-lexer.l b/lexer/theme-lexer.l index 80ee6584..826fff85 100644 --- a/lexer/theme-lexer.l +++ b/lexer/theme-lexer.l @@ -211,6 +211,9 @@ S_T_PARENT_RIGHT \) COMMA , FORWARD_SLASH \/ +LIST_OPEN \[ +LIST_CLOSE \] + LS_DASH "dash" LS_SOLID "solid" @@ -220,6 +223,7 @@ CONFIGURATION "configuration" %x INCLUDE %x PROPERTIES +%x PROPERTIES_LIST %x NAMESTR %x SECTION %x DEFAULTS @@ -382,7 +386,7 @@ if ( queue == NULL ){ /* After Namestr/Classstr we want to go to state str, then to { */ {WHITESPACE}+ ; // ignore all whitespace -{WHITESPACE}+ ; // ignore all whitespace +{WHITESPACE}+ ; // ignore all whitespace
":" { g_queue_push_head ( queue, GINT_TO_POINTER (YY_START) ); BEGIN(PROPERTIES); return T_PSEP; } ";" { BEGIN(GPOINTER_TO_INT ( g_queue_pop_head ( queue ))); return T_PCLOSE;} @@ -448,7 +452,16 @@ if ( queue == NULL ){ /* Fluff */ {S_T_PARENT_LEFT} { return T_PARENT_LEFT; } {S_T_PARENT_RIGHT} { return T_PARENT_RIGHT; } -{COMMA} { return T_COMMA; } +{COMMA} { return T_COMMA; } +{LIST_OPEN} { + g_queue_push_head ( queue, GINT_TO_POINTER (YY_START) ); + BEGIN(PROPERTIES_LIST); + return T_LIST_OPEN; +} +{LIST_CLOSE} { + BEGIN(GPOINTER_TO_INT(g_queue_pop_head ( queue ))); + return T_LIST_CLOSE; +} {FORWARD_SLASH} { return T_FORWARD_SLASH; } /* Position */ {CENTER} { return T_POS_CENTER; } @@ -521,7 +534,12 @@ if ( queue == NULL ){
. { return T_ERROR_SECTION; } -. { +{WORD} { + yylval->sval = g_strdup(yytext); + return T_ELEMENT; +} + +. { return T_ERROR_PROPERTY; } . { diff --git a/lexer/theme-parser.y b/lexer/theme-parser.y index d66b7cc4..fd290e85 100644 --- a/lexer/theme-parser.y +++ b/lexer/theme-parser.y @@ -141,7 +141,7 @@ static ThemeColor hwb_to_rgb ( double h, double w, double b) WindowLocation wloc; ThemeColor colorval; ThemeWidget *theme; - GList *name_path; + GList *list; Property *property; GHashTable *property_list; Distance distance; @@ -163,6 +163,7 @@ static ThemeColor hwb_to_rgb ( double h, double w, double b) %token T_BOOLEAN "Boolean value (true or false)" %token T_COLOR "Hexidecimal color value" %token T_LINK "Reference" +%token T_ELEMENT "Name of element" %token T_POS_CENTER "Center" %token T_POS_EAST "East" %token T_POS_WEST "West" @@ -198,6 +199,8 @@ static ThemeColor hwb_to_rgb ( double h, double w, double b) %token T_OPTIONAL_COMMA "Optional comma separator (',')" %token T_FORWARD_SLASH "forward slash ('/')" %token T_PERCENT "Percent sign ('%')" +%token T_LIST_OPEN "List open ('[')" +%token T_LIST_CLOSE "List close (']')" %token T_BOPEN "bracket open ('{')" %token T_BCLOSE "bracket close ('}')" @@ -213,7 +216,7 @@ static ThemeColor hwb_to_rgb ( double h, double w, double b) %type t_entry %type t_entry_list -%type t_entry_name_path +%type t_entry_name_path %type t_property %type t_property_list %type t_property_list_optional @@ -232,6 +235,7 @@ static ThemeColor hwb_to_rgb ( double h, double w, double b) %type t_property_highlight_styles %type t_property_highlight_style %type t_property_line_style +%type t_property_element_list %start t_entry_list %% @@ -363,6 +367,19 @@ t_property $$->name = $1; $$->value.color = $3; } +| t_property_name T_PSEP T_LIST_OPEN t_property_element_list T_LIST_CLOSE T_PCLOSE { + $$ = rofi_theme_property_create ( P_LIST ); + $$->name = $1; + $$->value.list = $4; +} +; + +/** List of elements */ +t_property_element_list +: T_ELEMENT { $$ = g_list_append ( NULL, $1); } +| t_property_element_list T_COMMA T_ELEMENT { + $$ = g_list_append ( $1, $3 ); +} ; /** diff --git a/source/theme.c b/source/theme.c index 468e1a8c..03837d59 100644 --- a/source/theme.c +++ b/source/theme.c @@ -591,6 +591,28 @@ Padding rofi_theme_get_padding ( const widget *widget, const char *property, Pad g_debug ( "Theme entry: #%s %s property %s unset.", widget->name, widget->state ? widget->state : "", property ); return pad; } + +GList *rofi_theme_get_list ( const widget *widget, const char * property, const char *defaults ) +{ + ThemeWidget *wid2 = rofi_theme_find_widget ( widget->name, widget->state, FALSE ); + Property *p = rofi_theme_find_property ( wid2, P_LIST, property, TRUE); + if ( p ) { + if ( p->type == P_LIST ){ + return g_list_copy_deep ( p->value.list, g_strdup, NULL ); + } + } + char **r = defaults?g_strsplit (defaults, ",",0):NULL; + if ( r ){ + GList *l = NULL; + for ( int i =0; r[i] != NULL; i++){ + l = g_list_append(l, r[i]); + } + g_free(r); + return l; + } + return NULL; +} + ThemeHighlight rofi_theme_get_highlight ( widget *widget, const char *property, ThemeHighlight th ) { ThemeWidget *wid = rofi_theme_find_widget ( widget->name, widget->state, FALSE ); diff --git a/source/view.c b/source/view.c index 6c44c289..0e6375b8 100644 --- a/source/view.c +++ b/source/view.c @@ -1578,6 +1578,121 @@ static void rofi_view_listview_mouse_activated_cb ( listview *lv, xcb_button_pre state->skip_absorb = TRUE; } + +static void rofi_view_add_widget ( RofiViewState *state, widget *parent_widget, const char *parent, const char *name ) +{ + char *defaults = NULL; + widget *wid = NULL; + char *str= g_strjoin ( "." , parent, name, NULL ); + char *strbox= g_strjoin ( "." , str, "box",NULL ); + + /** + * MAINBOX + */ + if ( strcmp ( name, "mainbox") == 0 ){ + state->main_box = box_create ( strbox, BOX_VERTICAL ); + container_add ( (container *)parent_widget, WIDGET ( state->main_box ) ); + wid = WIDGET ( state->main_box ); + defaults = "inputbar,message,listview"; + } + /** + * INPUTBAR + */ + else if ( strcmp ( name, "inputbar" ) == 0 ){ + state->input_bar = box_create ( strbox, BOX_HORIZONTAL ); + wid = WIDGET( state->input_bar ); + defaults = "prompt,entry,case-indicator"; + + box_add ( (box *)parent_widget, WIDGET ( state->input_bar ), FALSE, 0 ); + } + /** + * PROMPT + */ + else if ( strcmp ( name, "prompt" ) == 0 ){ + // Prompt box. + state->prompt = textbox_create ( str, TB_AUTOWIDTH | TB_AUTOHEIGHT, NORMAL, "" ); + rofi_view_update_prompt ( state ); + box_add ( (box *)parent_widget, WIDGET ( state->prompt ), FALSE, 1 ); + defaults = NULL; + } + /** + * CASE INDICATOR + */ + else if ( strcmp ( name, "case-indicator") == 0 ){ + state->case_indicator = textbox_create ( str, TB_AUTOWIDTH | TB_AUTOHEIGHT, NORMAL, "*" ); + // Add small separator between case indicator and text box. + box_add ( (box *)parent_widget, WIDGET ( state->case_indicator ), FALSE, 3 ); + textbox_text ( state->case_indicator, get_matching_state () ); + } + /** + * ENTRY BOX + */ + else if ( strcmp ( name, "entry" ) == 0 ){ + // Entry box + TextboxFlags tfl = TB_EDITABLE; + tfl |= ( ( state->menu_flags & MENU_PASSWORD ) == MENU_PASSWORD ) ? TB_PASSWORD : 0; + state->text = textbox_create ( str, tfl | TB_AUTOHEIGHT, NORMAL, NULL); + box_add ( (box*)parent_widget, WIDGET ( state->text ), TRUE, 2 ); + } + /** + * MESSAGE + */ + else if ( strcmp ( name, "message") == 0 ){ + char *strmsg= g_strjoin ( "." , str, "textbox",NULL ); + state->mesg_box = container_create ( strbox ); + state->mesg_tb = textbox_create ( strmsg, TB_AUTOHEIGHT | TB_MARKUP | TB_WRAP, NORMAL, NULL ); + container_add ( state->mesg_box, WIDGET ( state->mesg_tb ) ); + rofi_view_reload_message_bar ( state ); + box_add ( (box*)parent_widget, WIDGET ( state->mesg_box ), FALSE, 2 ); + g_free(strmsg); + } + /** + * LISTVIEW + */ + else if ( strcmp ( name, "listview" ) == 0 ) { + state->list_view = listview_create ( str, update_callback, state, config.element_height, 0); + box_add ( (box*)parent_widget, WIDGET ( state->list_view ), TRUE, 3 ); + // Set configuration + listview_set_multi_select ( state->list_view, ( state->menu_flags & MENU_INDICATOR ) == MENU_INDICATOR ); + 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 ); + + int lines = rofi_theme_get_integer ( WIDGET ( state->list_view ), "lines", config.menu_lines ); + listview_set_num_lines ( state->list_view, lines ); + listview_set_max_lines ( state->list_view, state->num_lines ); + } + /** + * SIDEBAR + */ + else if ( strcmp( name, "sidebar" ) == 0 ) { + state->sidebar_bar = box_create ( strbox, BOX_HORIZONTAL ); + box_add ( (box*)parent_widget, WIDGET ( state->sidebar_bar ), FALSE, 10 ); + state->num_modi = rofi_get_num_enabled_modi (); + state->modi = g_malloc0 ( state->num_modi * sizeof ( textbox * ) ); + char *strbutton= g_strjoin ( "." , str, "button",NULL ); + for ( unsigned int j = 0; j < state->num_modi; j++ ) { + const Mode * mode = rofi_get_mode ( j ); + state->modi[j] = textbox_create ( strbutton, TB_CENTER | TB_AUTOHEIGHT, ( mode == state->sw ) ? HIGHLIGHT : NORMAL, + mode_get_display_name ( mode ) ); + box_add ( state->sidebar_bar, WIDGET ( state->modi[j] ), TRUE, j ); + widget_set_clicked_handler ( WIDGET ( state->modi[j] ), rofi_view_modi_clicked_cb, state ); + } + g_free(strbutton); + } + else { + g_error("The widget %s does not exists. Invalid layout.", name); + } + if ( wid ) { + GList *list = rofi_theme_get_list ( wid, "children",defaults); + for ( const GList *iter = list; iter != NULL; iter = g_list_next ( iter )){ + rofi_view_add_widget ( state, wid, str, (const char *)iter->data ); + } + g_list_free_full ( list, g_free ); + } + g_free(strbox); + g_free(str); +} + RofiViewState *rofi_view_create ( Mode *sw, const char *input, MenuFlags menu_flags, @@ -1605,77 +1720,24 @@ RofiViewState *rofi_view_create ( Mode *sw, // Get active monitor size. TICK_N ( "Get active monitor" ); + state->main_window = container_create ( "window.box" ); - state->main_box = box_create ( "window.mainbox.box", BOX_VERTICAL ); - container_add ( state->main_window, WIDGET ( state->main_box ) ); + // Get children. + GList *list = rofi_theme_get_list ( WIDGET(state->main_window), "children", "mainbox"); + for ( const GList *iter = list; iter != NULL; iter = g_list_next ( iter )){ + rofi_view_add_widget ( state, WIDGET(state->main_window), "window", (const char *)iter->data ); - state->input_bar = box_create ( "window.mainbox.inputbar.box", BOX_HORIZONTAL ); - - // Only enable widget when sidebar is enabled. - if ( config.sidebar_mode ) { - state->sidebar_bar = box_create ( "window.mainbox.sidebar.box", BOX_HORIZONTAL ); - box_add ( state->main_box, WIDGET ( state->sidebar_bar ), FALSE, 10 ); - 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 ( "window.mainbox.sidebar.button", TB_CENTER | TB_AUTOHEIGHT, ( mode == state->sw ) ? HIGHLIGHT : NORMAL, - mode_get_display_name ( mode ) ); - box_add ( state->sidebar_bar, WIDGET ( state->modi[j] ), TRUE, j ); - widget_set_clicked_handler ( WIDGET ( state->modi[j] ), rofi_view_modi_clicked_cb, state ); - } } + g_list_free_full ( list, g_free ); - int location = rofi_theme_get_position ( WIDGET ( state->main_window ), "location", config.location ); - int end = ( location == WL_SOUTH_EAST || location == WL_SOUTH || location == WL_SOUTH_WEST ); - box_add ( state->main_box, WIDGET ( state->input_bar ), FALSE, end ? 9 : 0 ); - - state->case_indicator = textbox_create ( "window.mainbox.inputbar.case-indicator", TB_AUTOWIDTH | TB_AUTOHEIGHT, NORMAL, "*" ); - // Add small separator between case indicator and text box. - box_add ( state->input_bar, WIDGET ( state->case_indicator ), FALSE, 3 ); - - // Prompt box. - state->prompt = textbox_create ( "window.mainbox.inputbar.prompt", TB_AUTOWIDTH | TB_AUTOHEIGHT, NORMAL, "" ); - rofi_view_update_prompt ( state ); - box_add ( state->input_bar, WIDGET ( state->prompt ), FALSE, 1 ); - - // Entry box - TextboxFlags tfl = TB_EDITABLE; - tfl |= ( ( menu_flags & MENU_PASSWORD ) == MENU_PASSWORD ) ? TB_PASSWORD : 0; - state->text = textbox_create ( "window.mainbox.inputbar.entry", tfl | TB_AUTOHEIGHT, NORMAL, input ); - - box_add ( state->input_bar, WIDGET ( state->text ), TRUE, 2 ); - - textbox_text ( state->case_indicator, get_matching_state () ); - state->mesg_box = container_create ( "window.mainbox.message.box" ); - state->mesg_tb = textbox_create ( "window.mainbox.message.textbox", TB_AUTOHEIGHT | TB_MARKUP | TB_WRAP, NORMAL, NULL ); - container_add ( state->mesg_box, WIDGET ( state->mesg_tb ) ); - rofi_view_reload_message_bar ( state ); - box_add ( state->main_box, WIDGET ( state->mesg_box ), FALSE, end ? 8 : 2 ); + if ( state->text && input) { + textbox_text ( state->text, input ); + } state->overlay = textbox_create ( "window.overlay", TB_AUTOWIDTH | TB_AUTOHEIGHT, URGENT, "blaat" ); state->overlay->widget.parent = WIDGET ( state->main_window ); widget_disable ( WIDGET ( state->overlay ) ); - state->list_view = listview_create ( "window.mainbox.listview", update_callback, state, config.element_height, end ); - // Set configuration - listview_set_multi_select ( state->list_view, ( state->menu_flags & MENU_INDICATOR ) == MENU_INDICATOR ); - 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 ); - - int lines = rofi_theme_get_integer ( WIDGET ( state->list_view ), "lines", config.menu_lines ); - listview_set_num_lines ( state->list_view, lines ); - listview_set_max_lines ( state->list_view, state->num_lines ); - - if ( rofi_theme_get_boolean ( WIDGET ( state->main_window ), "listview-in-inputbar", FALSE)){ - box_add ( state->input_bar, WIDGET ( state->list_view ), TRUE, 3 ); - - Distance d = rofi_theme_get_distance_exact ( WIDGET ( state->text ) , "width", 150); - state->text->widget.expand = FALSE; - widget_resize ( WIDGET( state->text ), distance_get_pixel ( d, ORIENTATION_HORIZONTAL ), -1); - } else { - box_add ( state->main_box, WIDGET ( state->list_view ), TRUE, 3 ); - } // filtered list state->line_map = g_malloc0_n ( state->num_lines, sizeof ( unsigned int ) ); diff --git a/source/widgets/box.c b/source/widgets/box.c index faeba226..409fa0b1 100644 --- a/source/widgets/box.c +++ b/source/widgets/box.c @@ -263,7 +263,7 @@ void box_add ( box *box, widget *child, gboolean expand, int index ) child->index = rofi_theme_get_integer_exact ( child, "index", index ); child->parent = WIDGET ( box ); box->children = g_list_append ( box->children, (void *) child ); - box->children = g_list_sort ( box->children, box_sort_children ); + //box->children = g_list_sort ( box->children, box_sort_children ); widget_update ( WIDGET ( box ) ); }