Merge pull request #530 from DaveDavenport/themer

Themer
This commit is contained in:
Dave Davenport 2017-01-08 19:48:38 +01:00 committed by GitHub
commit 8fe5ea648a
41 changed files with 3353 additions and 784 deletions

View file

@ -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

View file

@ -9,6 +9,8 @@
* autoconf
* automake (1.11.3 or up)
* pkg-config
* flex
* bison
* Developer packages of the external libraries
### External libraries

View file

@ -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\

View file

@ -128,4 +128,5 @@ Settings config = {
.window_format = "{w} {c} {t}",
.click_to_exit = TRUE,
.show_match = TRUE,
.theme = NULL,
};

View file

@ -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

View file

@ -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

View file

@ -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

172
doc/themer.md Normal file
View file

@ -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.

View file

@ -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
*

View file

@ -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;

337
include/theme.h Normal file
View file

@ -0,0 +1,337 @@
#ifndef THEME_H
#define THEME_H
#include <glib.h>
#include <cairo.h>
#include <widgets/widget.h>
#include <settings.h>
/** 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

View file

@ -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;

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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
*
@ -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

View file

@ -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

View file

@ -1,65 +0,0 @@
#ifndef ROFI_SEPARATOR_H
#define ROFI_SEPARATOR_H
#include <cairo.h>
#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

View file

@ -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
*

View file

@ -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

View file

@ -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

336
lexer/theme-lexer.l Normal file
View file

@ -0,0 +1,336 @@
%option noyywrap nounput never-interactive
%option bison-locations
%{
#include <stdio.h>
#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
}
<INITIAL>{ASTERIX} {
g_queue_push_head ( queue, GINT_TO_POINTER (YY_START) );
BEGIN(DEFAULTS);
return PDEFAULTS;
}
<DEFAULTS>{WHITESPACE} {}
<DEFAULTS>"\{" {
g_queue_push_head ( queue, GINT_TO_POINTER (YY_START) );
BEGIN(ENTRY);
return BOPEN;
}
/* Go into parsing an entry */
<NAMESTR>"\{" {
g_queue_push_head ( queue, GINT_TO_POINTER (YY_START) );
BEGIN(ENTRY);
return BOPEN;
}
/* Pop out of parsing an entry. */
<ENTRY>"\}" {
g_queue_pop_head ( queue );
BEGIN(GPOINTER_TO_INT(g_queue_pop_head ( queue )));
return BCLOSE;
}
<INITIAL>"#" { g_queue_push_head ( queue, GINT_TO_POINTER (YY_START) ); BEGIN(NAMESTR);return NAME_PREFIX;}
<NAMESTR>\.|{WHITESPACE} { return NSEP; }
<ENTRY>{WORD} { yylval->sval = g_strdup(yytext); return N_STRING;}
<NAMESTR>{WORD} { yylval->sval = g_strdup(yytext); return NAME_ELEMENT;}
/* After Namestr/Classstr we want to go to state str, then to { */
/*<NAMESTR>{WHITESPACE} { BEGIN(GPOINTER_TO_INT (g_queue_pop_head ( queue )));}*/
<INITIAL,ENTRY>{WHITESPACE}+ ; // ignore all whitespace
<PROPERTIES>{WHITESPACE}+ ; // ignore all whitespace
<INITIAL,ENTRY>":" { g_queue_push_head ( queue, GINT_TO_POINTER (YY_START) ); BEGIN(PROPERTIES); return PSEP; }
<PROPERTIES>";" { BEGIN(GPOINTER_TO_INT ( g_queue_pop_head ( queue ))); return PCLOSE;}
<PROPERTIES>(true|false) { yylval->bval= g_strcmp0(yytext, "true") == 0; return T_BOOLEAN;}
<PROPERTIES>{NUMBER}+ { yylval->ival = (int)g_ascii_strtoll(yytext, NULL, 10); return T_INT;}
<PROPERTIES>{NUMBER}+\.{NUMBER}+ { yylval->fval = g_ascii_strtod(yytext, NULL); return T_DOUBLE;}
<PROPERTIES>\"{STRING}\" { yytext[yyleng-1] = '\0'; yylval->sval = g_strdup(&yytext[1]); return T_STRING;}
<PROPERTIES>@{WORD} {
yylval->sval = g_strdup(yytext);
return T_LINK;
}
<PROPERTIES>{REAL}{EM} {
yylval->distance.distance = (double)g_ascii_strtod(yytext, NULL);
yylval->distance.type = PW_EM;
yylval->distance.style = SOLID;
return T_PIXEL;
}
<PROPERTIES>{NUMBER}+{PX} {
yylval->distance.distance = (double)g_ascii_strtoll(yytext, NULL, 10);
yylval->distance.type = PW_PX;
yylval->distance.style = SOLID;
return T_PIXEL;
}
<PROPERTIES>{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;
}
<PROPERTIES>{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;
}
<PROPERTIES>{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;
}
<PROPERTIES>{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;
}
<PROPERTIES>{REAL}{PERCENT} {
yylval->distance.distance = (double)g_ascii_strtod(yytext, NULL);
yylval->distance.type = PW_PERCENT;
yylval->distance.style = SOLID;
return T_PIXEL;
}
<PROPERTIES>{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;
}
<PROPERTIES>{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;
}
<PROPERTIES>#{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;
}
<PROPERTIES>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;
}
<PROPERTIES>#{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;
}
<PROPERTIES>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;
}
<PROPERTIES>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;
}
<PROPERTIES>{CENTER} {
yylval->ival = WL_CENTER;
return T_POSITION;
}
<PROPERTIES>{EAST} {
yylval->ival = WL_EAST;
return T_POSITION;
}
<PROPERTIES>{WEST} {
yylval->ival = WL_WEST;
return T_POSITION;
}
<PROPERTIES>{SOUTH}{EAST} {
yylval->ival = WL_SOUTH_EAST;
return T_POSITION;
}
<PROPERTIES>{SOUTH}{WEST} {
yylval->ival = WL_SOUTH_WEST;
return T_POSITION;
}
<PROPERTIES>{SOUTH} {
yylval->ival = WL_SOUTH;
return T_POSITION;
}
<PROPERTIES>{NORTH}{EAST} {
yylval->ival = WL_NORTH_EAST;
return T_POSITION;
}
<PROPERTIES>{NORTH}{WEST} {
yylval->ival = WL_NORTH_WEST;
return T_POSITION;
}
<PROPERTIES>{NORTH} {
yylval->ival = WL_NORTH;
return T_POSITION;
}
<PROPERTIES>NORTH {
yylval->ival = WL_NORTH;
return T_POSITION;
}
<INITIAL><<EOF>> {
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 ++;
};
<INITIAL>. {
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();
}
%%

