1 /**************************************************************************
2 * Copyright (C) 2008 thierry lorthiois (lorthiois@bbsoft.fr)
3 *
4 **************************************************************************/
5 
6 #ifndef AREA_H
7 #define AREA_H
8 
9 #include <glib.h>
10 #include <X11/Xlib.h>
11 #include <cairo.h>
12 #include <cairo-xlib.h>
13 
14 #include "color.h"
15 #include "gradient.h"
16 
17 // DATA ORGANISATION
18 //
19 // Areas in tint2 are similar to widgets in a GUI.
20 // All graphical objects (panel, taskbar, task, systray, clock, ...) inherit the abstract class Area.
21 // This class 'Area' stores data about the background, border, size, position, padding and the child areas.
22 // Inheritance is simulated by having an Area member as the first member of each object (thus &object == &area).
23 //
24 // tint2 uses multiple panels, one per monitor. Each panel has an area containing the children objects in a tree of
25 // areas. The level in the tree gives the z-order: child areas are always displayed on top of their parents.
26 //
27 //
28 // LAYOUT
29 //
30 // Sibling areas never overlap.
31 //
32 // The position of an Area (posx, posy) is relative to the window (i.e. absolute) and
33 // is computed based on a simple box model:
34 // * parent position + parent padding + sum of the sizes of the previous siblings and spacing
35 //
36 // The size of an Area is:
37 // * SIZE_BY_CONTENT objects:
38 //   * fixed and set by the Area
39 //   * childred are resized before the parent
40 //   * if a child size has changed then the parent is resized
41 // * SIZE_BY_LAYOUT objects:
42 //   * expandable and computed as the total size of the parent - padding -
43 //     the size of the fixed sized siblings - spacing and divided by the number of expandable siblings
44 //   * the parent is resized before the children
45 //
46 //
47 // RENDERING
48 //
49 // Redrawing an object (like the clock) could come from an 'external event' (date change)
50 // or from a 'layout event' (position change).
51 //
52 //
53 // WIDGET LIFECYCLE
54 //
55 // Each widget that occurs once per panel is defined as a struct (e.g. Clock) which is stored as a member of Panel.
56 // Widgets that can occur more than once should be stored as an array, still as a member of Panel.
57 //
58 // There is a special Panel instance called 'panel_config' which stores the config options and the state variables
59 // of the widgets (however some config options are stored as global variables by the widgets).
60 //
61 // Tint2 maintains an array of Panel instances, one for each monitor. These contain the actual Areas that are used to
62 // render the panels on screen, interact with user input etc.
63 // Each Panel is initialized as a raw copy (memcpy, see init_panel()) of panel_config.
64 //
65 // Normally, widgets should implement the following functions:
66 //
67 // * void default_widget();
68 //
69 //   Called before the config is read and panel_config/panels are created.
70 //   Afterwards, the config parsing code creates the widget/widget array in panel_config and
71 //   populates the configuration fields.
72 //   If the widget uses global variables to store config options or other state variables, they should be initialized
73 //   here (e.g. with zero, NULL etc).
74 //
75 // * void init_widget();
76 //
77 //   Called after the config is read and panel_config is populated, but before panels are created.
78 //   Initializes the state of the widget in panel_config.
79 //   If the widget uses global variables to store config options or other state variables which depend on the config
80 //   options but not on the panel instance, they should be initialized here.
81 //   panel_config.panel_items can be used to determine which backend items are enabled.
82 //
83 // * void init_widget_panel(void *panel);
84 //
85 //   Called after each on-screen panel is created, with a pointer to the panel.
86 //   Completes the initialization of the widget.
87 //   At this point the widget Area has not been added yet to the GUI tree, but it will be added right afterwards.
88 //
89 // * void cleanup_widget();
90 //
91 //   Called just before the panels are destroyed. Afterwards, tint2 exits or restarts and reads the config again.
92 //   Must releases all resources.
93 //   The widget itself should not be freed by this function, only its members or global variables that were set.
94 //   The widget is freed by the Area tree cleanup function (remove_area).
95 //
96 // * void draw_widget(void *obj, cairo_t *c);
97 //
98 //   Called on draw, obj = pointer to the widget instance from the panel that is redrawn.
99 //   The Area's _draw_foreground member must point to this function.
100 //
101 // * int resize_widget(void *obj);
102 //
103 //   Called on resize, obj = pointer to the front-end Execp item.
104 //   Returns 1 if the new size is different than the previous size.
105 //   The Area's _resize member must point to this function.
106 //
107 // * void widget_action(void *obj, int button);
108 //
109 //   Called on mouse click event.
110 //
111 // * void widget_on_change_layout(void *obj);
112 //
113 //   Implemented only to override the default layout algorithm for this widget.
114 //   For example, if this widget is a cell in a table, its position and size should be computed here.
115 //   The Area's _on_change_layout member must point to this function.
116 //
117 // * char* widget_get_tooltip_text(void *obj);
118 //
119 //   Returns a copy of the tooltip to be displayed for this widget.
120 //   The caller takes ownership of the pointer.
121 //   The Area's _get_tooltip_text member must point to this function.
122 
123 typedef enum BorderMask {
124     BORDER_TOP = 1 << 0,
125     BORDER_BOTTOM = 1 << 1,
126     BORDER_LEFT = 1 << 2,
127     BORDER_RIGHT = 1 << 3
128 } BorderMask;
129 
130 #define BORDER_ALL (BORDER_TOP | BORDER_BOTTOM | BORDER_LEFT | BORDER_RIGHT)
131 
132 typedef struct Border {
133     // It's essential that the first member is color
134     Color color;
135     // Width in pixels
136     int width;
137     // Corner radius
138     int radius;
139     // Mask: bitwise OR of BorderMask
140     int mask;
141 } Border;
142 
143 typedef enum MouseState { MOUSE_NORMAL = 0, MOUSE_OVER = 1, MOUSE_DOWN = 2, MOUSE_STATE_COUNT } MouseState;
144 
145 typedef struct Background {
146     // Normal state
147     Color fill_color;
148     Border border;
149     // On mouse hover
150     Color fill_color_hover;
151     Color border_color_hover;
152     // On mouse press
153     Color fill_color_pressed;
154     Color border_color_pressed;
155     // Pointer to a GradientClass or NULL, no ownership
156     GradientClass *gradients[MOUSE_STATE_COUNT];
157     double fill_content_tint_weight;
158     double border_content_tint_weight;
159 } Background;
160 
161 typedef enum Layout {
162     LAYOUT_DYNAMIC,
163     LAYOUT_FIXED,
164 } Layout;
165 
166 typedef enum Alignment {
167     ALIGN_LEFT = 0,
168     ALIGN_CENTER = 1,
169     ALIGN_RIGHT = 2,
170 } Alignment;
171 
172 struct Panel;
173 
174 typedef struct Area {
175     // Position relative to the panel window
176     int posx, posy;
177     // Size, including borders
178     int width, height;
179     int old_width, old_height;
180     Background *bg;
181     // Each element is a GradientInstance attached to this Area (list can be empty)
182     GList *gradient_instances_by_state[MOUSE_STATE_COUNT];
183     // Each element is a GradientInstance that depends on this Area's geometry (position or size)
184     GList *dependent_gradients;
185     // List of children, each one a pointer to Area
186     GList *children;
187     // Pointer to the parent Area or NULL
188     void *parent;
189     // Pointer to the Panel that contains this Area
190     void *panel;
191     Layout size_mode;
192     Alignment alignment;
193     gboolean has_mouse_over_effect;
194     gboolean has_mouse_press_effect;
195     // TODO padding/spacing is a clusterfuck
196     // paddingxlr = padding
197     // paddingy = vertical padding, sometimes
198     // paddingx = spacing
199     int paddingxlr, paddingx, paddingy;
200     MouseState mouse_state;
201     // Set to non-zero if the Area is visible. An object may exist but stay hidden.
202     gboolean on_screen;
203     // Set to non-zero if the size of the Area has to be recalculated.
204     gboolean resize_needed;
205     // Set to non-zero if the Area has to be redrawn.
206     // Do not set this directly; use schedule_redraw() instead.
207     gboolean _redraw_needed;
208     // Set to non-zero if the position/size has changed, thus _on_change_layout needs to be called
209     gboolean _changed;
210     // This is the pixmap on which the Area is rendered. Render to it directly if needed.
211     Pixmap pix;
212     Pixmap pix_by_state[MOUSE_STATE_COUNT];
213     char name[32];
214 
215     // Callbacks
216 
217     // Called on draw before any drawing takes place, obj = pointer to the Area
218     void (*_clear)(void *obj);
219 
220     // Called on draw, obj = pointer to the Area
221     void (*_draw_foreground)(void *obj, cairo_t *c);
222 
223     // Called on resize, obj = pointer to the Area
224     // Returns 1 if the new size is different than the previous size.
225     gboolean (*_resize)(void *obj);
226 
227     // Called before resize, obj = pointer to the Area
228     // Returns the desired size of the Area
229     int (*_compute_desired_size)(void *obj);
230 
231     // Implemented only to override the default layout algorithm for this widget.
232     // For example, if this widget is a cell in a table, its position and size should be computed here.
233     void (*_on_change_layout)(void *obj);
234 
235     // Returns a copy of the tooltip to be displayed for this widget.
236     // The caller takes ownership of the pointer.
237     char *(*_get_tooltip_text)(void *obj);
238     cairo_surface_t *(*_get_tooltip_image)(void *obj);
239 
240     // Returns true if the Area handles a mouse event at the given x, y coordinates relative to the window.
241     // Leave this to NULL to use a default implementation.
242     gboolean (*_is_under_mouse)(void *obj, int x, int y);
243 
244     // Prints the geometry of the object on stderr, with left indentation of indent spaces.
245     void (*_dump_geometry)(void *obj, int indent);
246 
247     void (*_get_content_color)(void *obj, Color *color);
248 } Area;
249 
250 // Initializes the Background member to default values.
251 void init_background(Background *bg);
252 
253 // Layout
254 
255 // Called on startup to initialize the positions of all Areas in the Area tree.
256 // Parameters:
257 // * obj: pointer to Area
258 // * offset: offset in pixels from left/top, relative to the window
259 void initialize_positions(void *obj, int offset);
260 
261 // Relayouts the Area and its children. Normally called on the root of the tree (i.e. the Panel).
262 void relayout(Area *a);
263 
264 // Distributes the Area's size to its children, repositioning them as needed.
265 // If maximum_size > 0, it is an upper limit for the child size.
266 int relayout_with_constraint(Area *a, int maximum_size);
267 
268 int compute_desired_size(Area *a);
269 int container_compute_desired_size(Area *a);
270 
271 void area_compute_text_geometry(Area *area,
272                                 const char *line1,
273                                 const char *line2,
274                                 PangoFontDescription *line1_font_desc,
275                                 PangoFontDescription *line2_font_desc,
276                                 int *line1_height,
277                                 int *line1_width,
278                                 int *line2_height,
279                                 int *line2_width);
280 int text_area_compute_desired_size(Area *area,
281                                    const char *line1,
282                                    const char *line2,
283                                    PangoFontDescription *line1_font_desc,
284                                    PangoFontDescription *line2_font_desc);
285 gboolean resize_text_area(Area *area,
286                           const char *line1,
287                           const char *line2,
288                           PangoFontDescription *line1_font_desc,
289                           PangoFontDescription *line2_font_desc,
290                           int *line1_posy,
291                           int *line2_posy);
292 void draw_text_area(Area *area,
293                     cairo_t *c,
294                     const char *line1,
295                     const char *line2,
296                     PangoFontDescription *line1_font_desc,
297                     PangoFontDescription *line2_font_desc,
298                     int line1_posy,
299                     int line2_posy,
300                     Color *color,
301                     double scale);
302 
303 int left_border_width(Area *a);
304 int right_border_width(Area *a);
305 int left_right_border_width(Area *a);
306 int top_border_width(Area *a);
307 int bottom_border_width(Area *a);
308 int top_bottom_border_width(Area *a);
309 
310 int left_bg_border_width(Background *bg);
311 int right_bg_border_width(Background *bg);
312 int top_bg_border_width(Background *bg);
313 int bottom_bg_border_width(Background *bg);
314 int left_right_bg_border_width(Background *bg);
315 int top_bottom_bg_border_width(Background *bg);
316 
317 // Rendering
318 
319 // Sets the redraw_needed flag on the area and its descendants
320 void schedule_redraw(Area *a);
321 
322 // Recreates the Area pixmap and draws the background and the foreground
323 void draw(Area *a);
324 
325 // Draws the background of the Area
326 void draw_background(Area *a, cairo_t *c);
327 
328 // Explores the entire Area subtree (only if the on_screen flag set)
329 // and draws the areas with the redraw_needed flag set
330 void draw_tree(Area *a);
331 
332 // Clears the on_screen flag, sets the size to zero and triggers a parent resize
333 void hide(Area *a);
334 
335 // Sets the on_screen flag and triggers a parent and area resize
336 void show(Area *a);
337 
338 // Area tree
339 
340 void add_area(Area *a, Area *parent);
341 void remove_area(Area *a);
342 void free_area(Area *a);
343 
344 // Mouse events
345 
346 // Returns the area under the mouse for the given x, y mouse coordinates relative to the window.
347 // If no area is found, returns the root.
348 Area *find_area_under_mouse(void *root, int x, int y);
349 
350 // Returns true if the Area handles a mouse event at the given x, y coordinates relative to the window.
351 gboolean area_is_under_mouse(void *obj, int x, int y);
352 
353 // Returns true if the Area handles a mouse event at the given x, y coordinates relative to the window.
354 // The Area will also handle clicks on the border of its ancestors, including the panel.
355 // Useful so that a click at the edge of the screen is still handled by task buttons etc., even if technically
356 // they are outside the drawing area of the button.
357 gboolean full_width_area_is_under_mouse(void *obj, int x, int y);
358 
359 void instantiate_area_gradients(Area *area);
360 void free_area_gradient_instances(Area *area);
361 
362 void area_dump_geometry(Area *area, int indent);
363 
364 void mouse_over(Area *area, gboolean pressed);
365 void mouse_out();
366 
367 void update_gradient(GradientInstance *gi);
368 void update_dependent_gradients(Area *a);
369 
370 gboolean area_is_first(void *obj);
371 gboolean area_is_last(void *obj);
372 
373 #endif
374