rofi  1.6.1
widget.c
Go to the documentation of this file.
1 /*
2  * rofi
3  *
4  * MIT/X11 License
5  * Copyright © 2013-2020 Qball Cow <qball@gmpclient.org>
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining
8  * a copy of this software and associated documentation files (the
9  * "Software"), to deal in the Software without restriction, including
10  * without limitation the rights to use, copy, modify, merge, publish,
11  * distribute, sublicense, and/or sell copies of the Software, and to
12  * permit persons to whom the Software is furnished to do so, subject to
13  * the following conditions:
14  *
15  * The above copyright notice and this permission notice shall be
16  * included in all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25  *
26  */
27 
28 #include <glib.h>
29 #include <math.h>
30 #include "widgets/widget.h"
32 #include "theme.h"
33 
35 #define WIDGET_DEFAULT_PADDING 0
36 #define WIDGET_PADDING_INIT { { WIDGET_DEFAULT_PADDING, ROFI_PU_PX, ROFI_DISTANCE_MODIFIER_NONE, NULL, NULL }, ROFI_HL_SOLID }
37 
38 /* Corner radius - tl, tr, br, bl */
39 static void draw_rounded_rect ( cairo_t * d,
40  double x1, double y1, double x2, double y2,
41  double r0, double r1, double r2, double r3 )
42 {
43  if ( r0 > 0 ) {
44  cairo_move_to ( d, x1, y1 + r0 );
45  cairo_arc ( d, x1 + r0, y1 + r0, r0, -G_PI, -G_PI_2 );
46  }
47  else {
48  cairo_move_to ( d, x1, y1 );
49  }
50  if ( r1 > 0 ) {
51  cairo_line_to ( d, x2 - r1, y1 );
52  cairo_arc ( d, x2 - r1, y1 + r1, r1, -G_PI_2, 0.0 );
53  }
54  else {
55  cairo_line_to ( d, x2, y1 );
56  }
57  if ( r2 > 0 ) {
58  cairo_line_to ( d, x2, y2 - r2 );
59  cairo_arc ( d, x2 - r2, y2 - r2, r2, 0.0, G_PI_2 );
60  }
61  else {
62  cairo_line_to ( d, x2, y2 );
63  }
64  if ( r3 > 0 ) {
65  cairo_line_to ( d, x1 + r3, y2 );
66  cairo_arc ( d, x1 + r3, y2 - r3, r3, G_PI_2, G_PI );
67  }
68  else {
69  cairo_line_to ( d, x1, y2 );
70  }
71  cairo_close_path ( d );
72 }
73 
74 void widget_init ( widget *wid, widget *parent, WidgetType type, const char *name )
75 {
76  wid->type = type;
77  wid->parent = parent;
78  wid->name = g_strdup ( name );
83 
84  wid->padding = rofi_theme_get_padding ( wid, "padding", wid->def_padding );
85  wid->border = rofi_theme_get_padding ( wid, "border", wid->def_border );
86  wid->border_radius = rofi_theme_get_padding ( wid, "border-radius", wid->def_border_radius );
87  wid->margin = rofi_theme_get_padding ( wid, "margin", wid->def_margin );
88 
89  // bled by default
90  wid->enabled = rofi_theme_get_boolean ( wid, "enabled", TRUE );
91 }
92 
93 void widget_set_state ( widget *widget, const char *state )
94 {
95  if ( widget == NULL ) {
96  return;
97  }
98  if ( g_strcmp0 ( widget->state, state ) ) {
99  widget->state = state;
100  // Update border.
103  if ( widget->set_state != NULL ) {
104  widget->set_state ( widget, state );
105  }
107  }
108 }
109 
110 int widget_intersect ( const widget *widget, int x, int y )
111 {
112  if ( widget == NULL ) {
113  return FALSE;
114  }
115 
116  if ( x >= ( widget->x ) && x < ( widget->x + widget->w ) &&
117  y >= ( widget->y ) && y < ( widget->y + widget->h ) ) {
118  return TRUE;
119  }
120  return FALSE;
121 }
122 
123 void widget_resize ( widget *widget, short w, short h )
124 {
125  if ( widget == NULL ) {
126  return;
127  }
128  if ( widget->resize != NULL ) {
129  if ( widget->w != w || widget->h != h ) {
130  widget->resize ( widget, w, h );
131  }
132  }
133  else {
134  widget->w = w;
135  widget->h = h;
136  }
137  // On a resize we always want to udpate.
139 }
140 void widget_move ( widget *widget, short x, short y )
141 {
142  if ( widget == NULL ) {
143  return;
144  }
145  widget->x = x;
146  widget->y = y;
147 }
149 {
150  if ( widget == NULL ) {
151  return;
152  }
153  widget->type = type;
154 }
155 
157 {
158  if ( widget == NULL ) {
159  return WIDGET_TYPE_UNKNOWN;
160  }
161  return widget->type;
162 }
163 
165 {
166  if ( widget == NULL ) {
167  return FALSE;
168  }
169  return widget->enabled;
170 }
171 
172 void widget_set_enabled ( widget *widget, gboolean enabled )
173 {
174  if ( widget == NULL ) {
175  return;
176  }
177  if ( widget->enabled != enabled ) {
178  widget->enabled = enabled;
179  widget_update ( widget );
182  }
183 }
184 
185 void widget_draw ( widget *widget, cairo_t *d )
186 {
187  if ( widget == NULL ) {
188  return;
189  }
190  // Check if enabled and if draw is implemented.
191  if ( widget->enabled && widget->draw ) {
192  // Don't draw if there is no space.
193  if ( widget->h < 1 || widget->w < 1 ) {
194  widget->need_redraw = FALSE;
195  return;
196  }
197  // Store current state.
198  cairo_save ( d );
199  const int margin_left = distance_get_pixel ( widget->margin.left, ROFI_ORIENTATION_HORIZONTAL );
200  const int margin_top = distance_get_pixel ( widget->margin.top, ROFI_ORIENTATION_VERTICAL );
201  const int margin_right = distance_get_pixel ( widget->margin.right, ROFI_ORIENTATION_HORIZONTAL );
202  const int margin_bottom = distance_get_pixel ( widget->margin.bottom, ROFI_ORIENTATION_VERTICAL );
211 
212  double minof_tl, minof_tr, minof_br, minof_bl;
213  {
214  double left_2 = (double) left / 2;
215  double top_2 = (double) top / 2;
216  double right_2 = (double) right / 2;
217  double bottom_2 = (double) bottom / 2;
218 
219  // Calculate the different offsets for the corners.
220  minof_tl = MIN ( left_2, top_2 );
221  minof_tr = MIN ( right_2, top_2 );
222  minof_br = MIN ( right_2, bottom_2 );
223  minof_bl = MIN ( left_2, bottom_2 );
224 
225  // Contain border radius in widget space
226  double vspace, vspace_2, hspace, hspace_2;
227  vspace = widget->h - ( margin_top + margin_bottom ) - ( top_2 + bottom_2 );
228  hspace = widget->w - ( margin_left + margin_right ) - ( left_2 + right_2 );
229  vspace_2 = vspace / 2;
230  hspace_2 = hspace / 2;
231 
232  if ( radius_bl + radius_tl > vspace ) {
233  radius_bl = MIN ( radius_bl, vspace_2 );
234  radius_tl = MIN ( radius_tl, vspace_2 );
235  }
236  if ( radius_br + radius_tr > vspace ) {
237  radius_br = MIN ( radius_br, vspace_2 );
238  radius_tr = MIN ( radius_tr, vspace_2 );
239  }
240  if ( radius_tl + radius_tr > hspace ) {
241  radius_tr = MIN ( radius_tr, hspace_2 );
242  radius_tl = MIN ( radius_tl, hspace_2 );
243  }
244  if ( radius_bl + radius_br > hspace ) {
245  radius_br = MIN ( radius_br, hspace_2 );
246  radius_bl = MIN ( radius_bl, hspace_2 );
247  }
248  }
249 
250  // Background painting.
251  // Set new x/y position.
252  cairo_translate ( d, widget->x, widget->y );
253  cairo_set_line_width ( d, 0 );
254 
255  draw_rounded_rect ( d,
256  margin_left + ( left > 2 ? left - 1 : left == 1 ? 0.5 : 0 ),
257  margin_top + ( top > 2 ? top - 1 : top == 1 ? 0.5 : 0 ),
258  widget->w - margin_right - ( right > 2 ? right - 1 : right == 1 ? 0.5 : 0 ),
259  widget->h - margin_bottom - ( bottom > 2 ? bottom - 1 : bottom == 1 ? 0.5 : 0 ),
260  radius_tl - ( minof_tl > 1 ? minof_tl - 1 : 0 ),
261  radius_tr - ( minof_tr > 1 ? minof_tr - 1 : 0 ),
262  radius_br - ( minof_br > 1 ? minof_br - 1 : 0 ),
263  radius_bl - ( minof_bl > 1 ? minof_bl - 1 : 0 ) );
264 
265  cairo_set_source_rgba ( d, 1.0, 1.0, 1.0, 1.0 );
266  rofi_theme_get_color ( widget, "background-color", d );
267  cairo_fill_preserve ( d );
268  cairo_clip ( d );
269 
270  widget->draw ( widget, d );
271  widget->need_redraw = FALSE;
272 
273  cairo_restore ( d );
274 
275  if ( left != 0 || top != 0 || right != 0 || bottom != 0 ) {
276  cairo_save ( d );
277  cairo_translate ( d, widget->x, widget->y );
278  cairo_new_path ( d );
279  rofi_theme_get_color ( widget, "border-color", d );
280 
281  double radius_int_tl, radius_int_tr, radius_int_br, radius_int_bl;
282  double radius_out_tl, radius_out_tr, radius_out_br, radius_out_bl;
283 
284  if ( radius_tl > 0 ) {
285  radius_out_tl = radius_tl + minof_tl,
286  radius_int_tl = radius_tl - minof_tl;
287  }
288  else {
289  radius_out_tl = radius_int_tl = 0;
290  }
291  if ( radius_tr > 0 ) {
292  radius_out_tr = radius_tr + minof_tr,
293  radius_int_tr = radius_tr - minof_tr;
294  }
295  else {
296  radius_out_tr = radius_int_tr = 0;
297  }
298  if ( radius_br > 0 ) {
299  radius_out_br = radius_br + minof_br,
300  radius_int_br = radius_br - minof_br;
301  }
302  else {
303  radius_out_br = radius_int_br = 0;
304  }
305  if ( radius_bl > 0 ) {
306  radius_out_bl = radius_bl + minof_bl,
307  radius_int_bl = radius_bl - minof_bl;
308  }
309  else {
310  radius_out_bl = radius_int_bl = 0;
311  }
312 
313  draw_rounded_rect ( d,
314  margin_left,
315  margin_top,
316  widget->w - margin_right,
317  widget->h - margin_bottom,
318  radius_out_tl,
319  radius_out_tr,
320  radius_out_br,
321  radius_out_bl );
322  cairo_new_sub_path ( d );
323  draw_rounded_rect ( d,
324  margin_left + left,
325  margin_top + top,
326  widget->w - margin_right - right,
327  widget->h - margin_bottom - bottom,
328  radius_int_tl,
329  radius_int_tr,
330  radius_int_br,
331  radius_int_bl );
332  cairo_set_fill_rule ( d, CAIRO_FILL_RULE_EVEN_ODD );
333  cairo_fill ( d );
334  cairo_restore ( d );
335  }
336  }
337 }
338 
339 void widget_free ( widget *wid )
340 {
341  if ( wid == NULL ) {
342  return;
343  }
344  if ( wid->name != NULL ) {
345  g_free ( wid->name );
346  }
347  if ( wid->free != NULL ) {
348  wid->free ( wid );
349  }
350 }
351 
353 {
354  if ( widget == NULL ) {
355  return 0;
356  }
357  if ( widget->get_height == NULL ) {
358  return widget->h;
359  }
360  return widget->get_height ( widget );
361 }
363 {
364  if ( widget == NULL ) {
365  return 0;
366  }
367  if ( widget->get_width == NULL ) {
368  return widget->w;
369  }
370  return widget->get_width ( widget );
371 }
373 {
374  if ( widget == NULL ) {
375  return 0;
376  }
377  return widget->x;
378 }
380 {
381  if ( widget == NULL ) {
382  return 0;
383  }
384  return widget->y;
385 }
386 
387 void widget_xy_to_relative ( widget *widget, gint *x, gint *y )
388 {
389  *x -= widget->x;
390  *y -= widget->y;
391  if ( widget->parent == NULL ) {
392  return;
393  }
395 }
396 
398 {
399  if ( widget == NULL ) {
400  return;
401  }
402  // When (desired )size of widget changes.
403  if ( widget->update != NULL ) {
404  widget->update ( widget );
405  }
406 }
407 
409 {
410  if ( wid == NULL ) {
411  return;
412  }
413  widget *iter = wid;
414  // Find toplevel widget.
415  while ( iter->parent != NULL ) {
416  iter->need_redraw = TRUE;
417  iter = iter->parent;
418  }
419  iter->need_redraw = TRUE;
420 }
421 
422 gboolean widget_need_redraw ( widget *wid )
423 {
424  if ( wid == NULL ) {
425  return FALSE;
426  }
427  if ( !wid->enabled ) {
428  return FALSE;
429  }
430  return wid->need_redraw;
431 }
432 
433 widget *widget_find_mouse_target ( widget *wid, WidgetType type, gint x, gint y )
434 {
435  if ( wid == NULL ) {
436  return NULL;
437  }
438 
439  if ( wid->find_mouse_target != NULL ) {
440  widget *target = wid->find_mouse_target ( wid, type, x, y );
441  if ( target != NULL ) {
442  return target;
443  }
444  }
445  if ( wid->type == type ) {
446  return wid;
447  }
448  return NULL;
449 }
450 
451 WidgetTriggerActionResult widget_trigger_action ( widget *wid, guint action, gint x, gint y )
452 {
453  if ( wid == NULL ) {
454  return FALSE;
455  }
456  if ( wid->trigger_action == NULL ) {
457  return FALSE;
458  }
459  return wid->trigger_action ( wid, action, x, y, wid->trigger_action_cb_data );
460 }
461 
463 {
464  if ( wid == NULL ) {
465  return;
466  }
467  wid->trigger_action = cb;
468  wid->trigger_action_cb_data = cb_data;
469 }
470 
471 gboolean widget_motion_notify ( widget *wid, gint x, gint y )
472 {
473  if ( wid == NULL ) {
474  return FALSE;
475  }
476  if ( wid->motion_notify == NULL ) {
477  return FALSE;
478  }
479  return wid->motion_notify ( wid, x, y );
480 }
481 
482 int widget_padding_get_left ( const widget *wid )
483 {
484  if ( wid == NULL ) {
485  return 0;
486  }
490  return distance;
491 }
493 {
494  if ( wid == NULL ) {
495  return 0;
496  }
500  return distance;
501 }
502 int widget_padding_get_top ( const widget *wid )
503 {
504  if ( wid == NULL ) {
505  return 0;
506  }
507  int distance = distance_get_pixel ( wid->padding.top, ROFI_ORIENTATION_VERTICAL );
510  return distance;
511 }
513 {
514  if ( wid == NULL ) {
515  return 0;
516  }
520  return distance;
521 }
522 
524 {
525  int width = wid->w;
526  width -= widget_padding_get_left ( wid );
527  width -= widget_padding_get_right ( wid );
528  return width;
529 }
531 {
532  int height = wid->h;
533  height -= widget_padding_get_top ( wid );
534  height -= widget_padding_get_bottom ( wid );
535  return height;
536 }
538 {
539  int height = 0;
540  height += widget_padding_get_top ( wid );
541  height += widget_padding_get_bottom ( wid );
542  return height;
543 }
545 {
546  int width = 0;
547  width += widget_padding_get_left ( wid );
548  width += widget_padding_get_right ( wid );
549  return width;
550 }
551 
553 {
554  if ( wid == NULL ) {
555  return 0;
556  }
557  if ( wid->get_desired_height == NULL ) {
558  return wid->h;
559  }
560  return wid->get_desired_height ( wid );
561 }
563 {
564  if ( wid == NULL ) {
565  return 0;
566  }
567  if ( wid->get_desired_width == NULL ) {
568  return wid->w;
569  }
570  return wid->get_desired_width ( wid );
571 }
572 
574 {
575  if ( wid == NULL ) {
576  return 0;
577  }
578  int retv = wid->x;
579  if ( wid->parent != NULL ) {
580  retv += widget_get_absolute_xpos ( wid->parent );
581  }
582  return retv;
583 }
585 {
586  if ( wid == NULL ) {
587  return 0;
588  }
589  int retv = wid->y;
590  if ( wid->parent != NULL ) {
591  retv += widget_get_absolute_ypos ( wid->parent );
592  }
593  return retv;
594 }
WidgetType widget_type(widget *widget)
Definition: widget.c:156
WidgetType
Definition: widget.h:56
int widget_padding_get_bottom(const widget *wid)
Definition: widget.c:512
gboolean widget_need_redraw(widget *wid)
Definition: widget.c:422
widget_find_mouse_target_cb find_mouse_target
void * trigger_action_cb_data
void rofi_theme_get_color(const widget *widget, const char *property, cairo_t *d)
Definition: theme.c:861
RofiDistance bottom
Definition: rofi-types.h:165
RofiPadding def_margin
int(* get_width)(struct _widget *)
void widget_update(widget *widget)
Definition: widget.c:397
void widget_set_type(widget *widget, WidgetType type)
Definition: widget.c:148
int widget_padding_get_padding_width(const widget *wid)
Definition: widget.c:544
void(* resize)(struct _widget *, short, short)
int widget_padding_get_top(const widget *wid)
Definition: widget.c:502
WidgetTriggerActionResult(* widget_trigger_action_cb)(widget *widget, guint action, gint x, gint y, void *user_data)
Definition: widget.h:112
void widget_set_state(widget *widget, const char *state)
Definition: widget.c:93
struct _widget * parent
int widget_get_width(widget *widget)
Definition: widget.c:362
void widget_free(widget *wid)
Definition: widget.c:339
RofiPadding margin
int(* get_height)(struct _widget *)
RofiPadding rofi_theme_get_padding(const widget *widget, const char *property, RofiPadding pad)
Definition: theme.c:883
RofiPadding def_border_radius
int rofi_theme_get_boolean(const widget *widget, const char *property, int def)
Definition: theme.c:785
void widget_set_trigger_action_handler(widget *wid, widget_trigger_action_cb cb, void *cb_data)
Definition: widget.c:462
void(* draw)(struct _widget *widget, cairo_t *draw)
int(* get_desired_width)(struct _widget *)
void widget_draw(widget *widget, cairo_t *d)
Definition: widget.c:185
int widget_intersect(const widget *widget, int x, int y)
Definition: widget.c:110
RofiPadding def_border
char * name
gboolean need_redraw
gboolean enabled
void widget_move(widget *widget, short x, short y)
Definition: widget.c:140
void(* set_state)(struct _widget *, const char *)
void widget_xy_to_relative(widget *widget, gint *x, gint *y)
Definition: widget.c:387
void widget_resize(widget *widget, short w, short h)
Definition: widget.c:123
widget_trigger_action_cb trigger_action
WidgetTriggerActionResult widget_trigger_action(widget *wid, guint action, gint x, gint y)
Definition: widget.c:451
void widget_queue_redraw(widget *wid)
Definition: widget.c:408
RofiDistance right
Definition: rofi-types.h:164
gboolean widget_enabled(widget *widget)
Definition: widget.c:164
int widget_padding_get_remaining_height(const widget *wid)
Definition: widget.c:530
static void draw_rounded_rect(cairo_t *d, double x1, double y1, double x2, double y2, double r0, double r1, double r2, double r3)
Definition: widget.c:39
void(* free)(struct _widget *widget)
RofiPadding border_radius
int distance_get_pixel(RofiDistance d, RofiOrientation ori)
Definition: theme.c:1026
int widget_get_desired_width(widget *wid)
Definition: widget.c:562
WidgetTriggerActionResult
Definition: widget.h:77
int widget_padding_get_right(const widget *wid)
Definition: widget.c:492
#define WIDGET_PADDING_INIT
Definition: widget.c:36
int widget_get_x_pos(widget *widget)
Definition: widget.c:372
int widget_padding_get_remaining_width(const widget *wid)
Definition: widget.c:523
void widget_init(widget *wid, widget *parent, WidgetType type, const char *name)
Definition: widget.c:74
const char * state
int widget_get_absolute_xpos(widget *wid)
Definition: widget.c:573
gboolean widget_motion_notify(widget *wid, gint x, gint y)
Definition: widget.c:471
int widget_get_absolute_ypos(widget *wid)
Definition: widget.c:584
int(* get_desired_height)(struct _widget *)
int widget_get_desired_height(widget *wid)
Definition: widget.c:552
widget * widget_find_mouse_target(widget *wid, WidgetType type, gint x, gint y)
Definition: widget.c:433
int widget_padding_get_padding_height(const widget *wid)
Definition: widget.c:537
void(* update)(struct _widget *)
WidgetType type
RofiDistance left
Definition: rofi-types.h:166
RofiPadding padding
RofiPadding border
void widget_set_enabled(widget *widget, gboolean enabled)
Definition: widget.c:172
int widget_get_y_pos(widget *widget)
Definition: widget.c:379
RofiPadding def_padding
int widget_padding_get_left(const widget *wid)
Definition: widget.c:482
RofiDistance top
Definition: rofi-types.h:163
gboolean(* motion_notify)(struct _widget *, gint x, gint y)
int widget_get_height(widget *widget)
Definition: widget.c:352