185
lexer/theme-parser.y Normal file
View file

@ -0,0 +1,185 @@
%define api.pure
%glr-parser
%skeleton "glr.c"
%locations
%debug
%error-verbose
%code requires {
#include "theme.h"
}
%{
#include <stdio.h>
#include <stdlib.h>
#include <glib.h>
#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 <ival> T_INT
%token <fval> T_DOUBLE
%token <sval> T_STRING
%token <sval> N_STRING
%token <ival> T_POSITION;
%token <sval> NAME_ELEMENT "Element name"
%token <bval> T_BOOLEAN
%token <colorval> T_COLOR
%token <distance> T_PIXEL
%token <sval> T_LINK
%token <sval> 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 <sval> entry
%type <sval> pvalue
%type <theme> entries
%type <name_path> name_path
%type <property> property
%type <property_list> property_list
%type <property_list> 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; }
;
%%

76
script/rofi-convert-theme.sh Executable file
View file

@ -0,0 +1,76 @@
#!/usr/bin/env bash
#
# This code is released in public domain by Dave Davenport <qball@gmpclient.org>
# 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

View file

@ -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 );

View file

@ -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 ) {

840
source/theme.c Normal file
View file

@ -0,0 +1,840 @@
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#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

View file

@ -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 )

View file

@ -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 ) );
}
}

148
source/widgets/container.c Normal file
View file

@ -0,0 +1,148 @@
/**
* rofi
*
* MIT/X11 License
* Modified 2016-2017 Qball Cow <qball@gmpclient.org>
*
* 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 <config.h>
#include <stdio.h>
#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))
);
}
}

View file

@ -29,6 +29,11 @@
#include <widgets/listview.h>
#include <widgets/scrollbar.h>
#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;
}
}

View file

@ -27,24 +27,37 @@
#include <glib.h>
#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;
}

View file

@ -1,129 +0,0 @@
/**
* MIT/X11 License
* Modified (c) 2016-2017 Qball Cow <qball@gmpclient.org>
*
* 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 <config.h>
#include <xkbcommon/xkbcommon.h>
#include <glib.h>
#include <string.h>
#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 );
}

View file

@ -29,19 +29,21 @@
#include <string.h>
#include <glib.h>
#include <math.h>
#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 ) );
}

View file

@ -1,6 +1,31 @@
#include <glib.h>
#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;
}

View file

@ -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 */

View file

@ -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;

View file

@ -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 );

View file

@ -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 );

View file

@ -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 )
{

View file

@ -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