diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index faefe23e..09a04983 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -4,7 +4,7 @@ before_script: - add-apt-repository -y 'deb http://debian.jpleau.ca/ jessie-backports main contrib non-free' - apt-get update -qq - apt-get install --force-yes -y autoconf automake make libx11-dev libpango1.0-dev libcairo2-dev libstartup-notification0-dev libxcb-icccm4-dev libxcb-util0-dev libxcb-xinerama0-dev libxcb-xkb-dev libx11-xcb-dev - - apt-get install --force-yes -y libxcb1-dev xvfb discount xdotool fluxbox libxkbcommon-dev libxkbcommon-x11-dev libxcb-ewmh-dev xutils-dev libtool lcov libxcb-randr0-dev doxygen python + - apt-get install --force-yes -y libxcb1-dev xvfb discount xdotool fluxbox libxkbcommon-dev libxkbcommon-x11-dev libxcb-ewmh-dev xutils-dev libtool lcov libxcb-randr0-dev doxygen python flex bison - git clone --recursive https://github.com/Airblader/xcb-util-xrm.git - cd xcb-util-xrm - ./autogen.sh --prefix=/usr diff --git a/INSTALL.md b/INSTALL.md index 1097c9e2..f7fa0383 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -9,6 +9,8 @@ * autoconf * automake (1.11.3 or up) * pkg-config +* flex +* bison * Developer packages of the external libraries ### External libraries diff --git a/Makefile.am b/Makefile.am index 9f52c3d8..14fa6403 100644 --- a/Makefile.am +++ b/Makefile.am @@ -3,9 +3,18 @@ AUTOMAKE_OPTIONS = 1.11.3 ACLOCAL_AMFLAGS = -I libgwater ${ACLOCAL_FLAGS} +AM_YFLAGS = -d + noinst_LIBRARIES = include $(top_srcdir)/libgwater-xcb-nolibtool.mk + +BUILT_SOURCES=\ + lexer/theme-parser.h\ + lexer/theme-parser.c\ + lexer/theme-lexer.c + + ## # Rofi the program ## @@ -27,12 +36,13 @@ rofi_SOURCES=\ source/helper.c\ source/timings.c\ source/history.c\ + source/theme.c\ source/widgets/box.c\ + source/widgets/container.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\ @@ -43,6 +53,8 @@ rofi_SOURCES=\ source/dialogs/window.c\ source/dialogs/script.c\ source/dialogs/help-keys.c\ + lexer/theme-parser.y\ + lexer/theme-lexer.l\ include/xcb.h\ include/xcb-internal.h\ include/rofi.h\ @@ -55,13 +67,14 @@ rofi_SOURCES=\ include/helper.h\ include/timings.h\ include/history.h\ + include/theme.h\ include/widgets/box.h\ + include/widgets/container.h\ include/widgets/widget.h\ include/widgets/widget-internal.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\ @@ -88,7 +101,8 @@ rofi_CFLAGS=\ -I$(top_srcdir)/config/\ -I$(top_builddir)/\ -Werror=missing-prototypes\ - -DSYSCONFDIR=\"$(sysconfdir)\" + -DSYSCONFDIR=\"$(sysconfdir)\"\ + -DTHEME_CONVERTER rofi_LDADD=\ $(glib_LIBS)\ @@ -259,6 +273,13 @@ widget_test_LDADD=$(textbox_test_LDADD) widget_test_CFLAGS=$(textbox_test_CFLAGS) widget_test_SOURCES=\ source/widgets/widget.c\ + source/widgets/textbox.c\ + source/theme.c\ + source/helper.c\ + source/x11-helper.c\ + config/config.c\ + lexer/theme-parser.y\ + lexer/theme-lexer.l\ test/widget-test.c box_test_LDADD=$(textbox_test_LDADD) @@ -266,6 +287,10 @@ box_test_CFLAGS=$(textbox_test_CFLAGS) box_test_SOURCES=\ source/widgets/widget.c\ source/widgets/box.c\ + lexer/theme-parser.y\ + lexer/theme-lexer.l\ + source/theme.c\ + include/theme.h\ test/box-test.c scrollbar_test_LDADD=$(textbox_test_LDADD) @@ -273,11 +298,20 @@ scrollbar_test_CFLAGS=$(textbox_test_CFLAGS) scrollbar_test_SOURCES=\ source/widgets/widget.c\ source/widgets/scrollbar.c\ + lexer/theme-parser.y\ + lexer/theme-lexer.l\ + source/theme.c\ + include/theme.h\ test/scrollbar-test.c textbox_test_SOURCES=\ source/widgets/widget.c\ source/widgets/textbox.c\ + lexer/theme-parser.y\ + lexer/theme-lexer.l\ + source/theme.c\ + source/helper.c\ + source/x11-helper.c\ config/config.c\ include/keyb.h\ include/rofi.h\ diff --git a/config/config.c b/config/config.c index 1d8a12b8..7dc6e215 100644 --- a/config/config.c +++ b/config/config.c @@ -128,4 +128,5 @@ Settings config = { .window_format = "{w} {c} {t}", .click_to_exit = TRUE, .show_match = TRUE, + .theme = NULL, }; diff --git a/configure.ac b/configure.ac index 4f26493b..3b430991 100644 --- a/configure.ac +++ b/configure.ac @@ -3,6 +3,10 @@ AC_INIT([rofi], [1.3.1], [https://github.com/DaveDavenport/rofi/],[],[https://fo AC_CONFIG_SRCDIR([source/rofi.c]) AC_CONFIG_HEADER([config.h]) + +AC_PROG_LEX +AC_PROG_YACC + dnl --------------------------------------------------------------------- dnl Setup automake to be silent and in foreign mode. dnl We want xz distribution diff --git a/doc/help-output.txt b/doc/help-output.txt index 6706dd25..90e11b20 100644 --- a/doc/help-output.txt +++ b/doc/help-output.txt @@ -135,6 +135,8 @@ Global options: True (Default) -[no-]show-match Indicate how it match by underlining it. True (Default) + -theme [string] New style theme file + (unset) (Default) -pid [string] Pidfile location /tmp/rofi.pid (File) -kb-primary-paste [string] Paste primary selection diff --git a/doc/test_xr.txt b/doc/test_xr.txt index cbe76a61..13526a02 100644 --- a/doc/test_xr.txt +++ b/doc/test_xr.txt @@ -98,6 +98,8 @@ rofi.window-format: w c t ! rofi.click-to-exit: true ! "Indicate how it match by underlining it." Set from: Default ! rofi.show-match: true +! "New style theme file" Set from: Default +! rofi.theme: ! "Pidfile location" Set from: File rofi.pid: /tmp/rofi.pid ! "Paste primary selection" Set from: File diff --git a/doc/themer.md b/doc/themer.md new file mode 100644 index 00000000..01ea59bf --- /dev/null +++ b/doc/themer.md @@ -0,0 +1,172 @@ +# Basic Organization + +Each widget has: + +## Name + +Name: Internal name of the widget. + +Sub-widgets are {Parent}.{Child}. + +Example: window, window.mainbox.listview, window.mainbox.listview.element + +Names are prefixed with a `#` + +List of names in **rofi**: + + * `#window` + * `#window.overlay`: The overlay widget. + * `#window.mainbox` + * `#window.mainbox.box`: The main vertical @box + * `#window.mainbox.inputbar` + * `#window.mainbox.inputbar.box`: The horizontal @box packing the widgets. + * `#window.mainbox.inputbar.case-indicator`: The case/sort indicator @textbox + * `#window.mainbox.inputbar.prompt`: The prompt @textbox + * `#window.mainbox.inputbar.entry`: The main entry @textbox + * `#window.mainbox.listview` + * `#window.mainbox.listview.box`: The listview container. + * `#window.mainbox.listview.scrollbar`: The listview scrollbar + * `#window.mainbox.listview.element`: The entries in the listview + * `#window.mainbox.sidebar` + * `#window.mainbox.sidebar.box`: The main horizontal @box packing the buttons. + * `#window.mainbox.sidebar.button`: The buttons @textbox for each mode. + * `#window.mainbox.message` + * `#window.mainbox.message.textbox`: The message textbox. + * `#window.mainbox.message.box`: The box containing the message. + +## State + +State: State of widget + +Optional flag(s) indicating state. + +These are appended after the name or class of the widget. + +`#window.mainbox.sidebar.button selected.normal { }` + +`#window.mainbox.listview.element selected.urgent { }` + +Currently only the entrybox and scrollbar has states: + +`{visible modifier}.{state}` + +Where `visible modifier` can be: + * normal: No modification. + * selected: The entry is selected/highlighted by user. + * alternate: The entry is at an alternating row. (uneven row) + +Where `state` is: + * normal: No modification. + * urgent: This entry is marked urgent. + * activE: This entry is marked active. + +These can be mixed. + +Example: +``` +#name.to.textbox selected.active { + background: #003642; + foreground: #008ed4; +} +``` + +Sets all selected textboxes marked active to the given foreground and background color. + +The scrollbar when drawing uses the `handle` state when drawing the small scrollbar handle. +Allowing overriding of color. + +# File structure + +The file is structured as follows + +``` +/* Global properties, that apply as default to all widgets. */ +{list of properties} + +#{name} {optional state} { + {list of properties} +} +#{name}.{optional state} { + {list of properties} +} +``` + +The global properties an freeĺy be mixed between entries. +Name and state can be separated by a comman, or joined using a dot. + +Each property is constructed like: +``` +{key} : {value} ; +``` +Key is a simple ascii string. +Separated from value by a colon ':'; +Value supports the following formats: + + * string: `"{string}"` + * integer: `[0-9]+` + * double: `[0-9]+\.[0-9]` + * boolean: `true|false` + * color: + * `#[0-9a-fA-F]{6}`: hexidecimal rgb color. + * `#[0-9a-fA-F]{8}`: hexidecimal argb color. + * `argb:[0-0a-fA-F]{8}`: Old **rofi** argb color style. + * `rgba\([0-9]{1,3},[0-9]{1,3}, [0-9]{1,3}, {double}\)`: css style rgba color. + * `rgb\([0-9]{1,3},[0-9]{1,3}, [0-9]{1,3}\)`: css style rgb color. + * distance: + * `{size}{unit} {line-style}` + * unit can be px,em,%. for px `{size}` is an integer number, for %,em it is a positive real. + * {line-style} can be `dash` or `solid` and is optional. + * padding: `({distance}){1,4}` + * position: (center|north|south|east|west|northeast|northwest|southwest|southeast) + +Each property is closed by a semi-colon `;`; + +The following properties are currently supports: + + * all widgets: + * padding: distance + * margin: distance + * border: distance + * background: color + * foreground: color + * end: boolean + + * window: + * font: string + * transparency: string + - real + - background + - screenshot + - Path to png file + * position: position + The place of the anchor on the monitor. + * anchor: anchor + The anchor position on the window. + + * scrollbar + * foreground: color + * handle-width: distance + * handle-color: color + * foreground: color + + * box + * spacing: distance + + * textbox: + * background: color + * foreground: color + + * listview: + * columns: integer + * fixed-height: boolean + * dynamic: boolean + * scrollbar: boolean + * scrollbar-width: distance + * cycle: boolean + * spacing: distance + + +## Resolving properties + +It tries to find the longest match down the dependency tree. + diff --git a/include/helper.h b/include/helper.h index ebb8355e..af93aece 100644 --- a/include/helper.h +++ b/include/helper.h @@ -82,6 +82,14 @@ int find_arg_int ( const char * const key, int *val ); */ int find_arg_str ( const char * const key, char** val ); +/** + * @param key The key to search for + * + * Parse all command line options 'key' to string vector. + * + * @returns str vector. user should free array. + */ +const char ** find_arg_strv ( const char *const key ); /** * @param key The key to search for * diff --git a/include/settings.h b/include/settings.h index d5f513a5..344208e2 100644 --- a/include/settings.h +++ b/include/settings.h @@ -38,7 +38,7 @@ typedef enum /** Middle right */ WL_EAST = 4, /** Bottom right */ - WL_EAST_SOUTH = 5, + WL_SOUTH_EAST = 5, /** Bottom middle */ WL_SOUTH = 6, /** Bottom left */ @@ -149,6 +149,8 @@ typedef struct /** Click outside the window to exit */ int click_to_exit; gboolean show_match; + + char *theme; } Settings; /** Global Settings structure. */ extern Settings config; diff --git a/include/theme.h b/include/theme.h new file mode 100644 index 00000000..f867cb72 --- /dev/null +++ b/include/theme.h @@ -0,0 +1,337 @@ +#ifndef THEME_H +#define THEME_H +#include +#include +#include +#include + +/** Style of line */ +typedef enum { + /** Solid line */ + SOLID, + /** Dashed line */ + DASH +} LineStyle; + +/** + * Distance unit type. + */ +typedef enum { + /** PixelWidth in pixels. */ + PW_PX, + /** PixelWidth in EM. */ + PW_EM, + /** PixelWidget in percentage */ + PW_PERCENT, +} PixelWidth; + +/** + * Structure representing a distance. + */ +typedef struct { + /** Distance */ + double distance; + /** Unit type of the distance */ + PixelWidth type; + /** Style of the line */ + LineStyle style; +} Distance; + +/** + * Type of orientation. + */ +typedef enum { + ORIENTATION_VERTICAL, + ORIENTATION_HORIZONTAL +} Orientation; +/** + * Type of property + */ +typedef enum { + /** Integer */ + P_INTEGER, + /** Double */ + P_DOUBLE, + /** String */ + P_STRING, + /** Boolean */ + P_BOOLEAN, + /** Color */ + P_COLOR, + /** Padding */ + P_PADDING, + /** Link to global setting */ + P_LINK, + /** Position */ + P_POSITION, +} PropertyType; + +/** + * Represent the color in theme. + */ +typedef struct +{ + /** red channel */ + double red; + /** green channel */ + double green; + /** blue channel */ + double blue; + /** alpha channel */ + double alpha; +} ThemeColor; + +/** + * Padding + */ +typedef struct + { + Distance top; + Distance right; + Distance bottom; + Distance left; +} Padding; + +/** + * Property structure. + */ +typedef struct Property { + /** Name of property */ + char *name; + /** 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; + } value; +} Property; +/** + * ThemeWidget. + */ +typedef struct ThemeWidget { + int set; + char *name; + + unsigned int num_widgets; + struct ThemeWidget **widgets; + + GHashTable *properties; + + struct ThemeWidget *parent; +} ThemeWidget; + + +/** + * Global pointer to the current active theme. + */ +extern ThemeWidget *rofi_theme; + +/** + * @param base Handle to the current level in the theme. + * @param name Name of the new element. + * + * Create a new element in the theme structure. + * + * @returns handle to the new entry. + */ +ThemeWidget *rofi_theme_find_or_create_name ( ThemeWidget *base, const char *name ); + +/** + * @param widget The widget handle. + * + * Print out the widget to the commandline. + */ +void rofi_theme_print ( ThemeWidget *widget ); + +/** + * @param type The type of the property to create. + * + * Create a theme property of type. + * + * @returns a new property. + */ +Property *rofi_theme_property_create ( PropertyType type ); + +/** + * @param p The property to free. + * + * Free the content of the property. + */ +void rofi_theme_property_free ( Property *p ); + +/** + * @param wid + * + * Free the widget and alll children. + */ +void rofi_theme_free ( ThemeWidget *wid ); + +/** + * @param file filename to parse. + * + * Parse the input theme file. + * + * @returns returns TRUE when error. + */ +gboolean rofi_theme_parse_file ( const char *file ); + +/** + * @param string to parse. + * + * Parse the input string in addition to theme file. + * + * @returns returns TRUE when error. + */ +gboolean rofi_theme_parse_string ( const char *string ); + +/** + * @param widget The widget handle. + * @param table HashTable containing properties set. + * + * Merge properties with widgets current property. + */ +void rofi_theme_widget_add_properties ( ThemeWidget *widget, GHashTable *table ); + +/** + * Public API + */ + +/** + * @param widget The widget to query + * @param property The property to query. + * @param def The default value. + * + * Obtain the distance of the widget. + * + * @returns The distance value of this property for this widget. + */ +Distance rofi_theme_get_distance ( const widget *widget, const char *property, int def ); + +/** + * @param widget The widget to query + * @param property The property to query. + * @param def The default value. + * + * Obtain the integer of the widget. + * + * @returns The integer value of this property for this widget. + */ +int rofi_theme_get_integer ( const widget *widget, const char *property, int def ); + +/** + * @param widget The widget to query + * @param property The property to query. + * @param def The default value. + * + * Obtain the position of the widget. + * + * @returns The position value of this property for this widget. + */ +int rofi_theme_get_position ( const widget *widget, const char *property, int def ); + +/** + * @param widget The widget to query + * @param property The property to query. + * @param def The default value. + * + * Obtain the integer of the widget. + * + * @returns The integer value of this property for this widget. + */ +int rofi_theme_get_integer_exact ( const widget *widget, const char *property, int def ); + +/** + * @param widget The widget to query + * @param property The property to query. + * @param def The default value. + * + * Obtain the boolean of the widget. + * + * @returns The boolean value of this property for this widget. + */ +int rofi_theme_get_boolean ( const widget *widget, const char *property, int def ); + +/** + * @param widget The widget to query + * @param property The property to query. + * @param def The default value. + * + * Obtain the string of the widget. + * + * @returns The string value of this property for this widget. + */ +char *rofi_theme_get_string ( const widget *widget, const char *property, char *def ); + +/** + * @param widget The widget to query + * @param property The property to query. + * @param def The default value. + * + * Obtain the padding of the widget. + * + * @returns The double value of this property for this widget. + */ +double rofi_theme_get_double ( const widget *widget, const char *property, double def ); + +/** + * @param widget The widget to query + * @param property The property to query. + * @param d The drawable to apply color. + * + * Obtain the color of the widget and applies this to the drawable d. + * + */ +void rofi_theme_get_color ( const widget *widget, const char *property, cairo_t *d); + +/** + * @param widget The widget to query + * @param property The property to query. + * @param pad The default value. + * + * Obtain the padding of the widget. + * + * @returns The padding of this property for this widget. + */ +Padding rofi_theme_get_padding ( const widget *widget, const char *property, Padding pad ); + +/** + * @param d The distance handle. + * @param ori The orientation. + * + * Convert Distance into pixels. + * @returns the number of pixels this distance represents. + */ +int distance_get_pixel ( Distance d, Orientation ori ); +/** + * @param d The distance handle. + * @param draw The cairo drawable. + * + * Set linestyle. + */ +void distance_get_linestyle ( Distance d, cairo_t *draw ); + +#ifdef THEME_CONVERTER +/** + * Function to convert old theme into new theme format. + */ +void rofi_theme_convert_old_theme ( void ); +#endif +#endif diff --git a/include/view-internal.h b/include/view-internal.h index e55e01b7..51a1c90d 100644 --- a/include/view-internal.h +++ b/include/view-internal.h @@ -1,12 +1,13 @@ #ifndef ROFI_VIEW_INTERNAL_H #define ROFI_VIEW_INTERNAL_H +#include "widgets/container.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" +#include "theme.h" /** * @ingroup ViewHandle @@ -22,7 +23,8 @@ struct RofiViewState /** Flag indicating if view needs to be refiltered. */ int refilter; - + /** Widget representing the main container. */ + container *main_window; /** Main #box widget holding different elements. */ box *main_box; /** #box widget packing the input bar widgets. */ @@ -33,8 +35,6 @@ struct RofiViewState textbox *text; /** #textbox showing the state of the case sensitive and sortng. */ textbox *case_indicator; - /** #separator widget below the input bar. */ - separator *input_bar_separator; /** #listview holding the displayed elements. */ listview *list_view; @@ -63,8 +63,6 @@ struct RofiViewState unsigned int selected_line; /** The return state of the view */ MenuReturn retv; - /** Calculated border width */ - unsigned int border; /** Monitor #workarea the view is displayed on */ workarea mon; diff --git a/include/view.h b/include/view.h index 12d06c2f..3a482310 100644 --- a/include/view.h +++ b/include/view.h @@ -111,12 +111,6 @@ unsigned int rofi_view_get_selected_line ( const RofiViewState *state ); */ void rofi_view_restart ( RofiViewState *state ); -/** - * @param state The handle to the view - * - * Update the state of the view. This involves filter state. - */ -void rofi_view_update ( RofiViewState *state ); /** * @param state The handle to the view @@ -254,5 +248,12 @@ void rofi_view_workers_initialize ( void ); * Stop all threads and free the resources used by the threadpool */ void rofi_view_workers_finalize ( void ); + +/** + * Return the current monitor workarea. + * + * @returns the current monitor workarea + */ +void rofi_view_get_current_monitor ( int *width, int *height ); /**@}*/ #endif diff --git a/include/widgets/box.h b/include/widgets/box.h index 10d6a5a2..2c067598 100644 --- a/include/widgets/box.h +++ b/include/widgets/box.h @@ -32,42 +32,31 @@ typedef enum } boxType; /** + * @param name The name of the widget. * @param type The packing direction of the newly created box. - * @param x The x position of the box relative to its parent. - * @param y The y position of the box relative to its parent. - * @param w The width of the box. - * @param h The height of the box. * * @returns a newly created box, free with #widget_free */ -box * box_create ( boxType type, short x, short y, short w, short h ); +box * box_create ( const char *name, boxType type ); /** * @param box Handle to the box widget. * @param child Handle to the child widget to pack. * @param expand If the child widget should expand and use all available space. - * @param end If the child widget should be packed at the end. + * @param index The position index. * * Add a widget to the box. */ -void box_add ( box *box, widget *child, gboolean expand, gboolean end ); +void box_add ( box *box, widget *child, gboolean expand, int index ); /** * @param box Handle to the box widget. * * Obtains the minimal size required to display all widgets. (expanding widgets are not counted, except for their - * padding) + * spacing) * * @returns the minimum size in pixels. */ int box_get_fixed_pixels ( box *box ); - -/** - * @param box Handle to the box widget. - * @param padding The padding to apply. - * - * Set the padding to apply between the children in pixels. - */ -void box_set_padding ( box * box, unsigned int padding ); /*@}*/ #endif // ROFI_HBOX_H diff --git a/include/widgets/container.h b/include/widgets/container.h new file mode 100644 index 00000000..6b417075 --- /dev/null +++ b/include/widgets/container.h @@ -0,0 +1,34 @@ +#ifndef ROFI_CONTAINER_H +#define ROFI_CONTAINER_H + +#include "widget.h" + +/** + * @defgroup container container + * @ingroup widget + * + * + * @{ + */ + +/** + * Abstract handle to the container widget internal state. + */ +typedef struct _window container; + +/** + * @param name The name of the widget. + * + * @returns a newly created container, free with #widget_free + */ +container * container_create ( const char *name ); + +/** + * @param container Handle to the container widget. + * @param child Handle to the child widget to pack. + * + * Add a widget to the container. + */ +void container_add ( container *container, widget *child ); +/*@}*/ +#endif // ROFI_CONTAINER_H diff --git a/include/widgets/listview.h b/include/widgets/listview.h index 21ebe758..773b29c9 100644 --- a/include/widgets/listview.h +++ b/include/widgets/listview.h @@ -42,13 +42,15 @@ typedef void ( *listview_update_callback )( textbox *tb, unsigned int entry, voi typedef void ( *listview_mouse_activated_cb )( listview *, xcb_button_press_event_t *, void * ); /** + * @param name The name of the to be created widget. * @param cb The update callback. * @param udata The user data to pass to the callback * @param eh The height of one element + * @param reverse Reverse the listview order. * * @returns a new listview */ -listview *listview_create ( listview_update_callback cb, void *udata, unsigned int eh ); +listview *listview_create ( const char *name, listview_update_callback cb, void *udata, unsigned int eh, gboolean reverse ); /** * @param lv The listview handle @@ -75,15 +77,6 @@ void listview_set_selected ( listview *lv, unsigned int selected ); */ unsigned int listview_get_selected ( listview *lv ); -/** - * @param lv The listview handle - * - * Get the desired height of the listview widget. - * - * @returns the desired height. - */ -unsigned int listview_get_desired_height ( listview *lv ); - /** * @param lv The listview handle * @@ -107,14 +100,14 @@ void listview_nav_down ( listview *lv ); */ void listview_nav_right ( listview *lv ); /** - * @param lv The listview handle + * @param lv The listview handle * * Move the selection one column to the left. * - No wrap around. */ void listview_nav_left ( listview *lv ); /** - * @param lv The listview handle + * @param lv The listview handle * * Move the selection one page down. * - No wrap around. @@ -123,7 +116,7 @@ void listview_nav_left ( listview *lv ); void listview_nav_page_next ( listview *lv ); /** - * @param lv The listview handle + * @param lv The listview handle * * Move the selection one page up. * - No wrap around. @@ -131,44 +124,13 @@ void listview_nav_page_next ( listview *lv ); */ void listview_nav_page_prev ( listview *lv ); -/** - * @param lv Handler to the listview object - * @param padding The padding - * - * Padding on between the widgets. - */ -void listview_set_padding ( listview *lv, unsigned int padding ); - -/** - * @param lv Handler to the listview object - * @param lines The maximum number of lines - * - * Set the maximum number of lines to show. - */ -void listview_set_max_lines ( listview *lv, unsigned int lines ); - -/** - * @param lv Handler to the listview object - * @param columns The maximum number of columns - * - * Set the maximum number of columns to show. - */ -void listview_set_max_columns ( listview *lv, unsigned int columns ); - -/** - * @param lv Handler to the listview object - * @param enabled enable - * - * Set fixed num lines mode. - */ -void listview_set_fixed_num_lines ( listview *lv, gboolean enabled ); /** * @param lv Handler to the listview object * @param enabled enable * * Hide the scrollbar. */ -void listview_set_hide_scrollbar ( listview *lv, gboolean enabled ); +void listview_set_show_scrollbar ( listview *lv, gboolean enabled ); /** * @param lv Handler to the listview object * @param width Width in pixels @@ -207,6 +169,47 @@ void listview_set_mouse_activated_cb ( listview *lv, listview_mouse_activated_cb * Enable,disable multi-select. */ void listview_set_multi_select ( listview *lv, gboolean enable ); +/** + * @param lv Handler to the listview object. + * @param num_lines the maximum number of lines to display. + * + * Set the maximum number of lines to display. + */ +void listview_set_num_lines ( listview *lv, unsigned int num_lines ); + +/** + * @param lv Handler to the listview object. + * + * Get the maximum number of lines to display. + * + * @returns get the numger of lines to display. + */ +unsigned int listview_get_num_lines ( listview *lv ); + +/** + * @param lv Handler to the listview object. + * + * Get the fixed-height property. + * + * @returns get fixed-height. + */ +gboolean listview_get_fixed_num_lines ( listview *lv ); + +/** + * @param lv Handler to the listview object. + * + * Set fixed num lines mode. + */ +void listview_set_fixed_num_lines ( listview *lv ); + +/** + * @param lv Handler to the listview object. + * @param max_lines the maximum number of lines to display. + * + * Set the maximum number of lines to display. + */ +void listview_set_max_lines ( listview *lv, unsigned int max_lines ); + /* @} */ #endif // ROFI_LISTVIEW_H diff --git a/include/widgets/scrollbar.h b/include/widgets/scrollbar.h index d03c03ad..80cfceb6 100644 --- a/include/widgets/scrollbar.h +++ b/include/widgets/scrollbar.h @@ -19,19 +19,17 @@ typedef struct _scrollbar unsigned int length; unsigned int pos; unsigned int pos_length; + Distance width; } scrollbar; /** - * @param x The x coordinate (relative to parent) to position the new scrollbar - * @param y The y coordinate (relative to parent) to position the new scrollbar - * @param w The width of the scrollbar - * @param h The height of the scrollbar + * @param name The name of the widget. * * Create a new scrollbar * * @returns the scrollbar object. */ -scrollbar *scrollbar_create ( short x, short y, short w, short h ); +scrollbar *scrollbar_create ( const char *name ); /** * @param sb scrollbar object diff --git a/include/widgets/separator.h b/include/widgets/separator.h deleted file mode 100644 index f0f27f8e..00000000 --- a/include/widgets/separator.h +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef ROFI_SEPARATOR_H -#define ROFI_SEPARATOR_H -#include -#include "widget.h" - -/** - * @defgroup separator separator - * @ingroup widget - * - * Displays a horizontal separator line. The height of the widget determines the line width. - * - * @{ - */ - -/** - * Abstract handle to the separator widget internal state. - */ -typedef struct _separator separator; - -/** - * Direction of the separator. - */ -typedef enum -{ - S_HORIZONTAL = 0, - S_VERTICAL = 1 -} separator_type; - -/** - * The style of the separator line. - */ -typedef enum -{ - S_LINE_NONE, - S_LINE_SOLID, - S_LINE_DASH -} separator_line_style; - -/** - * @param type The type of separator. - * @param sw The thickness of the separator. - * - * Create a horizontal separator with height h. - * - * @returns a new separator, free with ::widget_free - */ -separator *separator_create ( separator_type type, short sw ); - -/** - * @param sp The separator widget handle. - * @param style_str String representation of the style. - * - * Sets the line style based on the string style_str - */ -void separator_set_line_style_from_string ( separator *sp, const char *style_str ); - -/** - * @param sp The separator widget handle. - * @param style The new style. - * - * Sets the line style. - */ -void separator_set_line_style ( separator *sp, separator_line_style style ); -/*@}*/ -#endif // ROFI_SEPARATOR_H diff --git a/include/widgets/textbox.h b/include/widgets/textbox.h index b40f3978..536bd30e 100644 --- a/include/widgets/textbox.h +++ b/include/widgets/textbox.h @@ -39,6 +39,8 @@ typedef struct int update; int blink; guint blink_timeout; + // + const char *theme_name ; } textbox; /** @@ -84,11 +86,8 @@ typedef enum } TextBoxFontType; /** + * @param name The name of the to be created widget. * @param flags #TextboxFlags indicating the type of textbox. - * @param x horizontal positon of textbox - * @param y vertical position of textbox - * @param w width of textbox - * @param h height of textbox * @param tbft #TextBoxFontType current state of textbox. * @param text intial text to display. * @@ -97,8 +96,7 @@ typedef enum * free with #widget_free * @returns a new #textbox */ -textbox* textbox_create ( TextboxFlags flags, - short x, short y, short w, short h, +textbox* textbox_create ( const char *name, TextboxFlags flags, TextBoxFontType tbft, const char *text ); /** @@ -200,6 +198,13 @@ int textbox_get_font_width ( const textbox *tb ); */ double textbox_get_estimated_char_width ( void ); +/** + * Estimate the height of a character. + * + * @returns the height of a character in pixels. + */ +double textbox_get_estimated_char_height ( void ); + /** * @param tb Handle to the textbox * @param pos The start position @@ -220,12 +225,17 @@ void textbox_delete ( textbox *tb, int pos, int dlen ); * TODO remove for #widget_resize and #widget_move */ void textbox_moveresize ( textbox *tb, int x, int y, int w, int h ); + /** + * @param tb Handle to the textbox + * @param eh The number of rows to display + * * Get the (estimated) with of a character, can be used to calculate window width. + * This includes padding. * * @returns the estimated width of a character. */ -int textbox_get_estimated_char_height ( void ); +int textbox_get_estimated_height ( const textbox *tb, int eh ); /** * @param p The new default PangoContext * diff --git a/include/widgets/widget-internal.h b/include/widgets/widget-internal.h index a50f3980..e26fb8f2 100644 --- a/include/widgets/widget-internal.h +++ b/include/widgets/widget-internal.h @@ -1,6 +1,7 @@ #ifndef WIDGET_INTERNAL_H #define WIDGET_INTERNAL_H +#include "theme.h" /** * Data structure holding the internal state of the Widget */ @@ -14,10 +15,17 @@ struct _widget short w; /** Height of the widget */ short h; + /** Padding */ + Padding margin; + Padding padding; + Padding border; + /** enabled or not */ gboolean enabled; /** Expand the widget when packed */ gboolean expand; + /*** The packing index */ + int index; /** Place widget at end of parent */ gboolean end; /** Parent widget */ @@ -38,6 +46,8 @@ struct _widget /** Handle mouse motion, used for dragging */ gboolean ( *motion_notify )( struct _widget *, xcb_motion_notify_event_t * ); + int (*get_desired_height) ( struct _widget * ); + /** widget clicked callback */ widget_clicked_cb clicked; /** user data for clicked callback */ @@ -45,5 +55,95 @@ struct _widget /** Free widget callback */ void ( *free )( struct _widget *widget ); + + /** Name of widget (used for theming) */ + char *name; + const char *state; }; + +/** + * @param widget The widget to initialize. + * @param name The name of the widget. + * + * Initializes the widget structure. + * + */ +void widget_init ( widget *widget , const char *name ); + +/** + * @param widget The widget handle. + * @param state The state of the widget. + * + * Set the state of the widget. + */ +void widget_set_state ( widget *widget, const char *state ); + +/** + * @param wid The widget handle. + * + * Get the left padding of the widget. + * + * @returns the left padding in pixels. + */ +int widget_padding_get_left ( const widget *wid ); + +/** + * @param wid The widget handle. + * + * Get the right padding of the widget. + * + * @returns the right padding in pixels. + */ +int widget_padding_get_right ( const widget *wid ); + +/** + * @param wid The widget handle. + * + * Get the top padding of the widget. + * + * @returns the top padding in pixels. + */ +int widget_padding_get_top ( const widget *wid ); + +/** + * @param wid The widget handle. + * + * Get the bottom padding of the widget. + * + * @returns the bottom padding in pixels. + */ +int widget_padding_get_bottom ( const widget *wid ); + +/** + * @param wid The widget handle. + * + * Get width of the content of the widget + * + * @returns the widget width, excluding padding. + */ +int widget_padding_get_remaining_width ( const widget *wid ); +/** + * @param wid The widget handle. + * + * Get height of the content of the widget + * + * @returns the widget height, excluding padding. + */ +int widget_padding_get_remaining_height ( const widget *wid ); +/** + * @param wid The widget handle. + * + * Get the combined top and bottom padding. + * + * @returns the top and bottom padding of the widget in pixels. + */ +int widget_padding_get_padding_height ( const widget *wid ); +/** + * @param wid The widget handle. + * + * Get the combined left and right padding. + * + * @returns the left and right padding of the widget in pixels. + */ +int widget_padding_get_padding_width ( const widget *wid ); #endif // WIDGET_INTERNAL_H diff --git a/include/widgets/widget.h b/include/widgets/widget.h index ba7fa841..9dda0457 100644 --- a/include/widgets/widget.h +++ b/include/widgets/widget.h @@ -173,5 +173,24 @@ void widget_set_clicked_handler ( widget *wid, widget_clicked_cb cb, void *udata * returns TRUE when handled. */ gboolean widget_motion_notify ( widget *wid, xcb_motion_notify_event_t *xme ); + + +/** + * @param wid The widget handle + * @param name The name of the widget. + * + * Set name on widget. + */ +void widget_set_name ( widget *wid, const char *name ); + +/** + * @param wid The widget handle + * + * Get the desired height of this widget recursively. + * + * @returns the desired height of the widget in pixels. + */ +int widget_get_desired_height ( widget *wid ); + /*@}*/ #endif // ROFI_WIDGET_H diff --git a/lexer/theme-lexer.l b/lexer/theme-lexer.l new file mode 100644 index 00000000..5a7540c1 --- /dev/null +++ b/lexer/theme-lexer.l @@ -0,0 +1,336 @@ +%option noyywrap nounput never-interactive +%option bison-locations + +%{ +#include + + +#include "lexer/theme-parser.h" +int last_state = 0; +GQueue *queue = NULL; + +%} +%{ + +int str_len = 0; +char *input_str = NULL; + +#define YY_INPUT(buf,result,max_size) \ +{\ + if ( input_str == NULL ) { \ + errno =0; \ + while ( (result = (int) fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(yyin); \ + } \ + } else {\ + yy_size_t len = MIN (max_size, str_len);\ + if ( len > 0 ){\ + memcpy (buf, input_str, len);\ + input_str+=len;\ + str_len-=len;\ + result = len;\ + } else {\ + result = YY_NULL;\ + } \ + }\ +} + + +#define YY_USER_ACTION {\ + yylloc->last_column+= yyleng;\ +} +#define YY_LLOC_START {\ + yylloc->first_line = yylloc->last_line;\ + yylloc->first_column = yylloc->last_column;\ +} +%} +WHITESPACE [[:blank:]] +WORD [[:alnum:]-]+ +STRING [[:print:]]+ +HEX [[:xdigit:]] +NUMBER [[:digit:]] +REAL [[:digit:]]+(\.[[:digit:]]+)? +PX (px) +EM (em) +PERCENT (\%) + +ASTERIX \* + +CENTER "center" +NORTH "north" +SOUTH "south" +EAST "east" +WEST "west" + +LS_DASH "dash" +LS_SOLID "solid" + +%x PROPERTIES +%x NAMESTR +%x ENTRY +%x DEFAULTS +%% + +%{ +YY_LLOC_START +%} +%{ +if ( queue == NULL ){ + queue = g_queue_new ( ); +} +%} + +<*>"//" { + int c; + while ((c = input()) != EOF){ + if (c == '\n') { + yylloc->last_column = 1; + yylloc->last_line ++; + break; + } + yylloc->last_column++; + } + YY_LLOC_START +} +<*>"/*" { + int c = 0, p; + int nesting_depth = 1; + while (nesting_depth) { + p = c; + c = input(); + switch (c) { + case '*': yylloc->last_column++; if (p == '/') { c = 0; nesting_depth++; } break; + case '/': yylloc->last_column++; if (p == '*') { c = 0; nesting_depth--; } break; + case '\n': { + yylloc->last_column = 1; + yylloc->last_line ++; + break; + } + case EOF: nesting_depth = 0; break; + default: + yylloc->last_column++; + ; + } + } + YY_LLOC_START +} + +{ASTERIX} { + g_queue_push_head ( queue, GINT_TO_POINTER (YY_START) ); + BEGIN(DEFAULTS); + return PDEFAULTS; +} +{WHITESPACE} {} +"\{" { + g_queue_push_head ( queue, GINT_TO_POINTER (YY_START) ); + BEGIN(ENTRY); + return BOPEN; +} + + /* Go into parsing an entry */ +"\{" { + g_queue_push_head ( queue, GINT_TO_POINTER (YY_START) ); + BEGIN(ENTRY); + return BOPEN; +} + /* Pop out of parsing an entry. */ +"\}" { + g_queue_pop_head ( queue ); + BEGIN(GPOINTER_TO_INT(g_queue_pop_head ( queue ))); + return BCLOSE; +} + +"#" { g_queue_push_head ( queue, GINT_TO_POINTER (YY_START) ); BEGIN(NAMESTR);return NAME_PREFIX;} +\.|{WHITESPACE} { return NSEP; } +{WORD} { yylval->sval = g_strdup(yytext); return N_STRING;} +{WORD} { yylval->sval = g_strdup(yytext); return NAME_ELEMENT;} + + /* After Namestr/Classstr we want to go to state str, then to { */ + /*{WHITESPACE} { BEGIN(GPOINTER_TO_INT (g_queue_pop_head ( queue )));}*/ +{WHITESPACE}+ ; // ignore all whitespace +{WHITESPACE}+ ; // ignore all whitespace + +":" { g_queue_push_head ( queue, GINT_TO_POINTER (YY_START) ); BEGIN(PROPERTIES); return PSEP; } +";" { BEGIN(GPOINTER_TO_INT ( g_queue_pop_head ( queue ))); return PCLOSE;} +(true|false) { yylval->bval= g_strcmp0(yytext, "true") == 0; return T_BOOLEAN;} +{NUMBER}+ { yylval->ival = (int)g_ascii_strtoll(yytext, NULL, 10); return T_INT;} +{NUMBER}+\.{NUMBER}+ { yylval->fval = g_ascii_strtod(yytext, NULL); return T_DOUBLE;} +\"{STRING}\" { yytext[yyleng-1] = '\0'; yylval->sval = g_strdup(&yytext[1]); return T_STRING;} +@{WORD} { + yylval->sval = g_strdup(yytext); + return T_LINK; +} + +{REAL}{EM} { + yylval->distance.distance = (double)g_ascii_strtod(yytext, NULL); + yylval->distance.type = PW_EM; + yylval->distance.style = SOLID; + return T_PIXEL; +} +{NUMBER}+{PX} { + yylval->distance.distance = (double)g_ascii_strtoll(yytext, NULL, 10); + yylval->distance.type = PW_PX; + yylval->distance.style = SOLID; + return T_PIXEL; +} +{NUMBER}+{PX}{WHITESPACE}{LS_DASH} { + yylval->distance.distance = (double)g_ascii_strtoll(yytext, NULL, 10); + yylval->distance.type = PW_PX; + yylval->distance.style = DASH; + return T_PIXEL; +} +{NUMBER}+{EM}{WHITESPACE}{LS_DASH} { + yylval->distance.distance = (double)g_ascii_strtoll(yytext, NULL, 10); + yylval->distance.type = PW_PX; + yylval->distance.style = DASH; + return T_PIXEL; +} +{NUMBER}+{PX}{WHITESPACE}{LS_SOLID} { + yylval->distance.distance = (double)g_ascii_strtoll(yytext, NULL, 10); + yylval->distance.type = PW_PX; + yylval->distance.style = SOLID; + return T_PIXEL; +} +{NUMBER}+{EM}{WHITESPACE}{LS_SOLID} { + yylval->distance.distance = (double)g_ascii_strtoll(yytext, NULL, 10); + yylval->distance.type = PW_PX; + yylval->distance.style = SOLID; + return T_PIXEL; +} +{REAL}{PERCENT} { + yylval->distance.distance = (double)g_ascii_strtod(yytext, NULL); + yylval->distance.type = PW_PERCENT; + yylval->distance.style = SOLID; + return T_PIXEL; +} +{REAL}{PERCENT}{WHITESPACE}{LS_SOLID} { + yylval->distance.distance = (double)g_ascii_strtod(yytext, NULL); + yylval->distance.type = PW_PERCENT; + yylval->distance.style = SOLID; + return T_PIXEL; +} +{REAL}{PERCENT}{WHITESPACE}{LS_DASH} { + yylval->distance.distance = (double)g_ascii_strtod(yytext, NULL); + yylval->distance.type = PW_PERCENT; + yylval->distance.style = DASH; + return T_PIXEL; +} +#{HEX}{8} { + union { unsigned int val; struct { unsigned char b,g,r,a;};} val; + val.val = (unsigned int)strtoull ( &yytext[1], NULL, 16); + yylval->colorval.alpha = val.a/255.0; + yylval->colorval.red = val.r/255.0; + yylval->colorval.green = val.g/255.0; + yylval->colorval.blue = val.b/255.0; + return T_COLOR; +} +argb:{HEX}{8} { + union { unsigned int val; struct { unsigned char b,g,r,a;};} val; + val.val = (unsigned int)strtoull ( &yytext[1], NULL, 16); + yylval->colorval.alpha = val.a/255.0; + yylval->colorval.red = val.r/255.0; + yylval->colorval.green = val.g/255.0; + yylval->colorval.blue = val.b/255.0; + return T_COLOR; +} +#{HEX}{6} { + union { unsigned int val; struct { unsigned char b,g,r,a;};} val; + val.val = (unsigned int)g_ascii_strtoull ( &yytext[1], NULL, 16); + yylval->colorval.alpha = 1.0; + yylval->colorval.red = val.r/255.0; + yylval->colorval.green = val.g/255.0; + yylval->colorval.blue = val.b/255.0; + return T_COLOR; +} +rgba\({NUMBER}{1,3},{NUMBER}{1,3},{NUMBER}{1,3},[01](\.{NUMBER}+)?\) { + char *endptr = &yytext[5]; + yylval->colorval.red = g_ascii_strtoull ( endptr, &endptr, 10); + yylval->colorval.green= g_ascii_strtoull ( endptr+1, &endptr, 10); + yylval->colorval.blue= g_ascii_strtoull ( endptr+1, &endptr, 10); + yylval->colorval.alpha= g_ascii_strtod ( endptr+1, NULL); + return T_COLOR; +} +rgb\({NUMBER}{1,3},{NUMBER}{1,3},{NUMBER}{1,3}\) { + char *endptr = &yytext[4]; + yylval->colorval.red = g_ascii_strtoull ( endptr, &endptr, 10); + yylval->colorval.green = g_ascii_strtoull ( endptr+1, &endptr, 10); + yylval->colorval.blue = g_ascii_strtoull ( endptr+1, &endptr, 10); + yylval->colorval.alpha = 1.0; + return T_COLOR; +} + +{CENTER} { + yylval->ival = WL_CENTER; + return T_POSITION; +} +{EAST} { + yylval->ival = WL_EAST; + return T_POSITION; +} +{WEST} { + yylval->ival = WL_WEST; + return T_POSITION; +} +{SOUTH}{EAST} { + yylval->ival = WL_SOUTH_EAST; + return T_POSITION; +} +{SOUTH}{WEST} { + yylval->ival = WL_SOUTH_WEST; + return T_POSITION; +} +{SOUTH} { + yylval->ival = WL_SOUTH; + return T_POSITION; +} +{NORTH}{EAST} { + yylval->ival = WL_NORTH_EAST; + return T_POSITION; +} +{NORTH}{WEST} { + yylval->ival = WL_NORTH_WEST; + return T_POSITION; +} +{NORTH} { + yylval->ival = WL_NORTH; + return T_POSITION; +} +NORTH { + yylval->ival = WL_NORTH; + return T_POSITION; +} +<> { + g_queue_free ( queue ); + // Reset pointer to NULL + queue = NULL; + yyterminate(); +} + +<*>\n { + yylloc->last_column = 1; + yylloc->last_line ++; +}; +<*>(\r\n) { + yylloc->last_column = 1; + yylloc->last_line ++; +}; +. { + const char *error_msg = "Expected 'root' element or a 'named' element.\n"\ + "Place all global properties in a root element:\n"\ + " * {\n"\ + " }\n"; + YY_FATAL_ERROR( error_msg ); +} +<*>. { + fprintf(stderr, "Invalid character: '%c'\n", *yytext); + yyterminate(); +} + +%% diff --git a/lexer/theme-parser.y b/lexer/theme-parser.y new file mode 100644 index 00000000..59317b52 --- /dev/null +++ b/lexer/theme-parser.y @@ -0,0 +1,185 @@ +%define api.pure +%glr-parser +%skeleton "glr.c" +%locations +%debug +%error-verbose + +%code requires { +#include "theme.h" +} +%{ +#include +#include +#include + + +#include "lexer/theme-parser.h" +ThemeWidget *rofi_theme = NULL; +void yyerror(YYLTYPE *yylloc, const char* s); +int yylex (YYSTYPE *, YYLTYPE *); +%} + +%union { + int ival; + double fval; + char *sval; + int bval; + ThemeColor colorval; + ThemeWidget *theme; + GList *name_path; + Property *property; + GHashTable *property_list; + Distance distance; +} + +%token T_INT +%token T_DOUBLE +%token T_STRING +%token N_STRING +%token T_POSITION; +%token NAME_ELEMENT "Element name" +%token T_BOOLEAN +%token T_COLOR +%token T_PIXEL +%token T_LINK +%token FIRST_NAME + +%token BOPEN "bracket open"; +%token BCLOSE "bracket close"; +%token PSEP "property separator"; +%token PCLOSE "property close"; +%token NSEP "Name separator"; +%token NAME_PREFIX "Name prefix"; +%token WHITESPACE "White space"; +%token PDEFAULTS "Default settings"; + +%type entry +%type pvalue +%type entries +%type name_path +%type property +%type property_list +%type optional_properties +%start entries + +%% + +entries: + %empty { + // There is always a base widget. + if (rofi_theme == NULL ){ + $$ = rofi_theme = (ThemeWidget*)g_malloc0 (sizeof(ThemeWidget)); + rofi_theme->name = g_strdup ( "Root" ); + } + } +| entries + entry { + } +; + +entry: +NAME_PREFIX name_path BOPEN optional_properties BCLOSE +{ + ThemeWidget *widget = rofi_theme; + for ( GList *iter = g_list_first ( $2 ); iter ; iter = g_list_next ( iter ) ) { + widget = rofi_theme_find_or_create_name ( widget, iter->data ); + } + g_list_foreach ( $2, (GFunc)g_free , NULL ); + g_list_free ( $2 ); + widget->set = TRUE; + rofi_theme_widget_add_properties ( widget, $4); +} +| + PDEFAULTS BOPEN optional_properties BCLOSE { + rofi_theme_widget_add_properties ( rofi_theme, $3); +} +; + +/** + * properties + */ +optional_properties + : %empty { $$ = NULL; } + | property_list { $$ = $1; } +; + +property_list: + property { + $$ = g_hash_table_new_full ( g_str_hash, g_str_equal, NULL, (GDestroyNotify)rofi_theme_property_free ); + g_hash_table_replace ( $$, $1->name, $1 ); + } +| property_list property { + // Old will be free'ed, and key/value will be replaced. + g_hash_table_replace ( $$, $2->name, $2 ); + } +; + +property +: pvalue PSEP T_INT PCLOSE { + $$ = rofi_theme_property_create ( P_INTEGER ); + $$->name = $1; + $$->value.i = $3; + } +| pvalue PSEP T_DOUBLE PCLOSE { + $$ = rofi_theme_property_create ( P_DOUBLE ); + $$->name = $1; + $$->value.f = $3; + } +| pvalue PSEP T_COLOR PCLOSE { + $$ = rofi_theme_property_create ( P_COLOR ); + $$->name = $1; + $$->value.color = $3; + } +| pvalue PSEP T_STRING PCLOSE { + $$ = rofi_theme_property_create ( P_STRING ); + $$->name = $1; + $$->value.s = $3; + } +| pvalue PSEP T_LINK PCLOSE { + $$ = rofi_theme_property_create ( P_LINK ); + $$->name = $1; + $$->value.link.name = $3; + } +| pvalue PSEP T_BOOLEAN PCLOSE { + $$ = rofi_theme_property_create ( P_BOOLEAN ); + $$->name = $1; + $$->value.b = $3; + } +| pvalue PSEP T_PIXEL PCLOSE { + $$ = rofi_theme_property_create ( P_PADDING ); + $$->name = $1; + $$->value.padding = (Padding){ $3, $3, $3, $3 }; +} +| pvalue PSEP T_PIXEL T_PIXEL PCLOSE { + $$ = rofi_theme_property_create ( P_PADDING ); + $$->name = $1; + $$->value.padding = (Padding){ $3, $4, $3, $4 }; +} +| pvalue PSEP T_PIXEL T_PIXEL T_PIXEL PCLOSE { + $$ = rofi_theme_property_create ( P_PADDING ); + $$->name = $1; + $$->value.padding = (Padding){ $3, $4, $5, $4 }; +} +| pvalue PSEP T_PIXEL T_PIXEL T_PIXEL T_PIXEL PCLOSE { + $$ = rofi_theme_property_create ( P_PADDING ); + $$->name = $1; + $$->value.padding = (Padding){ $3, $4, $5, $6 }; +} +| pvalue PSEP T_POSITION PCLOSE{ + $$ = rofi_theme_property_create ( P_POSITION ); + $$->name = $1; + $$->value.i = $3; +} +; + +pvalue: N_STRING { $$ = $1; } + +name_path: +NAME_ELEMENT { $$ = g_list_append ( NULL, $1 );} +| name_path NSEP NAME_ELEMENT { $$ = g_list_append ( $1, $3);} +| name_path NSEP { $$ = $1; } +; + +%% + diff --git a/script/rofi-convert-theme.sh b/script/rofi-convert-theme.sh new file mode 100755 index 00000000..7ec801d0 --- /dev/null +++ b/script/rofi-convert-theme.sh @@ -0,0 +1,76 @@ +#!/usr/bin/env bash +# +# This code is released in public domain by Dave Davenport +# This converts from old style theme (< 1.4) to new style theme (>= 1.4) +# +function update_color () +{ + var=${1} + var="${var#"${var%%[![:space:]]*}"}" # remove leading whitespace characters + var="${var%"${var##*[![:space:]]}"}" # remove trailing whitespace characters + if [[ ${var} =~ argb:[0-9a-fA-F]{6,8} ]] + then + echo "#${var:5}" + else + echo ${var} + fi +} + +function parse_window_color () +{ + OLDIFS=${IFS} + IFS="," + entries=( ${1} ) + echo "@window {" + echo " background: $( update_color ${entries[0]});" + echo " foreground: $( update_color ${entries[1]});" + echo "}" + if [ -n "${entries[2]}" ] + then + echo "@separator {" + echo " foreground: $( update_color ${entries[2]});" + echo "}" + echo "@scrollbar {" + echo " foreground: $( update_color ${entries[2]});" + echo "}" + fi + IFS=${OLDIFS} +} + +function parse_color () +{ + state=$1 + OLDIFS=${IFS} + IFS="," + entries=( ${2} ) + echo "@textbox normal.${state} { " + echo " background: $( update_color ${entries[0]});" + echo " foreground: $( update_color ${entries[1]});" + echo "}" + echo "@textbox selected.${state} { " + echo " background: $( update_color ${entries[3]});" + echo " foreground: $( update_color ${entries[4]});" + echo "}" + echo "@textbox alternate.${state} { " + echo " background: $( update_color ${entries[2]});" + echo " foreground: $( update_color ${entries[1]});" + echo "}" + IFS=${OLDIFS} +} + +while read LINE +do + if [[ ${LINE} =~ ^rofi\.color-normal: ]] + then + parse_color "normal" "${LINE:18}" + elif [[ ${LINE} =~ ^rofi\.color-urgent: ]] + then + parse_color "urgent" "${LINE:18}" + elif [[ ${LINE} =~ ^rofi\.color-active: ]] + then + parse_color "active" "${LINE:18}" + elif [[ ${LINE} =~ ^rofi\.color-window: ]] + then + parse_window_color "${LINE:18}" + fi +done diff --git a/source/helper.c b/source/helper.c index 9f529577..bbaf03c3 100644 --- a/source/helper.c +++ b/source/helper.c @@ -295,6 +295,27 @@ int find_arg_str ( const char * const key, char** val ) return FALSE; } +const char ** find_arg_strv ( const char *const key ) +{ + const char **retv =NULL; + int length = 0; + for ( int i = 0; i < stored_argc; i++ ) { + if ( strcasecmp ( stored_argv[i], key ) == 0 && i < (stored_argc -1 ) ){ + length++; + } + } + if ( length > 0 ) { + retv = g_malloc0((length+1)*sizeof(char*)); + int index = 0; + for ( int i = 0; i < stored_argc; i++ ) { + if ( strcasecmp ( stored_argv[i], key ) == 0 && i < (stored_argc -1 ) ){ + retv[index++] = stored_argv[i+1]; + } + } + } + return retv; +} + int find_arg_int ( const char * const key, int *val ) { int i = find_arg ( key ); diff --git a/source/rofi.c b/source/rofi.c index ebd16168..38db3e66 100644 --- a/source/rofi.c +++ b/source/rofi.c @@ -64,6 +64,8 @@ #include "gitconfig.h" +#include "theme.h" + // Pidfile. char *pidfile = NULL; const char *cache_dir = NULL; @@ -576,8 +578,9 @@ static void error_trap_pop ( G_GNUC_UNUSED SnDisplay *display, xcb_connection_t xcb_flush ( xdisplay ); --error_trap_depth; } - +/** Retry count of grabbing keyboard. */ unsigned int lazy_grab_retry_count_kb = 0; +/** Retry count of grabbing pointer. */ unsigned int lazy_grab_retry_count_pt = 0; static gboolean lazy_grab_pointer ( G_GNUC_UNUSED gpointer data ) { @@ -942,6 +945,31 @@ int main ( int argc, char *argv[] ) // Parse command line for settings, independent of other -no-config. config_parse_cmd_options_dynamic ( ); + if ( config.theme ) { + TICK_N ( "Parse theme" ); + rofi_theme_parse_file ( config.theme ); + TICK_N ( "Parsed theme" ); + } else { + rofi_theme_convert_old_theme ( ); + } + + const char ** theme_str = find_arg_strv ( "-theme-str" ); + if ( theme_str ) { + for ( int index = 0; theme_str && theme_str[index]; index++ ){ + if ( ! rofi_theme_parse_string ( theme_str[index] ) ){ + fprintf(stderr, "Failed to parse: %s\n", theme_str[index]); + exit ( EXIT_FAILURE ); + } + } + g_free ( theme_str ); + } + + + + if ( find_arg ( "-dump-theme" ) >= 0 ){ + rofi_theme_print ( rofi_theme ); + exit (EXIT_SUCCESS); + } // Dump. // catch help request if ( find_arg ( "-h" ) >= 0 || find_arg ( "-help" ) >= 0 || find_arg ( "--help" ) >= 0 ) { diff --git a/source/theme.c b/source/theme.c new file mode 100644 index 00000000..ed8b7bd1 --- /dev/null +++ b/source/theme.c @@ -0,0 +1,840 @@ +#include +#include +#include +#include +#include "theme.h" +#include "lexer/theme-parser.h" +#include "helper.h" +#include "settings.h" +#include "widgets/textbox.h" +#include "view.h" + +/** Logging domain for theme */ +#define LOG_DOMAIN "Theme" + +void yyerror ( YYLTYPE *ylloc, const char *); +static gboolean distance_compare ( Distance d, Distance e ) +{ + return ( d.type == e.type && d.distance == e.distance && d.style == e.style ); +} + +ThemeWidget *rofi_theme_find_or_create_name ( ThemeWidget *base, const char *name ) +{ + for ( unsigned int i = 0; i < base->num_widgets;i++){ + if ( g_strcmp0(base->widgets[i]->name, name) == 0 ){ + return base->widgets[i]; + } + } + + base->widgets = g_realloc ( base->widgets, sizeof(ThemeWidget*)*(base->num_widgets+1)); + base->widgets[base->num_widgets] = g_malloc0(sizeof(ThemeWidget)); + ThemeWidget *retv = base->widgets[base->num_widgets]; + retv->parent = base; + retv->name = g_strdup(name); + base->num_widgets++; + return retv; +} +/** + * Properties + */ +Property *rofi_theme_property_create ( PropertyType type ) +{ + Property *retv = g_malloc0 ( sizeof(Property) ); + retv->type = type; + return retv; +} +void rofi_theme_property_free ( Property *p ) +{ + if ( p == NULL ) { + return; + } + g_free ( p->name ); + if ( p->type == P_STRING ) { + g_free ( p->value.s ); + } else if ( p->type == P_LINK ) { + g_free ( p->value.link.name ); + } + g_free(p); +} + +void rofi_theme_free ( ThemeWidget *widget ) +{ + if ( widget == NULL ){ + return; + } + if ( widget->properties ) { + g_hash_table_destroy ( widget->properties ); + } + for ( unsigned int i = 0; i < widget->num_widgets; i++ ){ + rofi_theme_free ( widget->widgets[i] ); + } + g_free ( widget->widgets ); + g_free ( widget->name ); + g_free ( widget ); +} + +/** + * print + */ +static void rofi_theme_print_distance ( Distance d ) +{ + if ( d.type == PW_PX ) { + printf("%upx ", (int)d.distance ); + } else if ( d.type == PW_PERCENT ) { + printf("%f%% ", d.distance ); + } else { + printf("%fem ", d.distance ); + } + if ( d.style == DASH ) { + printf("dash "); + } +} +/** Textual representation of Window Location */ +const char const * WindowLocationStr[9] = { + "center", + "northwest", + "north", + "northeast", + "east", + "southeast", + "south", + "southwest", + "west" +}; + +static void rofi_theme_print_property_index ( size_t pnl, int depth, Property *p ) +{ + int pl = strlen ( p->name ); + printf("%*s%s:%*s ", depth, "", p->name, (int)pnl-pl,"" ); + switch ( p->type ) + { + case P_POSITION: + printf("%s;", WindowLocationStr[p->value.i]); + break; + case P_STRING: + printf("\"%s\";", p->value.s); + break; + case P_INTEGER: + printf("%d;", p->value.i); + break; + case P_DOUBLE: + printf("%.2f;", p->value.f); + break; + case P_BOOLEAN: + printf("%s;", p->value.b?"true":"false"); + break; + case P_COLOR: + printf("#%02X%02X%02X%02X;", + (unsigned char)(p->value.color.alpha*255.0), + (unsigned char)(p->value.color.red*255.0), + (unsigned char)(p->value.color.green*255.0), + (unsigned char)(p->value.color.blue*255.0)); + break; + case P_PADDING: + if ( distance_compare ( p->value.padding.top, p->value.padding.bottom) && + distance_compare ( p->value.padding.left, p->value.padding.right) && + distance_compare ( p->value.padding.left, p->value.padding.top) ) { + rofi_theme_print_distance ( p->value.padding.left ); + } else if ( distance_compare ( p->value.padding.top, p->value.padding.bottom) && + distance_compare ( p->value.padding.left, p->value.padding.right)){ + rofi_theme_print_distance ( p->value.padding.top ); + rofi_theme_print_distance ( p->value.padding.left ); + } else if ( !distance_compare ( p->value.padding.top, p->value.padding.bottom) && + distance_compare ( p->value.padding.left, p->value.padding.right)){ + rofi_theme_print_distance ( p->value.padding.top ); + rofi_theme_print_distance ( p->value.padding.left ); + rofi_theme_print_distance ( p->value.padding.bottom); + } else { + rofi_theme_print_distance ( p->value.padding.top ); + rofi_theme_print_distance ( p->value.padding.right ); + rofi_theme_print_distance ( p->value.padding.bottom); + rofi_theme_print_distance ( p->value.padding.left ); + } + printf(";"); + break; + case P_LINK: + printf("%s;", p->value.link.name); + break; + } + putchar ( '\n' ); +} + +static void rofi_theme_print_index ( ThemeWidget *widget ) +{ + GHashTableIter iter; + gpointer key, value; + if ( widget->properties ){ + int index = 0; + GList *list = NULL; + ThemeWidget *w = widget; + while ( w){ + if ( g_strcmp0(w->name,"Root") == 0 ) { + break; + } + list = g_list_prepend ( list, w->name ); + w = w->parent; + } + if ( g_list_length ( list ) > 0 ) { + index = 4; + for ( GList *iter = g_list_first ( list ); iter != NULL; iter = g_list_next ( iter ) ) { + char *name = (char *)iter->data; + if ( iter->prev == NULL ){ + putchar ( '#' ); + } + fputs(name, stdout); + if ( iter->next ) { + putchar('.'); + } + } + printf(" {\n"); + } else { + index = 4; + printf("* {\n"); + } + size_t property_name_length = 0; + g_hash_table_iter_init (&iter, widget->properties); + while (g_hash_table_iter_next (&iter, &key, &value)) + { + Property *p = (Property*)value; + property_name_length = MAX ( strlen (p->name), property_name_length ); + } + g_hash_table_iter_init (&iter, widget->properties); + while (g_hash_table_iter_next (&iter, &key, &value)) + { + Property *p = (Property*)value; + rofi_theme_print_property_index ( property_name_length, index, p ); + } + printf("}\n"); + g_list_free ( list ); + } + for ( unsigned int i = 0; i < widget->num_widgets;i++){ + rofi_theme_print_index ( widget->widgets[i] ); + } +} +void rofi_theme_print ( ThemeWidget *widget ) +{ + rofi_theme_print_index ( widget ); +} + +/** + * Main lex parser. + */ +int yyparse(); + +/** + * Destroy the internal of lex parser. + */ +void yylex_destroy( void ); + +/** + * Global handle input file to flex parser. + */ +extern FILE* yyin; + +/** + * @param yylloc The file location. + * @param s Error message string. + * + * Error handler for the lex parser. + */ +void yyerror(YYLTYPE *yylloc, const char* s) { + fprintf(stderr, "Parse error: %s\n", s); + fprintf(stderr, "From line %d column %d to line %d column %d\n", yylloc->first_line, yylloc->first_column, yylloc->last_line, yylloc->last_column); + exit(EXIT_FAILURE); +} + +static gboolean rofi_theme_steal_property_int ( gpointer key, gpointer value, gpointer user_data) +{ + GHashTable *table = (GHashTable*)user_data; + g_hash_table_replace ( table, key, value); + return TRUE; +} +void rofi_theme_widget_add_properties ( ThemeWidget *widget, GHashTable *table ) +{ + if ( table == NULL ) { + return; + } + if ( widget->properties == NULL ){ + widget->properties = table; + return; + } + g_hash_table_foreach_steal ( table, rofi_theme_steal_property_int, widget->properties ); + g_hash_table_destroy ( table ); +} + + +/** + * Public API + */ + +static ThemeWidget *rofi_theme_find_single ( ThemeWidget *widget, const char *name) +{ + for ( unsigned int j = 0; j < widget->num_widgets;j++){ + if ( g_strcmp0(widget->widgets[j]->name, name ) == 0 ){ + return widget->widgets[j]; + } + } + return widget; +} + +static ThemeWidget *rofi_theme_find ( ThemeWidget *widget , const char *name, const gboolean exact ) +{ + if ( widget == NULL || name == NULL ) { + return widget; + } + char **names = g_strsplit ( name, "." , 0 ); + int found = TRUE; + for ( unsigned int i = 0; found && names && names[i]; i++ ){ + found = FALSE; + ThemeWidget *f = rofi_theme_find_single ( widget, names[i]); + if ( f != widget ){ + widget = f; + found = TRUE; + } + } + g_strfreev(names); + if ( !exact || found ){ + return widget; + } else { + return NULL; + } +} + +static void rofi_theme_resolve_link_property ( Property *p, int depth ) +{ + // Set name, remove '@' prefix. + const char *name = p->value.link.name +1; + if ( depth > 20 ){ + g_log ( LOG_DOMAIN, G_LOG_LEVEL_WARNING, "Found more then 20 redirects for property. Stopping."); + p->value.link.ref = p; + return; + } + + if( g_hash_table_contains ( rofi_theme->properties, name ) ) { + Property *pr = g_hash_table_lookup ( rofi_theme->properties, name ); + if ( pr->type == P_LINK ) { + if ( pr->value.link.ref == NULL ) { + rofi_theme_resolve_link_property ( pr, depth+1); + } + if ( pr->value.link.ref != pr ){ + p->value.link.ref = pr->value.link.ref; + return; + } + } else { + p->value.link.ref = pr; + return; + } + } + + // No found, set ref to self. + p->value.link.ref = p; +} + +static Property *rofi_theme_find_property ( ThemeWidget *widget, PropertyType type, const char *property, gboolean exact ) +{ + while ( widget ) { + if ( widget->properties && g_hash_table_contains ( widget->properties, property) ) { + Property *p = g_hash_table_lookup ( widget->properties, property); + if ( p->type == P_LINK ) { + if ( p->value.link.ref == NULL ) { + // Resolve link. + rofi_theme_resolve_link_property ( p, 0 ); + } + if ( p->value.link.ref->type == type ){ + return p->value.link.ref; + } + } + if ( p->type == type ){ + return p; + } + // Padding and integer can be converted. + if ( p->type == P_INTEGER && type == P_PADDING ){ + return p; + } + } + if ( exact ) { + return NULL; + } + widget = widget->parent; + } + return NULL; +} +static ThemeWidget *rofi_theme_find_widget ( const char *name, const char *state, gboolean exact ) +{ + // First find exact match based on name. + ThemeWidget *widget = rofi_theme_find ( rofi_theme, name, exact ); + widget = rofi_theme_find ( widget, state, exact ); + + return widget; +} + +int rofi_theme_get_position ( const widget *widget, const char *property, int def ) +{ + ThemeWidget *wid = rofi_theme_find_widget ( widget->name, widget->state, FALSE ); + Property *p = rofi_theme_find_property ( wid, P_POSITION, property, FALSE ); + if ( p ){ + return p->value.i; + } + g_log ( LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Theme entry: #%s %s property %s unset.", widget->name, widget->state?widget->state:"", property ); + return def; +} + +int rofi_theme_get_integer ( const widget *widget, const char *property, int def ) +{ + ThemeWidget *wid = rofi_theme_find_widget ( widget->name, widget->state, FALSE ); + Property *p = rofi_theme_find_property ( wid, P_INTEGER, property, FALSE ); + if ( p ){ + return p->value.i; + } + g_log ( LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Theme entry: #%s %s property %s unset.", widget->name, widget->state?widget->state:"", property ); + return def; +} +int rofi_theme_get_integer_exact ( const widget *widget, const char *property, int def ) +{ + ThemeWidget *wid = rofi_theme_find_widget ( widget->name, widget->state, TRUE ); + Property *p = rofi_theme_find_property ( wid, P_INTEGER, property, TRUE ); + if ( p ){ + return p->value.i; + } + g_log ( LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Theme entry: #%s %s property %s unset.", widget->name, widget->state?widget->state:"", property ); + return def; +} + +Distance rofi_theme_get_distance ( const widget *widget, const char *property, int def ) +{ + ThemeWidget *wid = rofi_theme_find_widget ( widget->name, widget->state, FALSE ); + Property *p = rofi_theme_find_property ( wid, P_PADDING, property, FALSE ); + if ( p ){ + if ( p->type == P_INTEGER ){ + return (Distance){p->value.i,PW_PX, SOLID}; + } else { + return p->value.padding.left; + } + } + g_log ( LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Theme entry: #%s %s property %s unset.", widget->name, widget->state?widget->state:"", property ); + return (Distance){def, PW_PX, SOLID}; +} + +int rofi_theme_get_boolean ( const widget *widget, const char *property, int def ) +{ + ThemeWidget *wid = rofi_theme_find_widget ( widget->name, widget->state, FALSE ); + Property *p = rofi_theme_find_property ( wid, P_BOOLEAN, property, FALSE ); + if ( p ){ + return p->value.b; + } + g_log ( LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Theme entry: #%s %s property %s unset.", widget->name, widget->state?widget->state:"", property ); + return def; +} + +char *rofi_theme_get_string ( const widget *widget, const char *property, char *def ) +{ + ThemeWidget *wid = rofi_theme_find_widget ( widget->name, widget->state, FALSE ); + Property *p = rofi_theme_find_property ( wid, P_STRING, property, FALSE ); + if ( p ){ + return p->value.s; + } + g_log ( LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Theme entry: #%s %s property %s unset.", widget->name, widget->state?widget->state:"", property ); + return def; +} +double rofi_theme_get_double ( const widget *widget, const char *property, double def ) +{ + ThemeWidget *wid = rofi_theme_find_widget ( widget->name, widget->state, FALSE ); + Property *p = rofi_theme_find_property ( wid, P_DOUBLE, property, FALSE ); + if ( p ){ + return p->value.b; + } + g_log ( LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Theme entry: #%s %s property %s unset.", widget->name, widget->state?widget->state:"", property ); + return def; +} +void rofi_theme_get_color ( const widget *widget, const char *property, cairo_t *d) +{ + ThemeWidget *wid = rofi_theme_find_widget ( widget->name, widget->state, FALSE ); + Property *p = rofi_theme_find_property ( wid, P_COLOR, property, FALSE ); + if ( p ){ + cairo_set_source_rgba ( d, + p->value.color.red, + p->value.color.green, + p->value.color.blue, + p->value.color.alpha + ); + } else { + g_log ( LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Theme entry: #%s %s property %s unset.", widget->name, widget->state?widget->state:"", property ); + } +} +Padding rofi_theme_get_padding ( const widget *widget, const char *property, Padding pad ) +{ + ThemeWidget *wid = rofi_theme_find_widget ( widget->name, widget->state, FALSE ); + Property *p = rofi_theme_find_property ( wid, P_PADDING, property, FALSE ); + if ( p ){ + if ( p->type == P_PADDING ){ + pad = p->value.padding; + } else { + Distance d = (Distance){p->value.i, PW_PX, SOLID}; + return (Padding){d,d,d,d}; + } + } + g_log ( LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Theme entry: #%s %s property %s unset.", widget->name, widget->state?widget->state:"", property ); + return pad; +} +int distance_get_pixel ( Distance d, Orientation ori ) +{ + if ( d.type == PW_EM ){ + return d.distance*textbox_get_estimated_char_height(); + } else if ( d.type == PW_PERCENT ) { + if ( ori == ORIENTATION_VERTICAL ){ + int height = 0; + rofi_view_get_current_monitor ( NULL, &height ); + return (d.distance*height)/(100.0); + } else { + int width = 0; + rofi_view_get_current_monitor ( &width, NULL ); + return (d.distance*width)/(100.0); + + } + } + return d.distance; +} + +void distance_get_linestyle ( Distance d, cairo_t *draw ) +{ + if ( d.style == DASH ){ + const double dashes[1] = { 4 }; + cairo_set_dash ( draw, dashes, 1, 0.0 ); + } else { + cairo_set_dash ( draw, NULL, 0, 0.0); + } +} + +#ifdef THEME_CONVERTER + +static Property* rofi_theme_convert_get_color ( const char *color, const char *name ) +{ + Color c = color_get ( color ); + Property *p = rofi_theme_property_create ( P_COLOR ); + p->name = g_strdup(name); + p->value.color.alpha = c.alpha; + p->value.color.red = c.red; + p->value.color.green = c.green; + p->value.color.blue = c.blue; + + return p; +} +static void rofi_theme_convert_create_property_ht ( ThemeWidget *widget ) +{ + if ( widget->properties == NULL ) { + widget->properties = g_hash_table_new_full ( g_str_hash, g_str_equal, NULL, (GDestroyNotify)rofi_theme_property_free ); + } +} + +void rofi_theme_convert_old_theme ( void ) +{ + if ( rofi_theme != NULL ){ + return; + } + rofi_theme = (ThemeWidget*)g_malloc0 ( sizeof ( ThemeWidget ) ); + rofi_theme->name = g_strdup ( "Root" ); + rofi_theme_convert_create_property_ht ( rofi_theme ); + ThemeWidget *window_widget = rofi_theme_find_or_create_name ( rofi_theme , "window" ); + rofi_theme_convert_create_property_ht ( window_widget ); + ThemeWidget *mainbox_widget = rofi_theme_find_or_create_name ( window_widget, "mainbox" ); + rofi_theme_convert_create_property_ht ( mainbox_widget ); + ThemeWidget *message = rofi_theme_find_or_create_name ( mainbox_widget, "message" ); + ThemeWidget *message_box = rofi_theme_find_or_create_name ( message, "box" ); + rofi_theme_convert_create_property_ht ( message_box ); + ThemeWidget *listview_widget = rofi_theme_find_or_create_name ( mainbox_widget, "listview" ); + rofi_theme_convert_create_property_ht ( listview_widget ); + ThemeWidget *sidebar_widget = rofi_theme_find_or_create_name ( mainbox_widget, "sidebar" ); + ThemeWidget *sidebarbox_widget = rofi_theme_find_or_create_name ( sidebar_widget, "box" ); + rofi_theme_convert_create_property_ht ( sidebarbox_widget ); + { + Property *p = rofi_theme_property_create ( P_INTEGER ); + p->name = g_strdup ("border"); + p->value.i = 0; + g_hash_table_replace ( mainbox_widget->properties, p->name, p); + + p = rofi_theme_property_create ( P_INTEGER ); + p->name = g_strdup ("padding"); + p->value.i = config.padding; + g_hash_table_replace ( window_widget->properties, p->name, p); + + p = rofi_theme_property_create ( P_INTEGER ); + p->name = g_strdup ("padding"); + p->value.i = 0; + g_hash_table_replace ( mainbox_widget->properties, p->name, p); + // Spacing + p = rofi_theme_property_create ( P_INTEGER ); + p->name = g_strdup("spacing"); + p->value.i = config.line_margin; + g_hash_table_replace ( rofi_theme->properties, p->name, p ); + } + { + // Background + Property *p = rofi_theme_property_create ( P_COLOR ); + p->name = g_strdup("background"); + p->value.color.alpha = 0; + p->value.color.red = 0; + p->value.color.green= 0; + p->value.color.blue= 0; + g_hash_table_replace ( rofi_theme->properties, p->name, p ); + + ThemeWidget *inputbar_widget = rofi_theme_find_or_create_name ( mainbox_widget, "inputbar" ); + rofi_theme_convert_create_property_ht ( inputbar_widget ); + p = rofi_theme_property_create ( P_INTEGER ); + p->name = g_strdup("spacing"); + p->value.i = 0; + g_hash_table_replace ( inputbar_widget->properties, p->name, p ); + + + LineStyle style = (g_strcmp0(config.separator_style,"dash") == 0)?DASH:SOLID; + int place_end = ( config.location == WL_SOUTH_EAST || config.location == WL_SOUTH || config.location == WL_SOUTH_WEST ); + p = rofi_theme_property_create ( P_PADDING ); + p->name = g_strdup("border"); + Distance d = (Distance){config.menu_bw, PW_PX, style}; + if ( place_end ){ + p->value.padding.bottom= d; + } else { + p->value.padding.top= d; + } + g_hash_table_replace ( listview_widget->properties, p->name, p ); + + + p = rofi_theme_property_create ( P_PADDING ); + p->name = g_strdup("border"); + d = (Distance){config.menu_bw, PW_PX, style}; + if ( place_end ){ + p->value.padding.bottom= d; + } else { + p->value.padding.top= d; + } + g_hash_table_replace ( message_box->properties, p->name, p ); + + /** + * Sidebar top + */ + p = rofi_theme_property_create ( P_PADDING ); + p->name = g_strdup("border"); + d = (Distance){config.menu_bw, PW_PX, style}; + p->value.padding.top= d; + g_hash_table_replace ( sidebarbox_widget->properties, p->name, p ); + + p = rofi_theme_property_create ( P_PADDING ); + p->name = g_strdup("padding"); + d = (Distance){config.line_margin, PW_PX, SOLID}; + if ( place_end ){ + p->value.padding.bottom= d; + } else { + p->value.padding.top= d; + } + g_hash_table_replace ( listview_widget->properties, p->name, p ); + + p = rofi_theme_property_create ( P_PADDING ); + p->name = g_strdup("padding"); + d = (Distance){config.line_margin, PW_PX, SOLID}; + if ( place_end ){ + p->value.padding.bottom= d; + } else { + p->value.padding.top= d; + } + g_hash_table_replace ( message_box->properties, p->name, p ); + + } + { + Property *p = rofi_theme_property_create ( P_INTEGER ); + p->name = g_strdup("columns"); + p->value.i = config.menu_columns; + g_hash_table_replace ( listview_widget->properties, p->name, p ); + p = rofi_theme_property_create ( P_INTEGER ); + p->name = g_strdup("fixed-height"); + p->value.i = !(config.fixed_num_lines); + g_hash_table_replace ( listview_widget->properties, p->name, p ); + } + { + // Border width. + rofi_theme_convert_create_property_ht ( window_widget ); + // Padding + Property *p = rofi_theme_property_create ( P_INTEGER ); + p->name = g_strdup("padding"); + p->value.i = config.padding; + g_hash_table_replace ( window_widget->properties, p->name, p ); + + p = rofi_theme_property_create ( P_INTEGER ); + p->name = g_strdup("border"); + p->value.i = config.menu_bw; + g_hash_table_replace ( window_widget->properties, p->name, p ); + } + { + gchar **vals = g_strsplit ( config.color_window, ",", 3 ); + if ( vals != NULL ){ + if ( vals[0] != NULL ) { + Property *p = rofi_theme_convert_get_color ( vals[0], "background" ); + g_hash_table_replace ( window_widget->properties, p->name, p ); + + if ( vals[1] != NULL ) { + p = rofi_theme_convert_get_color ( vals[1], "foreground" ); + g_hash_table_replace ( window_widget->properties, p->name, p ); + + ThemeWidget *inputbar = rofi_theme_find_or_create_name ( mainbox_widget, "inputbar" ); + ThemeWidget *widget = rofi_theme_find_or_create_name ( inputbar, "box" ); + rofi_theme_convert_create_property_ht ( widget ); + if ( vals[2] != NULL ) { + p = rofi_theme_convert_get_color ( vals[2], "foreground" ); + g_hash_table_replace ( window_widget->properties, p->name, p ); + } else { + p = rofi_theme_convert_get_color ( vals[1], "foreground" ); + g_hash_table_replace ( window_widget->properties, p->name, p ); + } + } + + } + } + g_strfreev ( vals ); + { + Property *p = NULL; + ThemeWidget *widget = rofi_theme_find_or_create_name ( listview_widget, "element" ); + ThemeWidget *scrollbar = rofi_theme_find_or_create_name ( listview_widget, "scrollbar" ); + + + ThemeWidget *wnormal = rofi_theme_find_or_create_name ( widget, "normal" ); + ThemeWidget *wselected = rofi_theme_find_or_create_name ( widget, "selected" ); + ThemeWidget *walternate = rofi_theme_find_or_create_name ( widget, "alternate" ); + + rofi_theme_convert_create_property_ht ( widget ); + p = rofi_theme_property_create ( P_INTEGER ); + p->name = g_strdup ("border"); + p->value.i = 0; + g_hash_table_replace ( widget->properties, p->name, p); + + rofi_theme_convert_create_property_ht ( scrollbar ); + p = rofi_theme_property_create ( P_INTEGER ); + p->name = g_strdup ("border"); + p->value.i = 0; + g_hash_table_replace ( scrollbar->properties, p->name, p); + p = rofi_theme_property_create ( P_INTEGER ); + p->name = g_strdup ("padding"); + p->value.i = 0; + g_hash_table_replace ( scrollbar->properties, p->name, p); + + + gchar **vals = g_strsplit ( config.color_normal, ",", 5 ); + if ( g_strv_length (vals) == 5 ) { + ThemeWidget *wnn = rofi_theme_find_or_create_name ( wnormal, "normal" ); + rofi_theme_convert_create_property_ht ( wnn ); + p = rofi_theme_convert_get_color ( vals[0], "background" ); + g_hash_table_replace ( wnn->properties, p->name, p ); + p = rofi_theme_convert_get_color ( vals[1], "foreground" ); + g_hash_table_replace ( wnn->properties, p->name, p ); + + ThemeWidget *wsl = rofi_theme_find_or_create_name ( wselected, "normal" ); + rofi_theme_convert_create_property_ht ( wsl ); + p = rofi_theme_convert_get_color ( vals[3], "background" ); + g_hash_table_replace ( wsl->properties, p->name, p ); + p = rofi_theme_convert_get_color ( vals[4], "foreground" ); + g_hash_table_replace ( wsl->properties, p->name, p ); + + ThemeWidget *wal = rofi_theme_find_or_create_name ( walternate, "normal" ); + rofi_theme_convert_create_property_ht ( wal ); + p = rofi_theme_convert_get_color ( vals[2], "background" ); + g_hash_table_replace ( wal->properties, p->name, p ); + p = rofi_theme_convert_get_color ( vals[1], "foreground" ); + g_hash_table_replace ( wal->properties, p->name, p ); + + ThemeWidget *inputbar = rofi_theme_find_or_create_name ( mainbox_widget, "inputbar" ); + wnn = rofi_theme_find_or_create_name ( inputbar, "normal" ); + rofi_theme_convert_create_property_ht ( wnn ); + p = rofi_theme_convert_get_color ( vals[0], "background" ); + g_hash_table_replace ( wnn->properties, p->name, p ); + p = rofi_theme_convert_get_color ( vals[1], "foreground" ); + g_hash_table_replace ( wnn->properties, p->name, p ); + + wnn = rofi_theme_find_or_create_name ( message, "normal" ); + rofi_theme_convert_create_property_ht ( wnn ); + p = rofi_theme_convert_get_color ( vals[0], "background" ); + g_hash_table_replace ( wnn->properties, p->name, p ); + p = rofi_theme_convert_get_color ( vals[1], "foreground" ); + g_hash_table_replace ( wnn->properties, p->name, p ); + } + g_strfreev ( vals ); + + vals = g_strsplit ( config.color_urgent, ",", 5 ); + if ( g_strv_length (vals) == 5 ) { + ThemeWidget *wnn = rofi_theme_find_or_create_name ( wnormal, "urgent" ); + rofi_theme_convert_create_property_ht ( wnn ); + p = rofi_theme_convert_get_color ( vals[0], "background" ); + g_hash_table_replace ( wnn->properties, p->name, p ); + p = rofi_theme_convert_get_color ( vals[1], "foreground" ); + g_hash_table_replace ( wnn->properties, p->name, p ); + + ThemeWidget *wsl = rofi_theme_find_or_create_name ( wselected, "urgent" ); + rofi_theme_convert_create_property_ht ( wsl ); + p = rofi_theme_convert_get_color ( vals[3], "background" ); + g_hash_table_replace ( wsl->properties, p->name, p ); + p = rofi_theme_convert_get_color ( vals[4], "foreground" ); + g_hash_table_replace ( wsl->properties, p->name, p ); + + ThemeWidget *wal = rofi_theme_find_or_create_name ( walternate, "urgent" ); + rofi_theme_convert_create_property_ht ( wal ); + p = rofi_theme_convert_get_color ( vals[2], "background" ); + g_hash_table_replace ( wal->properties, p->name, p ); + p = rofi_theme_convert_get_color ( vals[1], "foreground" ); + g_hash_table_replace ( wal->properties, p->name, p ); + } + g_strfreev ( vals ); + + vals = g_strsplit ( config.color_active, ",", 5 ); + if ( g_strv_length (vals) == 5 ) { + ThemeWidget *wnn = rofi_theme_find_or_create_name ( wnormal, "active" ); + rofi_theme_convert_create_property_ht ( wnn ); + p = rofi_theme_convert_get_color ( vals[0], "background" ); + g_hash_table_replace ( wnn->properties, p->name, p ); + p = rofi_theme_convert_get_color ( vals[1], "foreground" ); + g_hash_table_replace ( wnn->properties, p->name, p ); + + ThemeWidget *wsl = rofi_theme_find_or_create_name ( wselected, "active" ); + rofi_theme_convert_create_property_ht ( wsl ); + p = rofi_theme_convert_get_color ( vals[3], "background" ); + g_hash_table_replace ( wsl->properties, p->name, p ); + p = rofi_theme_convert_get_color ( vals[4], "foreground" ); + g_hash_table_replace ( wsl->properties, p->name, p ); + + ThemeWidget *wal = rofi_theme_find_or_create_name ( walternate, "active" ); + rofi_theme_convert_create_property_ht ( wal ); + p = rofi_theme_convert_get_color ( vals[2], "background" ); + g_hash_table_replace ( wal->properties, p->name, p ); + p = rofi_theme_convert_get_color ( vals[1], "foreground" ); + g_hash_table_replace ( wal->properties, p->name, p ); + } + g_strfreev ( vals ); + } + } +} +gboolean rofi_theme_parse_file ( const char *file ) +{ + char *filename = rofi_expand_path ( file ); + yyin = fopen ( filename, "rb"); + if ( yyin == NULL ){ + fprintf(stderr, "Failed to open file: %s: '%s'\n", filename, strerror ( errno ) ); + g_free(filename); + return TRUE; + } + extern int str_len; + extern const char*input_str; + str_len = 0; + input_str = NULL; + while ( yyparse() ); + yylex_destroy(); + g_free(filename); + yyin = NULL; + return FALSE; +} +gboolean rofi_theme_parse_string ( const char *string ) +{ + extern int str_len; + extern const char*input_str; + yyin = NULL; + input_str = string; + str_len = strlen ( string ); + while ( yyparse () ); + yylex_destroy(); + return TRUE; +} +#endif diff --git a/source/view.c b/source/view.c index 9ff53a68..396c5b2f 100644 --- a/source/view.c +++ b/source/view.c @@ -58,10 +58,19 @@ #include "view.h" #include "view-internal.h" +#include "theme.h" + /** The Rofi View log domain */ #define LOG_DOMAIN "View" #include "xcb.h" +/** + * @param state The handle to the view + * @param qr Indicate if queue_redraw should be called on changes. + * + * Update the state of the view. This involves filter state. + */ +void rofi_view_update ( RofiViewState *state, gboolean qr ); static int rofi_view_calculate_height ( RofiViewState *state ); @@ -99,9 +108,11 @@ struct /** timeout for reloading */ guint idle_timeout; /** debug counter for redraws */ - uint64_t count; + unsigned long long count; /** redraw idle time. */ guint repaint_source; + /** Window fullscreen */ + gboolean fullscreen; } CacheState = { .main_window = XCB_WINDOW_NONE, .fake_bg = NULL, @@ -113,8 +124,18 @@ struct .idle_timeout = 0, .count = 0L, .repaint_source = 0, + .fullscreen = FALSE, }; +void rofi_view_get_current_monitor ( int *width, int *height) +{ + if (width ){ + *width = CacheState.mon.w; + } + if (height){ + *height = CacheState.mon.h; + } +} static char * get_matching_state ( void ) { if ( config.case_sensitive ) { @@ -200,6 +221,10 @@ static void menu_capture_screenshot ( void ) static gboolean rofi_view_repaint ( G_GNUC_UNUSED void * data ) { if ( current_active_menu ) { + // Repaint the view (if needed). + // After a resize the edit_pixmap surface might not contain anything anymore. + // If we already re-painted, this does nothing. + rofi_view_update (current_active_menu, FALSE); g_log ( LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "expose event" ); TICK_N ( "Expose" ); xcb_copy_area ( xcb->connection, CacheState.edit_pixmap, CacheState.main_window, CacheState.gc, @@ -231,22 +256,29 @@ static void rofi_view_update_prompt ( RofiViewState *state ) */ static void rofi_view_calculate_window_position ( RofiViewState *state ) { - if ( config.fullscreen ) { + int location = rofi_theme_get_position ( WIDGET ( state->main_window ), "location", config.location ); + int anchor = location; + if ( !listview_get_fixed_num_lines ( state->list_view ) ) { + anchor = location; + if ( location == WL_CENTER ) { + anchor = WL_NORTH; + } else if ( location == WL_EAST ) { + anchor = WL_NORTH_EAST; + } else if (location == WL_WEST ) { + anchor = WL_NORTH_WEST; + } + } + anchor = rofi_theme_get_position ( WIDGET ( state->main_window ), "anchor", anchor ); + + if ( CacheState.fullscreen ) { state->x = CacheState.mon.x; state->y = CacheState.mon.y; return; } - - 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 - widget_get_height ( WIDGET ( state->input_bar ) ); - } - else { - // Default location is center. - state->y = CacheState.mon.y + ( CacheState.mon.h - state->height ) / 2; - } - state->x = CacheState.mon.x + ( CacheState.mon.w - state->width ) / 2; + state->y = CacheState.mon.y + ( CacheState.mon.h ) / 2; + state->x = CacheState.mon.x + ( CacheState.mon.w ) / 2; // Determine window location - switch ( config.location ) + switch ( location ) { case WL_NORTH_WEST: state->x = CacheState.mon.x; @@ -256,15 +288,15 @@ static void rofi_view_calculate_window_position ( RofiViewState *state ) case WL_NORTH_EAST: state->y = CacheState.mon.y; case WL_EAST: - state->x = CacheState.mon.x + CacheState.mon.w - state->width; + state->x = CacheState.mon.x + CacheState.mon.w; break; - case WL_EAST_SOUTH: - state->x = CacheState.mon.x + CacheState.mon.w - state->width; + case WL_SOUTH_EAST: + state->x = CacheState.mon.x + CacheState.mon.w; case WL_SOUTH: - state->y = CacheState.mon.y + CacheState.mon.h - state->height; + state->y = CacheState.mon.y + CacheState.mon.h; break; case WL_SOUTH_WEST: - state->y = CacheState.mon.y + CacheState.mon.h - state->height; + state->y = CacheState.mon.y + CacheState.mon.h; case WL_WEST: state->x = CacheState.mon.x; break; @@ -272,6 +304,41 @@ static void rofi_view_calculate_window_position ( RofiViewState *state ) default: break; } + switch ( anchor ) + { + case WL_SOUTH_WEST: + state->y -= state->height; + break; + case WL_SOUTH: + state->x -= state->width/2; + state->y -= state->height; + break; + case WL_SOUTH_EAST: + state->x -= state->width; + state->y -= state->height; + break; + case WL_NORTH_EAST: + state->x -= state->width; + break; + case WL_NORTH_WEST: + break; + case WL_NORTH: + state->x -= state->width/2; + break; + case WL_EAST: + state->x -= state->width; + state->y -= state->height/2; + break; + case WL_WEST: + state->y -= state->height/2; + break; + case WL_CENTER: + state->y -= state->height/2; + state->x -= state->width/2; + break; + default: + break; + } // Apply offset. state->x += config.x_offset; state->y += config.y_offset; @@ -294,7 +361,10 @@ static void rofi_view_window_update_size ( RofiViewState * state ) CacheState.edit_surf = cairo_xcb_surface_create ( xcb->connection, CacheState.edit_pixmap, visual, state->width, state->height ); CacheState.edit_draw = cairo_create ( CacheState.edit_surf ); - widget_resize ( WIDGET ( state->main_box ), state->width - 2 * state->border, state->height - 2 * state->border ); + + g_log ( LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Re-size window based internal request: %dx%d.", state->width, state->height ); + // Should wrap main window in a widget. + widget_resize ( WIDGET ( state->main_window ), state->width, state->height ); } static gboolean rofi_view_reload_idle ( G_GNUC_UNUSED gpointer data ) @@ -319,7 +389,7 @@ void rofi_view_queue_redraw ( void ) { if ( current_active_menu && CacheState.repaint_source == 0 ) { CacheState.count++; - g_log ( LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "redraw %lu", CacheState.count ); + g_log ( LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "redraw %llu", CacheState.count ); CacheState.repaint_source = g_idle_add_full ( G_PRIORITY_HIGH_IDLE, rofi_view_repaint, NULL, NULL ); } } @@ -382,7 +452,7 @@ void rofi_view_free ( RofiViewState *state ) } // Do this here? // Wait for final release? - widget_free ( WIDGET ( state->main_box ) ); + widget_free ( WIDGET ( state->main_window ) ); widget_free ( WIDGET ( state->overlay ) ); g_free ( state->line_map ); @@ -489,23 +559,26 @@ static void filter_elements ( thread_state *t, G_GNUC_UNUSED gpointer user_data } } -static void rofi_view_setup_fake_transparency ( void ) +static void rofi_view_setup_fake_transparency ( const char const *fake_background ) { if ( CacheState.fake_bg == NULL ) { cairo_surface_t *s = NULL; /** * Select Background to use for fake transparency. - * Current options: 'screenshot','background' + * Current options: 'real', 'screenshot','background' */ TICK_N ( "Fake start" ); - if ( g_strcmp0 ( config.fake_background, "screenshot" ) == 0 ) { + if ( g_strcmp0 ( fake_background, "real" ) == 0 ){ + return; + } + else if ( g_strcmp0 ( fake_background, "screenshot" ) == 0 ) { s = x11_helper_get_screenshot_surface (); } - else if ( g_strcmp0 ( config.fake_background, "background" ) == 0 ) { + else if ( g_strcmp0 ( fake_background, "background" ) == 0 ) { s = x11_helper_get_bg_surface (); } else { - char *fpath = rofi_expand_path ( config.fake_background ); + char *fpath = rofi_expand_path ( fake_background ); g_log ( LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Opening %s to use as background.", fpath ); s = cairo_image_surface_create_from_png ( fpath ); CacheState.fake_bgrel = TRUE; @@ -584,8 +657,11 @@ void __create_window ( MenuFlags menu_flags ) pango_cairo_font_map_set_resolution ( (PangoCairoFontMap *) font_map, (double) config.dpi ); } // Setup font. - if ( config.menu_font ) { - PangoFontDescription *pfd = pango_font_description_from_string ( config.menu_font ); + // Dummy widget. + container *win = container_create ( "window" ); + char *font = rofi_theme_get_string ( WIDGET ( win ), "font" , config.menu_font ); + if ( font ) { + PangoFontDescription *pfd = pango_font_description_from_string ( font ); pango_context_set_font_description ( p, pfd ); pango_font_description_free ( pfd ); } @@ -605,7 +681,9 @@ void __create_window ( MenuFlags menu_flags ) window_set_atom_prop ( box, xcb->ewmh._NET_WM_WINDOW_TYPE, &( xcb->ewmh._NET_WM_WINDOW_TYPE_NORMAL ), 1 ); x11_disable_decoration ( box ); } - if ( config.fullscreen ) { + + CacheState.fullscreen = rofi_theme_get_boolean ( WIDGET (win ), "fullscreen", config.fullscreen ); + if ( CacheState.fullscreen ) { xcb_atom_t atoms[] = { xcb->ewmh._NET_WM_STATE_FULLSCREEN, xcb->ewmh._NET_WM_STATE_ABOVE @@ -620,12 +698,18 @@ void __create_window ( MenuFlags menu_flags ) CacheState.main_window = box; CacheState.flags = menu_flags; monitor_active ( &( CacheState.mon ) ); - if ( config.fake_transparency ) { - rofi_view_setup_fake_transparency (); + + char *transparency = rofi_theme_get_string ( WIDGET ( win ), "transparency", NULL); + if ( transparency == NULL && config.fake_transparency ){ + transparency = config.fake_background; + } + if ( transparency ) { + rofi_view_setup_fake_transparency ( transparency ); } if ( xcb->sncontext != NULL ) { sn_launchee_context_setup_window ( xcb->sncontext, CacheState.main_window ); } + widget_free ( WIDGET ( win ) ); } /** @@ -633,20 +717,24 @@ void __create_window ( MenuFlags menu_flags ) * * Calculate the width of the window and the width of an element. */ -static void rofi_view_calculate_window_and_element_width ( RofiViewState *state ) +static void rofi_view_calculate_window_width ( RofiViewState *state ) { - if ( config.fullscreen ) { + if ( CacheState.fullscreen ) { state->width = CacheState.mon.w; + return; } - else if ( config.menu_width < 0 ) { + if ( config.menu_width < 0 ) { double fw = textbox_get_estimated_char_width ( ); state->width = -( fw * config.menu_width ); - state->width += 2 * state->border; + state->width += widget_padding_get_padding_width ( WIDGET ( state->main_window ) ); } 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; } + // Use theme configured width, if set. + Distance width = rofi_theme_get_distance ( WIDGET ( state->main_window ), "width", state->width ); + state->width = distance_get_pixel ( width, ORIENTATION_HORIZONTAL ); } /** @@ -758,15 +846,16 @@ static void update_callback ( textbox *t, unsigned int index, void *udata, TextB } } -void rofi_view_update ( RofiViewState *state ) +void rofi_view_update ( RofiViewState *state, gboolean qr ) { - if ( !widget_need_redraw ( WIDGET ( state->main_box ) ) && !widget_need_redraw ( WIDGET ( state->overlay ) ) ) { + if ( !widget_need_redraw ( WIDGET ( state->main_window ) ) && !widget_need_redraw ( WIDGET ( state->overlay ) ) ) { return; } + g_log ( LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Redraw view" ); TICK (); cairo_t *d = CacheState.edit_draw; cairo_set_operator ( d, CAIRO_OPERATOR_SOURCE ); - if ( config.fake_transparency && CacheState.fake_bg != NULL ) { + if ( CacheState.fake_bg != NULL ) { if ( CacheState.fake_bgrel ) { cairo_set_source_surface ( d, CacheState.fake_bg, 0.0, 0.0 ); } @@ -777,39 +866,26 @@ void rofi_view_update ( RofiViewState *state ) } cairo_paint ( d ); cairo_set_operator ( d, CAIRO_OPERATOR_OVER ); - color_background ( d ); - cairo_paint ( d ); } else { - // Paint the background. - color_background ( d ); + // Paint the background transparent. + cairo_set_source_rgba ( d, 0,0,0,0.0); cairo_paint ( d ); } TICK_N ( "Background" ); - color_border ( d ); - - if ( config.menu_bw > 0 ) { - cairo_save ( d ); - cairo_set_line_width ( d, config.menu_bw ); - cairo_rectangle ( d, - config.menu_bw / 2.0, - config.menu_bw / 2.0, - state->width - config.menu_bw, - state->height - config.menu_bw ); - cairo_stroke ( d ); - cairo_restore ( d ); - } // Always paint as overlay over the background. cairo_set_operator ( d, CAIRO_OPERATOR_OVER ); - widget_draw ( WIDGET ( state->main_box ), d ); + widget_draw ( WIDGET ( state->main_window ), d ); if ( state->overlay ) { widget_draw ( WIDGET ( state->overlay ), d ); } TICK_N ( "widgets" ); cairo_surface_flush ( CacheState.edit_surf ); - rofi_view_queue_redraw (); + if ( qr ) { + rofi_view_queue_redraw (); + } } /** @@ -828,9 +904,10 @@ static void rofi_view_paste ( RofiViewState *state, xcb_selection_notify_event_t if ( text != NULL && text[0] != '\0' ) { unsigned int dl = strlen ( text ); // Strip new line - while ( dl > 0 && text[dl] == '\n' ) { - text[dl] = '\0'; - dl--; + for ( unsigned int i = 0; i < dl; i++){ + if ( text[i] == '\n' ){ + dl = i; + } } // Insert string move cursor. textbox_insert ( state->text, state->text->cursor, text, dl ); @@ -865,9 +942,7 @@ static void rofi_view_mouse_navigation ( RofiViewState *state, xcb_button_press_ } else { 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 ) ) { + if ( widget_clicked ( WIDGET ( state->main_window ), &rel ) ) { return; } } @@ -879,6 +954,7 @@ static void _rofi_view_reload_row ( RofiViewState *state ) state->num_lines = mode_get_num_entries ( state->sw ); state->line_map = g_malloc0_n ( state->num_lines, sizeof ( unsigned int ) ); state->distance = g_malloc0_n ( state->num_lines, sizeof ( int ) ); + listview_set_max_lines ( state->list_view, state->num_lines ); } static void rofi_view_refilter ( RofiViewState *state ) @@ -962,7 +1038,9 @@ static void rofi_view_refilter ( RofiViewState *state ) state->retv = MENU_OK; state->quit = TRUE; } - if ( config.fixed_num_lines == FALSE && ( CacheState.flags & MENU_NORMAL_WINDOW ) == 0 ) { + + // Make sure we enable fixed num lines when in normal window mode. + if ( (CacheState.flags&MENU_NORMAL_WINDOW) == 0 ){ int height = rofi_view_calculate_height ( state ); if ( height != state->height ) { state->height = height; @@ -1244,6 +1322,7 @@ void rofi_view_itterrate ( RofiViewState *state, xcb_generic_event_t *ev, xkb_st if ( state->x != xce->x || state->y != xce->y ) { state->x = xce->x; state->y = xce->y; + widget_queue_redraw ( WIDGET ( state->main_window ) ); } if ( state->width != xce->width || state->height != xce->height ) { state->width = xce->width; @@ -1259,7 +1338,8 @@ void rofi_view_itterrate ( RofiViewState *state, xcb_generic_event_t *ev, xkb_st CacheState.edit_surf = cairo_xcb_surface_create ( xcb->connection, CacheState.edit_pixmap, visual, state->width, state->height ); CacheState.edit_draw = cairo_create ( CacheState.edit_surf ); - widget_resize ( WIDGET ( state->main_box ), state->width - 2 * state->border, state->height - 2 * state->border ); + g_log ( LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Re-size window based external request: %d %d\n", state->width, state->height); + widget_resize ( WIDGET ( state->main_window ), state->width, state->height ); } } break; @@ -1280,9 +1360,7 @@ void rofi_view_itterrate ( RofiViewState *state, xcb_generic_event_t *ev, xkb_st state->mouse_seen = TRUE; } xcb_motion_notify_event_t xme = *( (xcb_motion_notify_event_t *) ev ); - xme.event_x -= config.padding; - xme.event_y -= config.padding; - if ( widget_motion_notify ( WIDGET ( state->main_box ), &xme ) ) { + if ( widget_motion_notify ( WIDGET ( state->main_window ), &xme ) ) { return; } break; @@ -1340,7 +1418,7 @@ void rofi_view_itterrate ( RofiViewState *state, xcb_generic_event_t *ev, xkb_st if ( state->refilter ) { rofi_view_refilter ( state ); } - rofi_view_update ( state ); + rofi_view_update ( state, TRUE ); if ( ( ev->response_type & ~0x80 ) == XCB_EXPOSE && CacheState.repaint_source == 0 ) { CacheState.repaint_source = g_idle_add_full ( G_PRIORITY_HIGH_IDLE, rofi_view_repaint, NULL, NULL ); @@ -1350,19 +1428,13 @@ void rofi_view_itterrate ( RofiViewState *state, xcb_generic_event_t *ev, xkb_st static int rofi_view_calculate_height ( RofiViewState *state ) { unsigned int height = 0; - if ( config.menu_lines == 0 || config.fullscreen == TRUE ) { + if ( listview_get_num_lines ( state->list_view ) == 0 || CacheState.fullscreen == TRUE ) { height = CacheState.mon.h; return height; } - 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; + + widget *main_window = WIDGET ( state->main_window ); + height = widget_get_desired_height ( main_window ); return height; } @@ -1411,7 +1483,6 @@ RofiViewState *rofi_view_create ( Mode *sw, state->skip_absorb = FALSE; //We want to filter on the first run. state->refilter = TRUE; - state->border = config.padding + config.menu_bw; state->finalize = finalize; state->mouse_seen = FALSE; @@ -1423,115 +1494,91 @@ 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 ); + state->main_window = container_create ( "window" ); + state->main_box = box_create ( "window.mainbox.box", BOX_VERTICAL ); + container_add ( state->main_window, WIDGET ( state->main_box ) ); - // we need this at this point so we can get height. - 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 ( S_HORIZONTAL, 2 ); - separator_set_line_style_from_string ( state->input_bar_separator, config.separator_style ); + 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 ( BOX_HORIZONTAL, 0, 0, state->width - 2 * state->border, line_height ); - box_set_padding ( state->sidebar_bar, config.line_margin ); - separator *sep = separator_create ( S_HORIZONTAL, 2 ); - separator_set_line_style_from_string ( sep, config.separator_style ); - box_add ( state->main_box, WIDGET ( state->sidebar_bar ), FALSE, TRUE ); - box_add ( state->main_box, WIDGET ( sep ), FALSE, TRUE ); + 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 ( TB_CENTER, 0, 0, 0, 0, ( mode == state->sw ) ? HIGHLIGHT : NORMAL, + 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, FALSE ); + 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 ); } } - int end = ( config.location == WL_EAST_SOUTH || config.location == WL_SOUTH || config.location == WL_SOUTH_WEST ); - box_add ( state->main_box, WIDGET ( state->input_bar ), FALSE, end ); + 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 ( TB_AUTOWIDTH, 0, 0, 0, line_height, NORMAL, "*" ); + 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, TRUE ); + box_add ( state->input_bar, WIDGET ( state->case_indicator ), FALSE, 3 ); // Prompt box. - state->prompt = textbox_create ( TB_AUTOWIDTH, 0, 0, 0, line_height, NORMAL, "" ); + 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, FALSE ); + 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 ( tfl, 0, 0, 0, line_height, NORMAL, input ); + state->text = textbox_create ( "window.mainbox.inputbar.entry", tfl|TB_AUTOHEIGHT, NORMAL, input ); - box_add ( state->input_bar, WIDGET ( state->text ), TRUE, FALSE ); + box_add ( state->input_bar, WIDGET ( state->text ), TRUE, 2 ); textbox_text ( state->case_indicator, get_matching_state () ); if ( message ) { - textbox *message_tb = textbox_create ( TB_AUTOHEIGHT | TB_MARKUP | TB_WRAP, 0, 0, - state->width - ( 2 * ( state->border ) ), -1, NORMAL, message ); - separator *sep = separator_create ( S_HORIZONTAL, 2 ); - box_add ( state->main_box, WIDGET ( sep ), FALSE, end); - box_add ( state->main_box, WIDGET ( message_tb ), FALSE, end); - separator_set_line_style_from_string ( sep, config.separator_style ); + container *box = container_create ( "window.mainbox.message.box" ); + textbox *message_tb = textbox_create ( "window.mainbox.message.textbox", TB_AUTOHEIGHT | TB_MARKUP | TB_WRAP, NORMAL, message ); + container_add ( box, WIDGET (message_tb) ); + box_add ( state->main_box, WIDGET ( box ), FALSE, end?8:2); } - box_add ( state->main_box, WIDGET ( state->input_bar_separator ), FALSE, end ); - state->overlay = textbox_create ( TB_AUTOWIDTH, 0, 0, 20, line_height, URGENT, "blaat" ); + state->overlay = textbox_create ( "window.overlay", TB_AUTOWIDTH|TB_AUTOHEIGHT, URGENT, "blaat" ); widget_disable ( WIDGET ( state->overlay ) ); - state->list_view = listview_create ( update_callback, state, config.element_height ); + 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_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 ); - box_add ( state->main_box, WIDGET ( state->list_view ), TRUE, FALSE ); + 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 ); - - // Height of a row. - if ( config.menu_lines == 0 || config.fullscreen ) { - state->height = CacheState.mon.h; - // Autosize it. - config.fixed_num_lines = TRUE; - } + 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 ) ); state->distance = (int *) g_malloc0_n ( state->num_lines, sizeof ( int ) ); - state->height = rofi_view_calculate_height ( state ); - + rofi_view_calculate_window_width ( state ); + // Need to resize otherwise calculated desired height is wrong. + widget_resize ( WIDGET ( state->main_window ), state->width, 100); + // Only needed when window is fixed size. + if (( CacheState.flags & MENU_NORMAL_WINDOW ) == MENU_NORMAL_WINDOW ) { + listview_set_fixed_num_lines ( state->list_view ); + rofi_view_window_update_size ( state ); + } // Move the window to the correct x,y position. rofi_view_calculate_window_position ( state ); - rofi_view_window_update_size ( state ); - // Update. - //state->selected = 0; - state->quit = FALSE; rofi_view_refilter ( state ); - - rofi_view_update ( state ); + rofi_view_update ( state, TRUE ); xcb_map_window ( xcb->connection, CacheState.main_window ); - widget_queue_redraw ( WIDGET ( state->main_box ) ); + widget_queue_redraw ( WIDGET ( state->main_window ) ); xcb_flush ( xcb->connection ); if ( xcb->sncontext != NULL ) { sn_launchee_context_complete ( xcb->sncontext ); @@ -1543,22 +1590,27 @@ int rofi_view_error_dialog ( const char *msg, int markup ) { RofiViewState *state = __rofi_view_state_create (); state->retv = MENU_CANCEL; - state->border = config.padding + config.menu_bw; state->menu_flags = MENU_ERROR_DIALOG; state->finalize = process_result; - rofi_view_calculate_window_and_element_width ( state ); - 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 : "" ); - box_add ( state->main_box, WIDGET ( state->text ), TRUE, FALSE ); - unsigned int line_height = textbox_get_height ( state->text ); + state->main_window = container_create ( "window" ); + state->main_box = box_create ( "window.mainbox.message.box", BOX_VERTICAL); + container_add ( state->main_window, WIDGET ( state->main_box ) ); + state->text = textbox_create ( "window.mainbox.message.textbox", ( TB_AUTOHEIGHT | TB_WRAP ) + ( ( markup ) ? TB_MARKUP : 0 ), + NORMAL, ( msg != NULL ) ? msg : "" ); + box_add ( state->main_box, WIDGET ( state->text ), TRUE, 1 ); + + // Make sure we enable fixed num lines when in normal window mode. + if ( (CacheState.flags&MENU_NORMAL_WINDOW) == MENU_NORMAL_WINDOW){ + listview_set_fixed_num_lines ( state->list_view ); + } + rofi_view_calculate_window_width ( state ); + // Need to resize otherwise calculated desired height is wrong. + widget_resize ( WIDGET ( state->main_window ), state->width, 100); + unsigned int line_height = textbox_get_height ( state->text ); // resize window vertically to suit - state->height = line_height + ( state->border ) * 2; + state->height = line_height + widget_padding_get_padding_height ( WIDGET(state->main_window) ); // Calculte window position. rofi_view_calculate_window_position ( state ); @@ -1568,7 +1620,7 @@ int rofi_view_error_dialog ( const char *msg, int markup ) // Display it. xcb_map_window ( xcb->connection, CacheState.main_window ); - widget_queue_redraw ( WIDGET ( state->main_box ) ); + widget_queue_redraw ( WIDGET ( state->main_window ) ); if ( xcb->sncontext != NULL ) { sn_launchee_context_complete ( xcb->sncontext ); @@ -1675,9 +1727,17 @@ void rofi_view_set_overlay ( RofiViewState *state, const char *text ) } widget_enable ( WIDGET ( state->overlay ) ); textbox_text ( state->overlay, text ); - unsigned int x_offset = state->width - ( 2 * state->border ) - widget_get_width ( WIDGET ( state->case_indicator ) ); + int x_offset = widget_get_width ( WIDGET(state->main_window) ); + // Within padding of window. + x_offset -= widget_padding_get_right ( WIDGET (state->main_window) ); + // Within the border of widget. + x_offset -= widget_padding_get_right ( WIDGET (state->main_box ) ); + x_offset -= widget_padding_get_right ( WIDGET (state->input_bar ) ); + x_offset -= widget_get_width ( WIDGET ( state->case_indicator ) ); x_offset -= widget_get_width ( WIDGET ( state->overlay ) ); - widget_move ( WIDGET ( state->overlay ), x_offset, state->border ); + int top_offset = widget_padding_get_top ( WIDGET (state->main_window) ); + top_offset += widget_padding_get_top ( WIDGET (state->main_box ) ); + widget_move ( WIDGET ( state->overlay ), x_offset, top_offset ); // We want to queue a repaint. rofi_view_queue_redraw ( ); } @@ -1707,7 +1767,7 @@ void rofi_view_switch_mode ( RofiViewState *state, Mode *mode ) state->reload = TRUE; state->refilter = TRUE; rofi_view_refilter ( state ); - rofi_view_update ( state ); + rofi_view_update ( state, TRUE ); } xcb_window_t rofi_view_get_window ( void ) diff --git a/source/widgets/box.c b/source/widgets/box.c index 3ebfa775..ba1e4eab 100644 --- a/source/widgets/box.c +++ b/source/widgets/box.c @@ -29,26 +29,75 @@ #include "widgets/widget.h" #include "widgets/widget-internal.h" #include "widgets/box.h" +#include "theme.h" #define LOG_DOMAIN "Widgets.Box" +/** Default spacing used in the box*/ +#define DEFAULT_SPACING 2 + struct _box { widget widget; boxType type; int max_size; // Padding between elements - int padding; + Distance spacing; GList *children; }; static void box_update ( widget *wid ); +static int box_get_desired_height ( widget *wid ) +{ + box *b = (box *)wid; + int spacing = distance_get_pixel ( b->spacing, b->type == BOX_VERTICAL? ORIENTATION_VERTICAL:ORIENTATION_HORIZONTAL ); + int active_widgets = 0; + int height = 0; + if ( b->type == BOX_VERTICAL ){ + 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 ) { + height += widget_get_desired_height ( child ); + continue; + } + height += widget_get_desired_height ( child ); + } + if ( active_widgets > 0 ){ + height += (active_widgets - 1)*spacing; + } + } else { + for ( GList *iter = g_list_first ( b->children ); iter != NULL; iter = g_list_next ( iter ) ) { + widget * child = (widget *) iter->data; + if ( !child->enabled ) { + continue; + } + height = MAX ( widget_get_desired_height ( child ), height ); + } + } + height += widget_padding_get_padding_height ( wid ); + return height; +} + + static void vert_calculate_size ( box *b ) { + int spacing = distance_get_pixel ( b->spacing, ORIENTATION_VERTICAL ); int expanding_widgets = 0; int active_widgets = 0; + int rem_width = widget_padding_get_remaining_width ( WIDGET (b) ); + int rem_height = widget_padding_get_remaining_height ( WIDGET (b) ); + for ( GList *iter = g_list_first ( b->children ); iter != NULL; iter = g_list_next ( iter ) ) { + widget * child = (widget *) iter->data; + if ( child->enabled && child->expand == FALSE ){ + widget_resize ( child, rem_width, widget_get_desired_height (child) ); + } + } b->max_size = 0; for ( GList *iter = g_list_first ( b->children ); iter != NULL; iter = g_list_next ( iter ) ) { widget * child = (widget *) iter->data; @@ -60,17 +109,21 @@ static void vert_calculate_size ( box *b ) expanding_widgets++; continue; } - b->max_size += child->h; + if ( child->h > 0 ){ + b->max_size += child->h; + } } - b->max_size += MAX ( 0, ( ( active_widgets - 1 ) * b->padding ) ); - if ( b->max_size > b->widget.h ) { + if ( active_widgets > 0 ){ + b->max_size += ( active_widgets - 1 ) * spacing; + } + if ( b->max_size > rem_height ) { + b->max_size = rem_height; g_log ( LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Widgets to large (height) for box: %d %d", b->max_size, b->widget.h ); return; } if ( active_widgets > 0 ) { - int bottom = b->widget.h; - int top = 0; - double rem = b->widget.h - b->max_size; + int top = widget_padding_get_top ( WIDGET ( b ) ); + double rem = rem_height - 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; @@ -80,40 +133,35 @@ static void vert_calculate_size ( box *b ) 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; - } + widget_move ( child, widget_padding_get_left ( WIDGET ( b ) ), top ); + top += expanding_widgets_size; + widget_resize ( child, rem_width, expanding_widgets_size ); + top += spacing; 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 ); + widget_move ( child, widget_padding_get_left ( WIDGET ( b ) ), top ); top += widget_get_height ( child ); - widget_resize ( child, b->widget.w, child->h ); - top += b->padding; + top += spacing; } } } + b->max_size += widget_padding_get_padding_height ( WIDGET (b) ); } static void hori_calculate_size ( box *b ) { + int spacing = distance_get_pixel ( b->spacing, ORIENTATION_HORIZONTAL ); int expanding_widgets = 0; int active_widgets = 0; + int rem_width = widget_padding_get_remaining_width ( WIDGET (b) ); + int rem_height = widget_padding_get_remaining_height ( WIDGET (b) ); + for ( GList *iter = g_list_first ( b->children ); iter != NULL; iter = g_list_next ( iter ) ) { + widget * child = (widget *) iter->data; + if ( child->enabled && child->expand == FALSE ){ + widget_resize ( child, child->w, rem_height ); + } + } b->max_size = 0; for ( GList *iter = g_list_first ( b->children ); iter != NULL; iter = g_list_next ( iter ) ) { widget * child = (widget *) iter->data; @@ -126,17 +174,19 @@ static void hori_calculate_size ( box *b ) continue; } // Size used by fixed width widgets. + if ( child->h > 0 ){ b->max_size += child->w; + } } - b->max_size += MAX ( 0, ( ( active_widgets - 1 ) * b->padding ) ); - if ( b->max_size > b->widget.w ) { + b->max_size += MAX ( 0, ( ( active_widgets - 1 ) * spacing ) ); + if ( b->max_size > (rem_width)) { + b->max_size = rem_width; 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 left = widget_padding_get_left ( WIDGET (b) ); + double rem = rem_width - 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; @@ -146,52 +196,30 @@ static void hori_calculate_size ( box *b ) 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; - } + widget_move ( child, left, widget_padding_get_top ( WIDGET ( b ) ) ); + left += expanding_widgets_size; + widget_resize ( child, expanding_widgets_size, rem_height ); + left += spacing; 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 ); + widget_move ( child, left, widget_padding_get_top ( WIDGET ( b ) ) ); left += widget_get_width ( child ); - widget_resize ( child, child->w, b->widget.h ); - left += b->padding; + left += spacing; } } } + b->max_size += widget_padding_get_padding_width ( WIDGET ( b ) ); } 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 ) @@ -206,15 +234,34 @@ static void box_free ( widget *wid ) g_free ( b ); } -void box_add ( box *box, widget *child, gboolean expand, gboolean end ) +static int box_sort_children ( gconstpointer a, gconstpointer b ) +{ + widget *child_a = (widget*)a; + widget *child_b = (widget*)b; + + return child_a->index - child_b->index; +} + +void box_add ( box *box, widget *child, gboolean expand, int index ) { if ( box == NULL ) { return; } - child->expand = expand; - child->end = end; + // Make sure box is width/heigh enough. + if ( box->type == BOX_VERTICAL){ + int width=box->widget.w; + width = MAX ( width, child->w+widget_padding_get_padding_width ( WIDGET ( box ) )); + box->widget.w = width; + } else { + int height = box->widget.h; + height = MAX (height, child->h+widget_padding_get_padding_height ( WIDGET ( box ))); + box->widget.h = height; + } + child->expand = rofi_theme_get_boolean ( child, "expand", expand); + 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 ); widget_update ( WIDGET ( box ) ); } @@ -263,22 +310,22 @@ static gboolean box_motion_notify ( widget *wid, xcb_motion_notify_event_t *xme return FALSE; } -box * box_create ( boxType type, short x, short y, short w, short h ) +box * box_create ( const char *name, boxType type ) { 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.motion_notify = box_motion_notify; - b->widget.enabled = TRUE; + // Initialize widget. + widget_init ( WIDGET(b), name ); + b->type = type; + 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.motion_notify = box_motion_notify; + b->widget.get_desired_height = box_get_desired_height; + b->widget.enabled = TRUE; + b->spacing = rofi_theme_get_distance ( WIDGET(b), "spacing", DEFAULT_SPACING ); return b; } @@ -302,11 +349,3 @@ int box_get_fixed_pixels ( box *box ) } return 0; } - -void box_set_padding ( box * box, unsigned int padding ) -{ - if ( box != NULL ) { - box->padding = padding; - widget_queue_redraw ( WIDGET ( box ) ); - } -} diff --git a/source/widgets/container.c b/source/widgets/container.c new file mode 100644 index 00000000..e1fabb46 --- /dev/null +++ b/source/widgets/container.c @@ -0,0 +1,148 @@ +/** + * rofi + * + * MIT/X11 License + * Modified 2016-2017 Qball Cow + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include "widgets/widget.h" +#include "widgets/widget-internal.h" +#include "widgets/container.h" +#include "theme.h" + +#define LOG_DOMAIN "Widgets.Window" + +/** The default border width of the container */ +#define DEFAULT_BORDER_WIDTH 2 + +struct _window +{ + widget widget; + widget *child; +}; + +static void container_update ( widget *wid ); + + +static int container_get_desired_height ( widget *widget ) +{ + container *b = (container *) widget; + int height = 0; + if ( b->child ) { + height += widget_get_desired_height ( b->child ); + } + height += widget_padding_get_padding_height ( widget ); + return height; +} + + +static void container_draw ( widget *wid, cairo_t *draw ) +{ + container *b = (container *) wid; + + widget_draw ( b->child, draw ); +} + +static void container_free ( widget *wid ) +{ + container *b = (container *) wid; + + widget_free ( b->child ); + g_free ( b ); +} + +void container_add ( container *container, widget *child ) +{ + if ( container == NULL ) { + return; + } + container->child = child; + child->parent = WIDGET ( container ); + widget_update ( WIDGET ( container ) ); +} + +static void container_resize ( widget *widget, short w, short h ) +{ + container *b = (container *) widget; + if ( b->widget.w != w || b->widget.h != h ) { + b->widget.w = w; + b->widget.h = h; + widget_update ( widget ); + } +} + +static gboolean container_clicked ( widget *wid, xcb_button_press_event_t *xbe, G_GNUC_UNUSED void *udata ) +{ + container *b = (container *) wid; + if ( widget_intersect ( b->child, xbe->event_x, xbe->event_y ) ) { + xcb_button_press_event_t rel = *xbe; + rel.event_x -= b->child->x; + rel.event_y -= b->child->y; + return widget_clicked ( b->child, &rel ); + } + return FALSE; +} +static gboolean container_motion_notify ( widget *wid, xcb_motion_notify_event_t *xme ) +{ + container *b = (container *) wid; + if ( widget_intersect ( b->child, xme->event_x, xme->event_y ) ) { + xcb_motion_notify_event_t rel = *xme; + rel.event_x -= b->child->x; + rel.event_y -= b->child->y; + return widget_motion_notify ( b->child, &rel ); + } + return FALSE; +} + +container * container_create ( const char *name ) +{ + container *b = g_malloc0 ( sizeof ( container ) ); + // Initialize widget. + widget_init ( WIDGET(b), name ); + b->widget.draw = container_draw; + b->widget.free = container_free; + b->widget.resize = container_resize; + b->widget.update = container_update; + b->widget.clicked = container_clicked; + b->widget.motion_notify = container_motion_notify; + b->widget.get_desired_height = container_get_desired_height; + b->widget.enabled = TRUE; + return b; +} + +static void container_update ( widget *wid ) +{ + container *b = (container *) wid; + if ( b->child && b->child->enabled ){ + widget_resize ( WIDGET ( b->child ), + widget_padding_get_remaining_width (WIDGET(b)), + widget_padding_get_remaining_height (WIDGET(b)) + ); + widget_move ( WIDGET ( b->child ), + widget_padding_get_left (WIDGET(b)), + widget_padding_get_top (WIDGET(b)) + ); + } +} + diff --git a/source/widgets/listview.c b/source/widgets/listview.c index 90d18382..d3b6aebb 100644 --- a/source/widgets/listview.c +++ b/source/widgets/listview.c @@ -29,6 +29,11 @@ #include #include +#include "settings.h" +#include "theme.h" + +#define DEFAULT_SPACING 2 + struct _listview { widget widget; @@ -51,10 +56,14 @@ struct _listview unsigned int req_elements; unsigned int cur_elements; - unsigned int padding; + Distance spacing; unsigned int menu_lines; + unsigned int max_displayed_lines; unsigned int menu_columns; unsigned int fixed_num_lines; + unsigned int dynamic; + unsigned int eh; + unsigned int reverse; gboolean cycle; gboolean multi_select; @@ -71,8 +80,13 @@ struct _listview xcb_timestamp_t last_click; listview_mouse_activated_cb mouse_activated; void *mouse_activated_data; + + + char *listview_name; }; +static int listview_get_desired_height ( widget *wid ); + static void listview_free ( widget *wid ) { listview *lv = (listview *) wid; @@ -81,6 +95,7 @@ static void listview_free ( widget *wid ) } g_free ( lv->boxes ); + g_free( lv->listview_name ); widget_free ( WIDGET ( lv->scrollbar ) ); g_free ( lv ); } @@ -152,24 +167,40 @@ static void listview_draw ( widget *wid, cairo_t *draw ) // Set these all together to make sure they update consistently. scrollbar_set_max_value ( lv->scrollbar, lv->req_elements ); scrollbar_set_handle_length ( lv->scrollbar, lv->cur_columns * lv->max_rows ); - scrollbar_set_handle ( lv->scrollbar, lv->selected ); + if ( lv->reverse ) { + scrollbar_set_handle ( lv->scrollbar, lv->req_elements - lv->selected -1 ); + } else { + scrollbar_set_handle ( lv->scrollbar, lv->selected ); + } lv->last_offset = offset; + int spacing_vert = distance_get_pixel ( lv->spacing, ORIENTATION_VERTICAL ); + int spacing_hori = distance_get_pixel ( lv->spacing, ORIENTATION_HORIZONTAL ); + + int left_offset = widget_padding_get_left ( wid ); + int top_offset = widget_padding_get_top ( wid ); + if ( lv->scrollbar->widget.index == 0 ) { + left_offset += spacing_hori + lv->scrollbar->widget.w; + } 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 ); + unsigned int width = lv->widget.w - spacing_hori * ( lv->cur_columns - 1 ); + width -= widget_padding_get_padding_width ( wid ); if ( widget_enabled ( WIDGET ( lv->scrollbar ) ) ) { - width -= lv->padding; + width -= spacing_hori; width -= widget_get_width ( WIDGET ( lv->scrollbar ) ); } 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 ); + unsigned int ex = left_offset + ( ( i ) / lv->max_rows ) * ( element_width + spacing_hori ); + if ( lv->reverse ) { + unsigned int ey = wid->h-(widget_padding_get_bottom ( wid ) + ( ( i ) % lv->max_rows ) * ( lv->element_height + spacing_vert ))-lv->element_height; + textbox_moveresize ( lv->boxes[i], ex, ey, element_width, lv->element_height ); + } else { + unsigned int ey = top_offset + ( ( i ) % lv->max_rows ) * ( lv->element_height + spacing_vert ); + 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 ); @@ -182,9 +213,8 @@ static void listview_draw ( widget *wid, cairo_t *draw ) widget_draw ( WIDGET ( lv->boxes[i] ), draw ); } } - widget_draw ( WIDGET ( lv->scrollbar ), draw ); - cairo_restore ( draw ); } + widget_draw ( WIDGET ( lv->scrollbar ), draw ); } static void listview_recompute_elements ( listview *lv ) @@ -208,7 +238,9 @@ static void listview_recompute_elements ( listview *lv ) 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, "" ); + char *name = g_strjoin (".", lv->listview_name,"element", NULL); + lv->boxes[i] = textbox_create ( name, flags, NORMAL, "" ); + g_free ( name ); } } lv->rchanged = TRUE; @@ -244,11 +276,21 @@ 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 ); + int height = lv->widget.h - widget_padding_get_padding_height ( WIDGET (lv) ); + int spacing_vert = distance_get_pixel ( lv->spacing, ORIENTATION_VERTICAL ); + lv->max_rows = ( spacing_vert + height ) / ( lv->element_height + spacing_vert ); lv->max_elements = lv->max_rows * lv->menu_columns; - widget_move ( WIDGET ( lv->scrollbar ), lv->widget.w - widget_get_width ( WIDGET ( lv->scrollbar ) ), 0 ); - widget_resize ( WIDGET ( lv->scrollbar ), widget_get_width ( WIDGET ( lv->scrollbar ) ), h ); + if ( lv->scrollbar->widget.index == 0 ){ + widget_move ( WIDGET ( lv->scrollbar ), + widget_padding_get_left ( WIDGET ( lv ) ), + widget_padding_get_top ( WIDGET ( lv ) ) ); + } else { + widget_move ( WIDGET ( lv->scrollbar ), + lv->widget.w - widget_padding_get_right ( WIDGET ( lv ) ) - widget_get_width ( WIDGET ( lv->scrollbar ) ), + widget_padding_get_top ( WIDGET (lv ) )); + } + widget_resize ( WIDGET ( lv->scrollbar ), widget_get_width ( WIDGET ( lv->scrollbar ) ), height ); listview_recompute_elements ( lv ); widget_queue_redraw ( wid ); @@ -310,24 +352,49 @@ static gboolean listview_motion_notify ( widget *wid, xcb_motion_notify_event_t return FALSE; } -listview *listview_create ( listview_update_callback cb, void *udata, unsigned int eh ) +listview *listview_create ( const char *name, listview_update_callback cb, void *udata, unsigned int eh, gboolean reverse ) { 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.motion_notify = listview_motion_notify; - lv->widget.enabled = TRUE; + gchar *box = g_strjoin (".", name, "box", NULL ); + widget_init ( WIDGET ( lv ), box ); + g_free(box); + lv->listview_name = g_strdup ( name ); + lv->widget.free = listview_free; + lv->widget.resize = listview_resize; + lv->widget.draw = listview_draw; + lv->widget.clicked = listview_clicked; + lv->widget.motion_notify = listview_motion_notify; + lv->widget.get_desired_height = listview_get_desired_height; + lv->widget.enabled = TRUE; + lv->eh = eh; - lv->scrollbar = scrollbar_create ( 0, 0, 4, 0 ); + char *n = g_strjoin(".", lv->listview_name,"scrollbar", NULL); + lv->scrollbar = scrollbar_create ( n ); + // Default position on right. + lv->scrollbar->widget.index = rofi_theme_get_integer_exact ( WIDGET (lv->scrollbar), "index", 1); + g_free(n); 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; + // + char *tb_name = g_strjoin (".", lv->listview_name,"element", NULL); + textbox *tb = textbox_create ( tb_name, 0, NORMAL, "" ); + lv->element_height = textbox_get_estimated_height (tb, lv->eh); + g_free(tb_name); lv->callback = cb; lv->udata = udata; + + // Some settings. + lv->spacing = rofi_theme_get_distance ( WIDGET ( lv ), "spacing", DEFAULT_SPACING ); + lv->menu_columns = rofi_theme_get_integer ( WIDGET ( lv ), "columns", config.menu_columns ); + lv->fixed_num_lines = rofi_theme_get_boolean ( WIDGET ( lv ), "fixed-height", config.fixed_num_lines ); + lv->dynamic = rofi_theme_get_boolean ( WIDGET ( lv ), "dynamic", TRUE ); + lv->reverse = rofi_theme_get_boolean ( WIDGET ( lv ), "reverse", reverse ); + listview_set_show_scrollbar ( lv, rofi_theme_get_boolean ( WIDGET ( lv ), "scrollbar", !config.hide_scrollbar )); + lv->cycle = rofi_theme_get_boolean ( WIDGET ( lv ), "cycle", config.cycle ); + + return lv; } @@ -335,7 +402,7 @@ listview *listview_create ( listview_update_callback cb, void *udata, unsigned i * Navigation commands. */ -void listview_nav_up ( listview *lv ) +static void listview_nav_up_int ( listview *lv ) { if ( lv == NULL ) { return; @@ -349,7 +416,7 @@ void listview_nav_up ( listview *lv ) lv->selected--; widget_queue_redraw ( WIDGET ( lv ) ); } -void listview_nav_down ( listview *lv ) +static void listview_nav_down_int ( listview *lv ) { if ( lv == NULL ) { return; @@ -362,6 +429,23 @@ void listview_nav_down ( listview *lv ) widget_queue_redraw ( WIDGET ( lv ) ); } +void listview_nav_up ( listview *lv ) +{ + if ( lv->reverse ) { + listview_nav_down_int ( lv ); + } else { + listview_nav_up_int ( lv ); + } +} +void listview_nav_down ( listview *lv ) +{ + if ( lv->reverse ) { + listview_nav_up_int ( lv ); + } else { + listview_nav_down_int ( lv ); + } +} + void listview_nav_left ( listview *lv ) { if ( lv == NULL ) { @@ -396,7 +480,7 @@ void listview_nav_right ( listview *lv ) } } -void listview_nav_page_prev ( listview *lv ) +static void listview_nav_page_prev_int ( listview *lv ) { if ( lv == NULL ) { return; @@ -409,7 +493,7 @@ void listview_nav_page_prev ( listview *lv ) } widget_queue_redraw ( WIDGET ( lv ) ); } -void listview_nav_page_next ( listview *lv ) +static void listview_nav_page_next_int ( listview *lv ) { if ( lv == NULL ) { return; @@ -424,50 +508,51 @@ void listview_nav_page_next ( listview *lv ) widget_queue_redraw ( WIDGET ( lv ) ); } -unsigned int listview_get_desired_height ( listview *lv ) +void listview_nav_page_prev ( listview *lv ) { - if ( lv == NULL ) { + if ( lv->reverse ){ + listview_nav_page_next_int ( lv ); + } else { + listview_nav_page_prev_int ( lv ); + } +} +void listview_nav_page_next ( listview *lv ) +{ + if ( lv->reverse ){ + listview_nav_page_prev_int ( lv ); + } else { + listview_nav_page_next_int ( lv ); + } +} + +static int listview_get_desired_height ( widget *wid ) +{ + listview *lv = (listview *)wid; + int spacing = distance_get_pixel ( lv->spacing, ORIENTATION_VERTICAL ); + if ( lv == NULL || lv->widget.enabled == FALSE ) { return 0; } int h = lv->menu_lines; if ( !( lv->fixed_num_lines ) ) { - h = MIN ( lv->menu_lines, lv->req_elements ); + if ( lv->dynamic ) { + h = MIN ( lv->menu_lines, lv->req_elements ); + } else { + h = MIN ( lv->menu_lines, lv->max_displayed_lines ); + } } if ( h == 0 ) { - return 0; + if ( lv->dynamic && !lv->fixed_num_lines ){ + // Hide widget fully. + return 0; + } + return widget_padding_get_padding_height ( WIDGET (lv) ); } - return h * lv->element_height + ( h - 1 ) * lv->padding; + int height = widget_padding_get_padding_height ( WIDGET (lv) ); + height += h*(lv->element_height+spacing) - spacing; + return height; } -/** - * 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 ) +void listview_set_show_scrollbar ( listview *lv, gboolean enabled ) { if ( lv ) { if ( enabled ) { @@ -479,19 +564,7 @@ void listview_set_hide_scrollbar ( listview *lv, gboolean enabled ) 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 ) { @@ -512,3 +585,37 @@ void listview_set_multi_select ( listview *lv, gboolean enable ) lv->multi_select = enable; } } +void listview_set_num_lines ( listview *lv, unsigned int num_lines ) +{ + if ( lv ) { + lv->menu_lines = num_lines; + } +} + +unsigned int listview_get_num_lines ( listview *lv ) +{ + if ( lv ) { + return lv->menu_lines; + } + return 0; +} +void listview_set_max_lines ( listview *lv, unsigned int max_lines ) +{ + if ( lv ) { + lv->max_displayed_lines = max_lines; + } +} + +gboolean listview_get_fixed_num_lines ( listview *lv ) +{ + if ( lv ) { + return lv->fixed_num_lines; + } + return FALSE; +} +void listview_set_fixed_num_lines ( listview *lv ) +{ + if ( lv ) { + lv->fixed_num_lines = TRUE; + } +} diff --git a/source/widgets/scrollbar.c b/source/widgets/scrollbar.c index 79c8d28c..c395b7ea 100644 --- a/source/widgets/scrollbar.c +++ b/source/widgets/scrollbar.c @@ -27,24 +27,37 @@ #include #include "widgets/scrollbar.h" #include "x11-helper.h" -#include "settings.h" + +#include "theme.h" + +#define DEFAULT_SCROLLBAR_WIDTH 8 static void scrollbar_draw ( widget *, cairo_t * ); static void scrollbar_free ( widget * ); static gboolean scrollbar_motion_notify ( widget *wid, xcb_motion_notify_event_t *xme ); -scrollbar *scrollbar_create ( short x, short y, short w, short h ) + +static int scrollbar_get_desired_height ( widget *wid ) +{ + // Want height we are. + return wid->h; +} + +scrollbar *scrollbar_create ( const char *name ) { scrollbar *sb = g_malloc0 ( sizeof ( scrollbar ) ); - - sb->widget.x = x; - sb->widget.y = y; - sb->widget.w = MAX ( 1, w ); - sb->widget.h = MAX ( 1, h ); + widget_init ( WIDGET (sb), name ); + sb->widget.x = 0; + sb->widget.y = 0; + sb->width = rofi_theme_get_distance ( WIDGET (sb), "handle-width", DEFAULT_SCROLLBAR_WIDTH ); + int width = distance_get_pixel (sb->width, ORIENTATION_HORIZONTAL); + sb->widget.w = widget_padding_get_padding_width ( WIDGET (sb)) + width; + sb->widget.h = widget_padding_get_padding_height ( WIDGET ( sb ) ); sb->widget.draw = scrollbar_draw; sb->widget.free = scrollbar_free; sb->widget.motion_notify = scrollbar_motion_notify; + sb->widget.get_desired_height = scrollbar_get_desired_height; sb->length = 10; sb->pos = 0; @@ -97,20 +110,26 @@ void scrollbar_set_handle_length ( scrollbar *sb, unsigned int pos_length ) static void scrollbar_draw ( widget *wid, cairo_t *draw ) { scrollbar *sb = (scrollbar *) wid; + unsigned int wh = widget_padding_get_remaining_height ( wid ); // Calculate position and size. - unsigned int r = ( sb->length * wid->h ) / ( (double) ( sb->length + sb->pos_length ) ); + unsigned int r = ( sb->length * wh ) / ( (double) ( sb->length + sb->pos_length ) ); unsigned int handle = wid->h - r; double sec = ( ( r ) / (double) ( sb->length - 1 ) ); unsigned int height = handle; unsigned int y = sb->pos * sec; // Set max pos. - y = MIN ( y, wid->h - handle ); + y = MIN ( y, wh - handle ); // Never go out of bar. height = MAX ( 2, height ); // Cap length; - color_separator ( draw ); + rofi_theme_get_color ( WIDGET (sb ), "foreground", draw ); + rofi_theme_get_color ( WIDGET (sb ), "handle-color", draw ); - cairo_rectangle ( draw, sb->widget.x, sb->widget.y + y, sb->widget.w, height ); + cairo_rectangle ( draw, + widget_padding_get_left ( wid ), + widget_padding_get_top ( wid ) + y, + widget_padding_get_remaining_width ( wid ), + height ); cairo_fill ( draw ); } static gboolean scrollbar_motion_notify ( widget *wid, xcb_motion_notify_event_t *xme ) @@ -140,3 +159,4 @@ unsigned int scrollbar_clicked ( const scrollbar *sb, int y ) } return 0; } + diff --git a/source/widgets/separator.c b/source/widgets/separator.c deleted file mode 100644 index d27b26fa..00000000 --- a/source/widgets/separator.c +++ /dev/null @@ -1,129 +0,0 @@ -/** - * MIT/X11 License - * Modified (c) 2016-2017 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/widget.h" -#include "widgets/widget-internal.h" -#include "widgets/separator.h" -#include "x11-helper.h" -#include "settings.h" - -/** - * Internal structure for the separator. - */ -struct _separator -{ - widget widget; - separator_type type; - separator_line_style line_style; -}; - -/** Configuration value for separator style indicating no line */ -const char *const _separator_style_none = "none"; -/** Configuration value for separator style indicating dashed line. */ -const char *const _separator_style_dash = "dash"; -static void separator_draw ( widget *, cairo_t * ); -static void separator_free ( widget * ); - -separator *separator_create ( separator_type type, short sw ) -{ - separator *sb = g_malloc0 ( sizeof ( separator ) ); - sb->type = type; - - sb->widget.x = 0; - sb->widget.y = 0; - if ( sb->type == S_HORIZONTAL ) { - sb->widget.w = 1; - sb->widget.h = MAX ( 1, sw ); - } - else { - sb->widget.h = 1; - sb->widget.w = MAX ( 1, sw ); - } - - 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 ); -} - -void separator_set_line_style ( separator *sp, separator_line_style style ) -{ - if ( sp ) { - sp->line_style = style; - widget_need_redraw ( WIDGET ( sp ) ); - } -} -void separator_set_line_style_from_string ( separator *sp, const char *style_str ) -{ - if ( !sp ) { - return; - } - separator_line_style style = S_LINE_SOLID; - if ( g_strcmp0 ( style_str, _separator_style_none ) == 0 ) { - style = S_LINE_NONE; - } - else if ( g_strcmp0 ( style_str, _separator_style_dash ) == 0 ) { - style = S_LINE_DASH; - } - separator_set_line_style ( sp, style ); -} - -static void separator_draw ( widget *wid, cairo_t *draw ) -{ - separator *sep = (separator *) wid; - if ( sep->line_style == S_LINE_NONE ) { - // Nothing to draw. - return; - } - color_separator ( draw ); - if ( sep->line_style == S_LINE_DASH ) { - const double dashes[1] = { 4 }; - cairo_set_dash ( draw, dashes, 1, 0.0 ); - } - if ( sep->type == S_HORIZONTAL ) { - cairo_set_line_width ( draw, wid->h ); - 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 ); - } - else { - cairo_set_line_width ( draw, wid->w ); - double half = wid->w / 2.0; - cairo_move_to ( draw, wid->x + half, wid->y ); - cairo_line_to ( draw, wid->x + half, wid->y + wid->h ); - } - cairo_stroke ( draw ); -} diff --git a/source/widgets/textbox.c b/source/widgets/textbox.c index 02717a23..877266b5 100644 --- a/source/widgets/textbox.c +++ b/source/widgets/textbox.c @@ -29,19 +29,21 @@ #include #include #include -#include "settings.h" #include "widgets/textbox.h" #include "keyb.h" #include "x11-helper.h" #include "mode.h" #include "view.h" +#include "theme.h" + #define DOT_OFFSET 15 static void textbox_draw ( widget *, cairo_t * ); static void textbox_free ( widget * ); static int textbox_get_width ( widget * ); static int _textbox_get_height ( widget * ); +static void __textbox_update_pango_text ( textbox *tb ); /** * @param tb Handle to the textbox @@ -50,26 +52,6 @@ static int _textbox_get_height ( widget * ); */ static void textbox_cursor_end ( textbox *tb ); -/** - * Font + font color cache. - * Avoid re-loading font on every change on every textbox. - */ -typedef struct -{ - Color fg; - Color bg; - Color bgalt; - Color hlfg; - Color hlbg; -} RowColor; - -/** Number of states */ -#define num_states 3 -/** - * Different colors for the different states - */ -RowColor colors[num_states]; - /** Default pango context */ static PangoContext *p_context = NULL; /** The pango font metrics */ @@ -95,24 +77,34 @@ 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 ); } +static int textbox_get_desired_height ( widget *wid ) +{ + textbox *tb = (textbox *)wid; + if ( (tb->flags & TB_AUTOHEIGHT) == 0 ) + { + return tb->widget.h; + } + if ( tb->changed ) { + __textbox_update_pango_text ( tb ); + } + int height = textbox_get_height (tb); + return height; +} -textbox* textbox_create ( TextboxFlags flags, short x, short y, short w, short h, - TextBoxFontType tbft, const char *text ) +textbox* textbox_create ( const char *name, TextboxFlags flags, TextBoxFontType tbft, const char *text ) { textbox *tb = g_slice_new0 ( textbox ); + widget_init ( WIDGET (tb), name ); + 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->widget.get_desired_height = textbox_get_desired_height; tb->flags = flags; - tb->widget.x = x; - tb->widget.y = y; - tb->widget.w = MAX ( 1, w ); - tb->widget.h = MAX ( 1, h ); - tb->changed = FALSE; tb->main_surface = cairo_image_surface_create ( CAIRO_FORMAT_ARGB32, tb->widget.w, tb->widget.h ); @@ -140,6 +132,18 @@ textbox* textbox_create ( TextboxFlags flags, short x, short y, short w, short h return tb; } +/** + * State names used for theming. + */ +const char const *const theme_prop_names[][3] = { + /** Normal row */ + {"normal.normal", "selected.normal", "alternate.normal"}, + /** Urgent row */ + {"normal.urgent", "selected.urgent", "alternate.urgent"}, + /** Active row */ + {"normal.active", "selected.active", "alternate.active"}, +}; + void textbox_font ( textbox *tb, TextBoxFontType tbft ) { TextBoxFontType t = tbft & STATE_MASK; @@ -150,23 +154,19 @@ void textbox_font ( textbox *tb, TextBoxFontType tbft ) if ( t == ( URGENT | ACTIVE ) ) { t = ACTIVE; } - RowColor *color = &( colors[t] ); switch ( ( tbft & FMOD_MASK ) ) { case HIGHLIGHT: - tb->color_bg = color->hlbg; - tb->color_fg = color->hlfg; + widget_set_state ( WIDGET (tb), theme_prop_names[t][1]); break; case ALT: - tb->color_bg = color->bgalt; - tb->color_fg = color->fg; + widget_set_state ( WIDGET (tb), theme_prop_names[t][2]); break; default: - tb->color_bg = color->bg; - tb->color_fg = color->fg; + widget_set_state ( WIDGET (tb), theme_prop_names[t][0]); break; } - if ( tb->tbft != tbft ) { + if ( tb->tbft != tbft || tb->widget.state == NULL ) { tb->update = TRUE; widget_queue_redraw ( WIDGET ( tb ) ); } @@ -232,7 +232,9 @@ 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 ) ); + if ( WIDGET(tb)->parent ){ + widget_update ( WIDGET ( tb )->parent ); + } } tb->cursor = MAX ( 0, MIN ( ( int ) g_utf8_strlen ( tb->text, -1 ), tb->cursor ) ); @@ -246,7 +248,7 @@ void textbox_moveresize ( textbox *tb, int x, int y, int w, int h ) if ( tb->flags & TB_AUTOWIDTH ) { pango_layout_set_width ( tb->layout, -1 ); unsigned int offset = ( tb->flags & TB_INDICATOR ) ? DOT_OFFSET : 0; - w = textbox_get_font_width ( tb ) + 2 * config.line_padding + offset; + w = textbox_get_font_width ( tb ) + widget_padding_get_padding_width ( WIDGET (tb) ) + offset; } else { // set ellipsize @@ -261,8 +263,9 @@ void textbox_moveresize ( textbox *tb, int x, int y, int w, int h ) if ( tb->flags & TB_AUTOHEIGHT ) { // Width determines height! int tw = MAX ( 1, w ); - pango_layout_set_width ( tb->layout, PANGO_SCALE * ( tw - 2 * config.line_padding - offset ) ); - h = textbox_get_height ( tb ); + pango_layout_set_width ( tb->layout, PANGO_SCALE * ( tw - widget_padding_get_padding_width ( WIDGET (tb) ) - offset ) ); + int hd = textbox_get_height ( tb ); + h = MAX (hd, h); } if ( x != tb->widget.x || y != tb->widget.y || w != tb->widget.w || h != tb->widget.h ) { @@ -273,7 +276,7 @@ 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 ) ); + pango_layout_set_width ( tb->layout, PANGO_SCALE * ( tb->widget.w - widget_padding_get_padding_width ( WIDGET (tb) ) - offset ) ); tb->update = TRUE; widget_queue_redraw ( WIDGET ( tb ) ); } @@ -286,7 +289,6 @@ static void textbox_free ( widget *wid ) g_source_remove ( tb->blink_timeout ); tb->blink_timeout = 0; } - g_free ( tb->text ); if ( tb->layout != NULL ) { @@ -316,13 +318,15 @@ static void texbox_update ( textbox *tb ) } tb->main_surface = cairo_image_surface_create ( CAIRO_FORMAT_ARGB32, tb->widget.w, tb->widget.h ); tb->main_draw = cairo_create ( tb->main_surface ); - cairo_set_operator ( tb->main_draw, CAIRO_OPERATOR_SOURCE ); + cairo_set_operator ( tb->main_draw, CAIRO_OPERATOR_OVER ); pango_cairo_update_layout ( tb->main_draw, tb->layout ); int font_height = textbox_get_font_height ( tb ); int cursor_x = 0; - int cursor_width = MAX ( 2, font_height / 10 ); + int cursor_y = 0; + int cursor_width = 2;//MAX ( 2, font_height / 10 ); + int cursor_height = font_height; if ( tb->changed ) { __textbox_update_pango_text ( tb ); @@ -338,50 +342,52 @@ static void texbox_update ( textbox *tb ) char *offset = g_utf8_offset_to_pointer ( text, cursor_offset ); pango_layout_get_cursor_pos ( tb->layout, offset - text, &pos, NULL ); cursor_x = pos.x / PANGO_SCALE; + cursor_y = pos.y / PANGO_SCALE; + cursor_height = pos.height/PANGO_SCALE; } // Skip the side MARGIN on the X axis. - int x = config.line_padding + offset; + int x = widget_padding_get_left ( WIDGET (tb) ) + offset; int y = 0; if ( tb->flags & TB_RIGHT ) { int line_width = 0; // Get actual width. pango_layout_get_pixel_size ( tb->layout, &line_width, NULL ); - x = ( tb->widget.w - line_width - config.line_padding - offset ); + x = ( tb->widget.w - line_width - widget_padding_get_right ( WIDGET (tb) ) - offset ); } else if ( tb->flags & TB_CENTER ) { int tw = textbox_get_font_width ( tb ); - x = ( ( tb->widget.w - tw - 2 * config.line_padding - offset ) ) / 2; + x = ( ( tb->widget.w - tw - widget_padding_get_padding_width( WIDGET (tb) ) - offset ) ) / 2; } - y = config.line_padding + ( pango_font_metrics_get_ascent ( p_metrics ) - pango_layout_get_baseline ( tb->layout ) ) / PANGO_SCALE; + y = widget_padding_get_top ( WIDGET (tb) ) + ( pango_font_metrics_get_ascent ( p_metrics ) - pango_layout_get_baseline ( tb->layout ) ) / PANGO_SCALE; - // Set ARGB - Color col = tb->color_bg; - cairo_set_source_rgba ( tb->main_draw, col.red, col.green, col.blue, col.alpha ); - cairo_paint ( tb->main_draw ); + // Set background transparency + //cairo_set_source_rgba ( tb->main_draw, 0,0,0,0.0); + //cairo_paint ( tb->main_draw ); - col = tb->color_fg; - cairo_set_source_rgba ( tb->main_draw, col.red, col.green, col.blue, col.alpha ); + rofi_theme_get_color ( WIDGET ( tb ), "foreground", tb->main_draw); // draw the cursor if ( tb->flags & TB_EDITABLE && tb->blink ) { - cairo_rectangle ( tb->main_draw, x + cursor_x, y, cursor_width, font_height ); + cairo_rectangle ( tb->main_draw, x + cursor_x, y+cursor_y, cursor_width, cursor_height); cairo_fill ( tb->main_draw ); } // Set ARGB // We need to set over, otherwise subpixel hinting wont work. - cairo_set_operator ( tb->main_draw, CAIRO_OPERATOR_OVER ); + //cairo_set_operator ( tb->main_draw, CAIRO_OPERATOR_OVER ); cairo_move_to ( tb->main_draw, x, y ); pango_cairo_show_layout ( tb->main_draw, tb->layout ); - if ( ( tb->flags & TB_INDICATOR ) == TB_INDICATOR && ( tb->tbft & ( SELECTED | HIGHLIGHT ) ) ) { + if ( ( tb->flags & TB_INDICATOR ) == TB_INDICATOR && ( tb->tbft & ( SELECTED ) ) ) { + /* if ( ( tb->tbft & SELECTED ) == SELECTED ) { cairo_set_source_rgba ( tb->main_draw, col.red, col.green, col.blue, col.alpha ); } else if ( ( tb->tbft & HIGHLIGHT ) == HIGHLIGHT ) { cairo_set_source_rgba ( tb->main_draw, col.red, col.green, col.blue, col.alpha * 0.2 ); } + */ cairo_arc ( tb->main_draw, DOT_OFFSET / 2.0, tb->widget.h / 2.0, 2.0, 0, 2.0 * M_PI ); cairo_fill ( tb->main_draw ); @@ -396,9 +402,8 @@ static void textbox_draw ( widget *wid, cairo_t *draw ) texbox_update ( tb ); /* Write buffer */ - - cairo_set_source_surface ( draw, tb->main_surface, tb->widget.x, tb->widget.y ); - cairo_rectangle ( draw, tb->widget.x, tb->widget.y, tb->widget.w, tb->widget.h ); + cairo_set_source_surface ( draw, tb->main_surface, 0,0 ); + cairo_rectangle ( draw, 0,0, tb->widget.w, tb->widget.h ); cairo_fill ( draw ); } @@ -709,47 +714,8 @@ gboolean textbox_append_char ( textbox *tb, const char *pad, const int pad_len ) return FALSE; } -/*** - * Font setup. - */ -static void textbox_parse_string ( const char *str, RowColor *color ) -{ - if ( str == NULL ) { - return; - } - char *cstr = g_strdup ( str ); - char *endp = NULL; - char *token; - int index = 0; - const char *const sep = ","; - for ( token = strtok_r ( cstr, sep, &endp ); token != NULL; token = strtok_r ( NULL, sep, &endp ) ) { - switch ( index ) - { - case 0: - color->bg = color_get ( g_strstrip ( token ) ); - break; - case 1: - color->fg = color_get ( g_strstrip ( token ) ); - break; - case 2: - color->bgalt = color_get ( g_strstrip ( token ) ); - break; - case 3: - color->hlbg = color_get ( g_strstrip ( token ) ); - break; - case 4: - color->hlfg = color_get ( g_strstrip ( token ) ); - break; - } - index++; - } - g_free ( cstr ); -} void textbox_setup ( void ) { - textbox_parse_string ( config.color_normal, &( colors[NORMAL] ) ); - textbox_parse_string ( config.color_urgent, &( colors[URGENT] ) ); - textbox_parse_string ( config.color_active, &( colors[ACTIVE] ) ); } void textbox_set_pango_context ( PangoContext *p ) @@ -774,12 +740,9 @@ void textbox_cleanup ( void ) int textbox_get_width ( widget *wid ) { 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; + if ( tb->flags & TB_AUTOWIDTH ) { + unsigned int offset = ( tb->flags & TB_INDICATOR ) ? DOT_OFFSET : 0; + return textbox_get_font_width ( tb ) + widget_padding_get_padding_width ( wid ) + offset; } return tb->widget.w; } @@ -787,17 +750,14 @@ int textbox_get_width ( widget *wid ) 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; + if ( tb->flags & TB_AUTOHEIGHT ) { + return textbox_get_height ( tb ); } return tb->widget.h; } int textbox_get_height ( const textbox *tb ) { - return textbox_get_font_height ( tb ) + 2 * config.line_padding; + return textbox_get_font_height ( tb ) + widget_padding_get_padding_height ( WIDGET (tb) ); } int textbox_get_font_height ( const textbox *tb ) @@ -814,14 +774,30 @@ int textbox_get_font_width ( const textbox *tb ) return width; } -double textbox_get_estimated_char_width ( void ) +/** Caching for the expected character height. */ +static double char_height = -1; +double textbox_get_estimated_char_height ( void ) { - int width = pango_font_metrics_get_approximate_char_width ( p_metrics ); - return ( width ) / (double) PANGO_SCALE; + if ( char_height < 0 ){ + int height = pango_font_metrics_get_ascent ( p_metrics ) + pango_font_metrics_get_descent ( p_metrics ); + char_height = ( height ) / (double) PANGO_SCALE; + } + return char_height; } -int textbox_get_estimated_char_height ( void ) +/** Caching for the expected character width. */ +static double char_width = -1; +double textbox_get_estimated_char_width ( void ) +{ + if ( char_width < 0 ){ + int width = pango_font_metrics_get_approximate_char_width ( p_metrics ); + char_width = ( width ) / (double) PANGO_SCALE; + } + return char_width; +} + +int textbox_get_estimated_height ( const textbox *tb, int eh ) { int height = pango_font_metrics_get_ascent ( p_metrics ) + pango_font_metrics_get_descent ( p_metrics ); - return ( height ) / PANGO_SCALE + 2 * config.line_padding; + return ( eh*height ) / PANGO_SCALE + widget_padding_get_padding_height ( WIDGET ( tb ) ); } diff --git a/source/widgets/widget.c b/source/widgets/widget.c index c77ef1f9..fa11506c 100644 --- a/source/widgets/widget.c +++ b/source/widgets/widget.c @@ -1,6 +1,31 @@ #include #include "widgets/widget.h" #include "widgets/widget-internal.h" +#include "theme.h" + + +void widget_init ( widget *widget , const char *name ) +{ + widget->name = g_strdup(name); + widget->padding = (Padding){ {0, PW_PX, SOLID}, {0, PW_PX, SOLID}, {0, PW_PX, SOLID}, {0, PW_PX, SOLID}}; + widget->border = (Padding){ {0, PW_PX, SOLID}, {0, PW_PX, SOLID}, {0, PW_PX, SOLID}, {0, PW_PX, SOLID}}; + widget->margin = (Padding){ {0, PW_PX, SOLID}, {0, PW_PX, SOLID}, {0, PW_PX, SOLID}, {0, PW_PX, SOLID}}; + + widget->padding = rofi_theme_get_padding ( widget, "padding", widget->padding); + widget->border = rofi_theme_get_padding ( widget, "border", widget->border); + widget->margin = rofi_theme_get_padding ( widget, "margin", widget->margin); +} + +void widget_set_state ( widget *widget, const char *state ) +{ + if ( g_strcmp0(widget->state, state ) ){ + widget->state = state; + // Update border. + widget->border = rofi_theme_get_padding ( widget, "border", widget->border); + + widget_queue_redraw ( widget ); + } +} int widget_intersect ( const widget *widget, int x, int y ) { @@ -20,12 +45,16 @@ void widget_resize ( widget *widget, short w, short h ) { if ( widget != NULL ) { if ( widget->resize != NULL ) { - widget->resize ( widget, w, h ); + if ( widget->w != w || widget->h != h ) { + widget->resize ( widget, w, h ); + } } else { widget->w = w; widget->h = h; } + // On a resize we always want to udpate. + widget_queue_redraw ( widget ); } } void widget_move ( widget *widget, short x, short y ) @@ -49,6 +78,7 @@ void widget_enable ( widget *widget ) if ( widget && !widget->enabled ) { widget->enabled = TRUE; widget_update ( widget ); + widget_update ( widget->parent ); } } void widget_disable ( widget *widget ) @@ -56,20 +86,91 @@ void widget_disable ( widget *widget ) if ( widget && widget->enabled ) { widget->enabled = FALSE; widget_update ( widget ); + widget_update ( widget->parent ); } } void widget_draw ( widget *widget, cairo_t *d ) { // Check if enabled and if draw is implemented. if ( widget && widget->enabled && widget->draw ) { + // Don't draw if there is no space. + if ( widget->h < 1 || widget->w < 1 ){ + widget->need_redraw = FALSE; + return; + } + // Store current state. + cairo_save ( d ); + int margin_left = distance_get_pixel ( widget->margin.left, ORIENTATION_HORIZONTAL); + int margin_top = distance_get_pixel ( widget->margin.top, ORIENTATION_VERTICAL); + int margin_right = distance_get_pixel ( widget->margin.right, ORIENTATION_HORIZONTAL); + int margin_bottom = distance_get_pixel ( widget->margin.bottom, ORIENTATION_VERTICAL); + // Define a clipmask so we won't draw outside out widget. + cairo_rectangle ( d, + widget->x+margin_left, + widget->y+margin_top, + widget->w-margin_right-margin_left, + widget->h-margin_top-margin_bottom + ); + cairo_clip ( d ); + + rofi_theme_get_color ( widget, "background", d ); + cairo_paint( d ) ; + + // Set new x/y possition. + cairo_translate ( d, widget->x, widget->y); + + int left = distance_get_pixel ( widget->border.left, ORIENTATION_HORIZONTAL ); + int right = distance_get_pixel ( widget->border.right, ORIENTATION_HORIZONTAL); + int top = distance_get_pixel ( widget->border.top, ORIENTATION_VERTICAL); + int bottom = distance_get_pixel ( widget->border.bottom, ORIENTATION_VERTICAL ); + if ( left || top || right || bottom ) { + cairo_save ( d ); + rofi_theme_get_color ( widget, "foreground", d ); + if ( left > 0 ) { + cairo_set_line_width ( d, left ); + distance_get_linestyle ( widget->border.left, d); + cairo_move_to ( d, margin_left + left/2.0, margin_top ); + cairo_line_to ( d, margin_left + left/2.0, widget->h-margin_bottom); + cairo_stroke ( d ); + } + if ( right > 0 ) { + cairo_set_line_width ( d, right ); + distance_get_linestyle ( widget->border.right, d); + cairo_move_to ( d, widget->w - margin_right - right/2.0, 0 ); + cairo_line_to ( d, widget->w - margin_right - right/2.0, widget->h-margin_bottom ); + cairo_stroke ( d ); + } + if ( top > 0 ) { + cairo_set_line_width ( d, top ); + distance_get_linestyle ( widget->border.top, d); + cairo_move_to ( d, margin_left,margin_top+ top/2.0 ); + cairo_line_to ( d, widget->w-margin_right, margin_top+top/2.0 ); + cairo_stroke ( d ); + } + if ( bottom > 0 ) { + cairo_set_line_width ( d, bottom ); + distance_get_linestyle ( widget->border.bottom, d); + cairo_move_to ( d, margin_left, widget->h-bottom/2.0-margin_bottom); + cairo_line_to ( d, widget->w-margin_right, widget->h-bottom/2.0-margin_bottom); + cairo_stroke ( d ); + } + cairo_restore (d); + } widget->draw ( widget, d ); widget->need_redraw = FALSE; + + cairo_restore ( d ); } } void widget_free ( widget *wid ) { - if ( wid && wid->free ) { - wid->free ( wid ); + if ( wid ) { + if ( wid->name ) { + g_free ( wid->name ); + } + if ( wid->free ) { + wid->free ( wid ); + } } } @@ -115,8 +216,6 @@ void widget_update ( widget *widget ) if ( widget->update ) { widget->update ( widget ); } - // Recurse back. - widget_update ( widget->parent ); } } @@ -163,3 +262,79 @@ gboolean widget_motion_notify ( widget *wid, xcb_motion_notify_event_t *xme ) return FALSE; } + +void widget_set_name ( widget *wid, const char *name ) +{ + if ( wid->name ) { + g_free(wid); + } + wid->name = g_strdup ( name ); +} + +int widget_padding_get_left ( const widget *wid ) +{ + int distance = distance_get_pixel ( wid->padding.left, ORIENTATION_HORIZONTAL ); + distance += distance_get_pixel ( wid->border.left, ORIENTATION_HORIZONTAL ); + distance += distance_get_pixel ( wid->margin.left, ORIENTATION_HORIZONTAL ); + return distance; +} +int widget_padding_get_right ( const widget *wid ) +{ + int distance = distance_get_pixel ( wid->padding.right, ORIENTATION_HORIZONTAL ); + distance += distance_get_pixel ( wid->border.right, ORIENTATION_HORIZONTAL ); + distance += distance_get_pixel ( wid->margin.right, ORIENTATION_HORIZONTAL ); + return distance; +} +int widget_padding_get_top ( const widget *wid ) +{ + int distance = distance_get_pixel ( wid->padding.top, ORIENTATION_VERTICAL ); + distance += distance_get_pixel ( wid->border.top, ORIENTATION_VERTICAL ); + distance += distance_get_pixel ( wid->margin.top, ORIENTATION_VERTICAL ); + return distance; +} +int widget_padding_get_bottom ( const widget *wid ) +{ + int distance = distance_get_pixel ( wid->padding.bottom, ORIENTATION_VERTICAL ); + distance += distance_get_pixel ( wid->border.bottom, ORIENTATION_VERTICAL ); + distance += distance_get_pixel ( wid->margin.bottom, ORIENTATION_VERTICAL ); + return distance; +} + +int widget_padding_get_remaining_width ( const widget *wid ) +{ + int width = wid->w; + width -= widget_padding_get_left ( wid ); + width -= widget_padding_get_right ( wid ); + return width; +} +int widget_padding_get_remaining_height ( const widget *wid ) +{ + int height = wid->h; + height -= widget_padding_get_top ( wid ); + height -= widget_padding_get_bottom ( wid ); + return height; +} +int widget_padding_get_padding_height ( const widget *wid ) +{ + int height = 0; + height += widget_padding_get_top ( wid ); + height += widget_padding_get_bottom ( wid ); + return height; +} +int widget_padding_get_padding_width ( const widget *wid ) +{ + int width = 0; + width += widget_padding_get_left ( wid ); + width += widget_padding_get_right ( wid ); + return width; +} + + +int widget_get_desired_height ( widget *wid ) +{ + if ( wid && wid->get_desired_height ) + { + return wid->get_desired_height ( wid ); + } + return 0; +} diff --git a/source/xrmoptions.c b/source/xrmoptions.c index 967c5b72..e807dbfd 100644 --- a/source/xrmoptions.c +++ b/source/xrmoptions.c @@ -194,6 +194,8 @@ static XrmOption xrmOptions[] = { "Click outside the window to exit", CONFIG_DEFAULT }, { xrm_Boolean, "show-match", { .snum = &config.show_match }, NULL, "Indicate how it match by underlining it.", CONFIG_DEFAULT }, + { xrm_String, "theme", { .str = &config.theme }, NULL, + "New style theme file", CONFIG_DEFAULT }, }; /** Dynamic array of extra options */ diff --git a/test/box-test.c b/test/box-test.c index b9283d44..d34a4fb1 100644 --- a/test/box-test.c +++ b/test/box-test.c @@ -22,6 +22,16 @@ unsigned int test =0; abort ( ); \ } \ } +int textbox_get_estimated_char_height ( void ); +int textbox_get_estimated_char_height ( void ) +{ + return 16; +} +void rofi_view_get_current_monitor ( int *width, int *height ) +{ + +} + static gboolean test_widget_clicked ( G_GNUC_UNUSED widget *wid, G_GNUC_UNUSED xcb_button_press_event_t* xce, G_GNUC_UNUSED void *data ) { @@ -31,11 +41,12 @@ static gboolean test_widget_clicked ( G_GNUC_UNUSED widget *wid, G_GNUC_UNUSED x int main ( G_GNUC_UNUSED int argc, G_GNUC_UNUSED char **argv ) { { - box *b = box_create ( BOX_HORIZONTAL, 0, 0, 100, 20 ); - box_set_padding ( b, 5 ); + box *b = box_create ( "box", BOX_HORIZONTAL ); + //box_set_padding ( b, 5 ); + widget_resize ( WIDGET (b), 100, 20); widget *wid1 = g_malloc0(sizeof(widget)); - box_add ( b , WIDGET( wid1 ), TRUE, FALSE ); + box_add ( b , WIDGET( wid1 ), TRUE, 0 ); // Widget not enabled. no width allocated. TASSERTE ( wid1->h, 0); TASSERTE ( wid1->w, 0 ); @@ -46,57 +57,58 @@ int main ( G_GNUC_UNUSED int argc, G_GNUC_UNUSED char **argv ) TASSERTE ( wid1->w, 100 ); widget *wid2 = g_malloc0(sizeof(widget)); widget_enable ( WIDGET ( wid2 ) ); - box_add ( b , WIDGET( wid2 ), TRUE, FALSE ); + box_add ( b , WIDGET( wid2 ), TRUE, 1 ); TASSERTE ( wid1->h, 20); - TASSERTE ( wid1->w, 47); + TASSERTE ( wid1->w, 49); TASSERTE ( wid2->h, 20); - TASSERTE ( wid2->w, 48); + TASSERTE ( wid2->w, 49); widget *wid3 = g_malloc0(sizeof(widget)); widget_enable ( WIDGET ( wid3 ) ); - box_add ( b , WIDGET( wid3 ), FALSE, FALSE ); + box_add ( b , WIDGET( wid3 ), FALSE, 2 ); TASSERTE ( wid1->h, 20); - TASSERTE ( wid1->w, 45); + TASSERTE ( wid1->w, 48); TASSERTE ( wid2->h, 20); - TASSERTE ( wid2->w, 45); + TASSERTE ( wid2->w, 48); widget_resize ( WIDGET (wid3) , 20, 10 ); // TODO should this happen automagically? widget_update ( WIDGET ( b ) ) ; TASSERTE ( wid1->h, 20); - TASSERTE ( wid1->w, 35); + TASSERTE ( wid1->w, 38); TASSERTE ( wid2->h, 20); - TASSERTE ( wid2->w, 35); + TASSERTE ( wid2->w, 38); TASSERTE ( wid3->h, 20); TASSERTE ( wid3->w, 20); widget_resize ( WIDGET (b ), 200, 20 ); TASSERTE ( wid1->h, 20); - TASSERTE ( wid1->w, 85); + TASSERTE ( wid1->w, 88); TASSERTE ( wid2->h, 20); - TASSERTE ( wid2->w, 85); + TASSERTE ( wid2->w, 88); TASSERTE ( wid3->h, 20); TASSERTE ( wid3->w, 20); - TASSERTE ( box_get_fixed_pixels ( b ) , 30 ); + TASSERTE ( box_get_fixed_pixels ( b ) , 24 ); widget *wid4 = g_malloc0(sizeof(widget)); widget_enable ( WIDGET ( wid4 ) ); widget_resize ( WIDGET ( wid4 ), 20, 20 ); - box_add ( b , WIDGET( wid4 ), FALSE, TRUE ); + box_add ( b , WIDGET( wid4 ), FALSE, 5 ); TASSERTE ( wid4->x, 200-20); widget *wid5 = g_malloc0(sizeof(widget)); widget_enable ( WIDGET ( wid5 ) ); widget_resize ( WIDGET ( wid5 ), 20, 20 ); - box_add ( b , WIDGET( wid5 ), TRUE, TRUE ); - TASSERTE ( wid5->x, 128); + box_add ( b , WIDGET( wid5 ), TRUE, 6 ); + TASSERTE ( wid5->x, 149); widget_free ( WIDGET ( b ) ); } { - box *b = box_create ( BOX_VERTICAL, 0, 0, 20, 100 ); - box_set_padding ( b, 5 ); + box *b = box_create ( "box", BOX_VERTICAL ); + widget_resize ( WIDGET (b), 20, 100); + //box_set_padding ( b, 5 ); widget *wid1 = g_malloc0(sizeof(widget)); - box_add ( b , WIDGET( wid1 ), TRUE, FALSE ); + box_add ( b , WIDGET( wid1 ), TRUE, 0 ); // Widget not enabled. no width allocated. TASSERTE ( wid1->h, 0); TASSERTE ( wid1->w, 0 ); @@ -107,60 +119,62 @@ int main ( G_GNUC_UNUSED int argc, G_GNUC_UNUSED char **argv ) TASSERTE ( wid1->w, 20 ); widget *wid2 = g_malloc0(sizeof(widget)); widget_enable ( WIDGET ( wid2 ) ); - box_add ( b , WIDGET( wid2 ), TRUE, FALSE ); + box_add ( b , WIDGET( wid2 ), TRUE, 1 ); TASSERTE ( wid1->w, 20); - TASSERTE ( wid1->h, 47); + TASSERTE ( wid1->h, 49); TASSERTE ( wid2->w, 20); - TASSERTE ( wid2->h, 48); + TASSERTE ( wid2->h, 49); widget *wid3 = g_malloc0(sizeof(widget)); widget_enable ( WIDGET ( wid3 ) ); - box_add ( b , WIDGET( wid3 ), FALSE, FALSE ); + box_add ( b , WIDGET( wid3 ), FALSE, 2 ); TASSERTE ( wid1->w, 20); - TASSERTE ( wid1->h, 45); + TASSERTE ( wid1->h, 48); TASSERTE ( wid2->w, 20); - TASSERTE ( wid2->h, 45); + TASSERTE ( wid2->h, 48); widget_resize ( WIDGET (wid3) , 10, 20 ); // TODO should this happen automagically? widget_update ( WIDGET ( b ) ) ; TASSERTE ( wid1->w, 20); - TASSERTE ( wid1->h, 35); + TASSERTE ( wid1->h, 48); TASSERTE ( wid2->w, 20); - TASSERTE ( wid2->h, 35); + TASSERTE ( wid2->h, 48); TASSERTE ( wid3->w, 20); - TASSERTE ( wid3->h, 20); + TASSERTE ( wid3->h, 0); widget_resize ( WIDGET (b ), 20, 200 ); TASSERTE ( wid1->w, 20); - TASSERTE ( wid1->h, 85); + TASSERTE ( wid1->h, 98); TASSERTE ( wid2->w, 20); - TASSERTE ( wid2->h, 85); + TASSERTE ( wid2->h, 98); TASSERTE ( wid3->w, 20); - TASSERTE ( wid3->h, 20); - TASSERTE ( box_get_fixed_pixels ( b ) , 30 ); + // has no height, gets no height. + TASSERTE ( wid3->h, 0); + TASSERTE ( box_get_fixed_pixels ( b ) , 4 ); widget *wid4 = g_malloc0(sizeof(widget)); widget_enable ( WIDGET ( wid4 ) ); widget_resize ( WIDGET ( wid4 ), 20, 20 ); - box_add ( b , WIDGET( wid4 ), FALSE, TRUE ); - TASSERTE ( wid4->y, 200-20); + box_add ( b , WIDGET( wid4 ), FALSE, 5 ); + TASSERTE ( wid4->y, 200); widget *wid5 = g_malloc0(sizeof(widget)); widget_enable ( WIDGET ( wid5 ) ); widget_resize ( WIDGET ( wid5 ), 20, 20 ); - box_add ( b , WIDGET( wid5 ), TRUE, TRUE ); - TASSERTE ( wid5->y, 128); + box_add ( b , WIDGET( wid5 ), TRUE, 6 ); + TASSERTE ( wid5->y, 136); widget_free ( WIDGET ( b ) ); } { - box *b = box_create ( BOX_VERTICAL, 0, 0, 20, 100 ); - box_set_padding ( b, 5 ); + box *b = box_create ( "box", BOX_VERTICAL ); + widget_resize ( WIDGET (b), 20, 100); + //box_set_padding ( b, 5 ); widget *wid1 = g_malloc0(sizeof(widget)); widget_enable(wid1); wid1->clicked = test_widget_clicked; - box_add ( b , WIDGET( wid1 ), TRUE, FALSE ); + box_add ( b , WIDGET( wid1 ), TRUE, 0 ); widget *wid2 = g_malloc0(sizeof(widget)); widget_enable(wid2); - box_add ( b , WIDGET( wid2 ), TRUE, FALSE ); + box_add ( b , WIDGET( wid2 ), TRUE, 1 ); xcb_button_press_event_t xce; xce.event_x = 10; @@ -169,7 +183,7 @@ int main ( G_GNUC_UNUSED int argc, G_GNUC_UNUSED char **argv ) xce.event_y = 50; TASSERTE ( widget_clicked ( WIDGET(b), &xce ), 0); - xce.event_y = 45; + xce.event_y = 48; TASSERTE ( widget_clicked ( WIDGET(b), &xce ), 1); widget_disable ( wid2 ); xce.event_y = 60; diff --git a/test/scrollbar-test.c b/test/scrollbar-test.c index b0cdc95d..e0d022b8 100644 --- a/test/scrollbar-test.c +++ b/test/scrollbar-test.c @@ -23,14 +23,25 @@ unsigned int test =0; } \ } +int textbox_get_estimated_char_height ( void ); +int textbox_get_estimated_char_height ( void ) +{ + return 16; +} void color_separator ( G_GNUC_UNUSED void *d ) { } +void rofi_view_get_current_monitor ( int *width, int *height ) +{ + +} + int main ( G_GNUC_UNUSED int argc, G_GNUC_UNUSED char **argv ) { - scrollbar * sb = scrollbar_create ( 0, 0, 10, 100); + scrollbar * sb = scrollbar_create ( "scrollbar" ); + widget_resize ( WIDGET (sb), 10, 100); scrollbar_set_handle ( NULL, 10213); scrollbar_set_max_value ( NULL, 10 ); diff --git a/test/textbox-test.c b/test/textbox-test.c index 3fa33344..3a04cea4 100644 --- a/test/textbox-test.c +++ b/test/textbox-test.c @@ -24,12 +24,10 @@ unsigned int normal_window_mode = 0; void rofi_view_queue_redraw () { } -Color color_get ( G_GNUC_UNUSED const char *name ) +void rofi_view_get_current_monitor ( int *width, int *height ) { - Color retv = { 1.0, 1.0, 1.0, 1.0 }; - return retv; -} +} int rofi_view_error_dialog ( const char *msg, G_GNUC_UNUSED int markup ) { fputs ( msg, stderr ); @@ -53,8 +51,7 @@ int main ( G_GNUC_UNUSED int argc, G_GNUC_UNUSED char **argv ) PangoContext *p = pango_cairo_create_context ( draw ); textbox_set_pango_context ( p ); - textbox *box = textbox_create ( TB_EDITABLE | TB_AUTOWIDTH | TB_AUTOHEIGHT, 0, 0, -1, -1, - NORMAL, "test" ); + textbox *box = textbox_create ( "textbox", TB_EDITABLE | TB_AUTOWIDTH | TB_AUTOHEIGHT, NORMAL, "test" ); TASSERT ( box != NULL ); textbox_keybinding ( box, MOVE_END ); diff --git a/test/widget-test.c b/test/widget-test.c index 109197cb..58ad7c4f 100644 --- a/test/widget-test.c +++ b/test/widget-test.c @@ -12,7 +12,19 @@ unsigned int test =0; assert ( a ); \ printf ( "Test %3i passed (%s)\n", ++test, # a ); \ } +void rofi_view_queue_redraw ( void ) +{ +} +void rofi_view_get_current_monitor ( int *width, int *height ) +{ + +} +int rofi_view_error_dialog ( const char *msg, G_GNUC_UNUSED int markup ) +{ + fputs ( msg, stderr ); + return FALSE; +} int main ( G_GNUC_UNUSED int argc, G_GNUC_UNUSED char **argv ) { diff --git a/themes/DarkBlue.theme b/themes/DarkBlue.theme index ec0d080a..eb4d2d4d 100644 --- a/themes/DarkBlue.theme +++ b/themes/DarkBlue.theme @@ -4,11 +4,11 @@ ! Copyright: Dave Davenport ! ------------------------------------------------------------------------------ ! "Color scheme for normal row" Set from: File -rofi.color-normal: argb:0000000, #dbdfbc, argb:00000000, #dbdfbc, #02143f +rofi.color-normal: argb:00000000, #dbdfbc, argb:00000000, #dbdfbc, #02143f ! "Color scheme for urgent row" Set from: File -rofi.color-urgent: argb:0000000, #ff81ff, argb:00000000, #ff817f, #02143f +rofi.color-urgent: argb:00000000, #ff81ff, argb:00000000, #ff817f, #02143f ! "Color scheme for active row" Set from: File -rofi.color-active: argb:0000000, #8ac4ff, argb:00000000, #8ac4ff, #02143f +rofi.color-active: argb:00000000, #8ac4ff, argb:00000000, #8ac4ff, #02143f ! "Color scheme window" Set from: File rofi.color-window: argb:dd000021, #dbdfbc, #dbdfbc ! "Separator style (none, dash, solid)" Set from: XResources