Cleanup: add trailing commas to structs
[blender.git] / source / blender / editors / interface / interface_region_hud.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2008 Blender Foundation.
19  * All rights reserved.
20  *
21  * Contributor(s): Blender Foundation
22  *
23  * ***** END GPL LICENSE BLOCK *****
24  */
25
26 /** \file blender/editors/interface/interface_region_hud.c
27  *  \ingroup edinterface
28  *
29  * Floating Persistent Region
30  */
31
32 #include <string.h>
33
34 #include "MEM_guardedalloc.h"
35
36 #include "DNA_userdef_types.h"
37
38 #include "BLI_string.h"
39 #include "BLI_rect.h"
40 #include "BLI_listbase.h"
41 #include "BLI_utildefines.h"
42 #include "BLI_math_color.h"
43
44 #include "BKE_context.h"
45 #include "BKE_screen.h"
46 #include "BKE_main.h"
47
48 #include "WM_api.h"
49 #include "WM_types.h"
50
51 #include "RNA_access.h"
52
53 #include "BIF_gl.h"
54
55 #include "UI_interface.h"
56 #include "UI_view2d.h"
57
58 #include "BLT_translation.h"
59
60 #include "ED_screen.h"
61 #include "ED_undo.h"
62
63 #include "interface_intern.h"
64 #include "GPU_framebuffer.h"
65
66
67 /* -------------------------------------------------------------------- */
68 /** \name Utilities
69  * \{ */
70
71 static bool last_redo_poll(const bContext *C)
72 {
73         wmOperator *op = WM_operator_last_redo(C);
74         if (op == NULL) {
75                 return false;
76         }
77         bool success = false;
78         if (WM_operator_repeat_check(C, op) &&
79             WM_operator_check_ui_empty(op->type) == false)
80         {
81                 success = WM_operator_poll((bContext *)C, op->type);
82         }
83         return success;
84 }
85
86 static void hud_region_hide(ARegion *ar)
87 {
88         ar->flag |= RGN_FLAG_HIDDEN;
89         /* Avoids setting 'AREA_FLAG_REGION_SIZE_UPDATE'
90          * since other regions don't depend on this. */
91         BLI_rcti_init(&ar->winrct, 0, 0, 0, 0);
92 }
93
94 /** \} */
95
96 /* -------------------------------------------------------------------- */
97 /** \name Redo Panel
98  * \{ */
99
100 static bool hud_panel_operator_redo_poll(const bContext *C, PanelType *UNUSED(pt))
101 {
102         return last_redo_poll(C);
103 }
104
105 static void hud_panel_operator_redo_draw_header(const bContext *C, Panel *pa)
106 {
107         wmOperator *op = WM_operator_last_redo(C);
108         BLI_strncpy(pa->drawname, RNA_struct_ui_name(op->type->srna), sizeof(pa->drawname));
109 }
110
111 static void hud_panel_operator_redo_draw(const bContext *C, Panel *pa)
112 {
113         wmOperator *op = WM_operator_last_redo(C);
114         if (op == NULL) {
115                 return;
116         }
117         if (!WM_operator_check_ui_enabled(C, op->type->name)) {
118                 uiLayoutSetEnabled(pa->layout, false);
119         }
120         uiLayout *col = uiLayoutColumn(pa->layout, false);
121         uiTemplateOperatorRedoProperties(col, C);
122 }
123
124 static void hud_panels_register(ARegionType *art, int space_type, int region_type)
125 {
126         PanelType *pt;
127
128         pt = MEM_callocN(sizeof(PanelType), __func__);
129         strcpy(pt->idname, "OPERATOR_PT_redo");
130         strcpy(pt->label, N_("Redo"));
131         strcpy(pt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA);
132         pt->draw_header = hud_panel_operator_redo_draw_header;
133         pt->draw = hud_panel_operator_redo_draw;
134         pt->poll = hud_panel_operator_redo_poll;
135         pt->space_type = space_type;
136         pt->region_type = region_type;
137         pt->flag |= PNL_DEFAULT_CLOSED;
138         BLI_addtail(&art->paneltypes, pt);
139 }
140
141 /** \} */
142
143 /* -------------------------------------------------------------------- */
144 /** \name Callbacks for Floating Region
145  * \{ */
146
147 struct HudRegionData {
148         short regionid;
149 };
150
151 static void hud_region_init(wmWindowManager *wm, ARegion *ar)
152 {
153         ED_region_panels_init(wm, ar);
154         UI_region_handlers_add(&ar->handlers);
155         ar->flag |= RGN_FLAG_TEMP_REGIONDATA;
156 }
157
158 static void hud_region_free(ARegion *ar)
159 {
160         MEM_SAFE_FREE(ar->regiondata);
161 }
162
163 static void hud_region_layout(const bContext *C, ARegion *ar)
164 {
165         bool ok = false;
166
167         {
168                 struct HudRegionData *hrd = ar->regiondata;
169                 if (hrd != NULL) {
170                         ScrArea *sa = CTX_wm_area(C);
171                         ARegion *ar_op = (hrd->regionid != -1) ? BKE_area_find_region_type(sa, hrd->regionid) : NULL;
172                         ARegion *ar_prev = CTX_wm_region(C);
173                         CTX_wm_region_set((bContext *)C, ar_op);
174                         ok = last_redo_poll(C);
175                         CTX_wm_region_set((bContext *)C, ar_prev);
176                 }
177         }
178
179         if (!ok) {
180                 ED_region_tag_redraw(ar);
181                 hud_region_hide(ar);
182                 return;
183         }
184
185         int size_y = ar->sizey;
186
187         ED_region_panels_layout(C, ar);
188
189         if (ar->panels.first && (ar->sizey != size_y)) {
190                 View2D *v2d = &ar->v2d;
191                 ar->winx = ar->sizex * UI_DPI_FAC;
192                 ar->winy = ar->sizey * UI_DPI_FAC;
193
194                 ar->winrct.xmax = (ar->winrct.xmin + ar->winx) - 1;
195                 ar->winrct.ymax = (ar->winrct.ymin + ar->winy) - 1;
196
197                 UI_view2d_region_reinit(v2d, V2D_COMMONVIEW_PANELS_UI, ar->winx, ar->winy);
198
199                 /* Weak, but needed to avoid glitches, especially with hi-dpi (where resizing the view glitches often).
200                  * Fortunately this only happens occasionally. */
201                 ED_region_panels_layout(C, ar);
202         }
203
204         /* restore view matrix */
205         UI_view2d_view_restore(C);
206 }
207
208 static void hud_region_draw(const bContext *C, ARegion *ar)
209 {
210         UI_view2d_view_ortho(&ar->v2d);
211         wmOrtho2_region_pixelspace(ar);
212         GPU_clear_color(0, 0, 0, 0.0f);
213         GPU_clear(GPU_COLOR_BIT);
214
215         if ((ar->flag & RGN_FLAG_HIDDEN) == 0) {
216                 ui_draw_menu_back(NULL, NULL, &(rcti){ .xmax = ar->winx, .ymax = ar->winy, });
217                 ED_region_panels_draw(C, ar);
218         }
219 }
220
221 ARegionType *ED_area_type_hud(int space_type)
222 {
223         ARegionType *art = MEM_callocN(sizeof(ARegionType), __func__);
224         art->regionid = RGN_TYPE_HUD;
225         art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D;
226         art->layout = hud_region_layout;
227         art->draw = hud_region_draw;
228         art->init = hud_region_init;
229         art->free = hud_region_free;
230
231         hud_panels_register(art, space_type, art->regionid);
232
233         art->lock = 1;   /* can become flag, see BKE_spacedata_draw_locks */
234         return art;
235 }
236
237 static ARegion *hud_region_add(ScrArea *sa)
238 {
239         ARegion *ar = MEM_callocN(sizeof(ARegion), "area region");
240         ARegion *ar_win = BKE_area_find_region_type(sa, RGN_TYPE_WINDOW);
241         if (ar_win) {
242                 BLI_insertlinkbefore(&sa->regionbase, ar_win, ar);
243         }
244         else {
245                 BLI_addtail(&sa->regionbase, ar);
246         }
247         ar->regiontype = RGN_TYPE_HUD;
248         ar->alignment = RGN_ALIGN_FLOAT;
249         ar->overlap = true;
250         ar->flag |= RGN_FLAG_DYNAMIC_SIZE;
251
252         return ar;
253 }
254
255 void ED_area_type_hud_clear(wmWindowManager *wm, ScrArea *sa_keep)
256 {
257         for (wmWindow *win = wm->windows.first; win; win = win->next) {
258                 bScreen *screen = WM_window_get_active_screen(win);
259                 for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
260                         if (sa != sa_keep) {
261                                 for (ARegion *ar = sa->regionbase.first; ar; ar = ar->next) {
262                                         if (ar->regiontype == RGN_TYPE_HUD) {
263                                                 if ((ar->flag & RGN_FLAG_HIDDEN) == 0) {
264                                                         hud_region_hide(ar);
265                                                         ED_region_tag_redraw(ar);
266                                                         ED_area_tag_redraw(sa);
267                                                 }
268                                         }
269                                 }
270                         }
271                 }
272         }
273 }
274
275 void ED_area_type_hud_ensure(bContext *C, ScrArea *sa)
276 {
277         wmWindowManager *wm = CTX_wm_manager(C);
278         ED_area_type_hud_clear(wm, sa);
279
280         ARegionType *art = BKE_regiontype_from_id(sa->type, RGN_TYPE_HUD);
281         if (art == NULL) {
282                 return;
283         }
284
285         ARegion *ar = BKE_area_find_region_type(sa, RGN_TYPE_HUD);
286         bool init = false;
287         bool was_hidden = ar == NULL || ar->visible == false;
288         if (!last_redo_poll(C)) {
289                 if (ar) {
290                         ED_region_tag_redraw(ar);
291                         hud_region_hide(ar);
292                 }
293                 return;
294         }
295
296         if (ar == NULL) {
297                 init = true;
298                 ar = hud_region_add(sa);
299                 ar->type = art;
300         }
301
302         /* Let 'ED_area_update_region_sizes' do the work of placing the region.
303          * Otherwise we could set the 'ar->winrct' & 'ar->winx/winy' here. */
304         if (init) {
305                 sa->flag |= AREA_FLAG_REGION_SIZE_UPDATE;
306         }
307         else {
308                 if (ar->flag & RGN_FLAG_HIDDEN) {
309                         sa->flag |= AREA_FLAG_REGION_SIZE_UPDATE;
310                 }
311                 ar->flag &= ~RGN_FLAG_HIDDEN;
312         }
313
314         {
315                 ARegion *ar_op = CTX_wm_region(C);
316                 BLI_assert((ar_op == NULL) || (ar_op->regiontype != RGN_TYPE_HUD));
317                 struct HudRegionData *hrd = ar->regiondata;
318                 if (hrd == NULL) {
319                         hrd = MEM_callocN(sizeof(*hrd), __func__);
320                         ar->regiondata = hrd;
321                 }
322                 if (ar_op) {
323                         hrd->regionid = ar_op->regiontype;
324                 }
325                 else {
326                         hrd->regionid = -1;
327                 }
328         }
329
330         if (init) {
331                 /* This is needed or 'winrct' will be invalid. */
332                 wmWindow *win = CTX_wm_window(C);
333                 ED_area_update_region_sizes(wm, win, sa);
334         }
335
336         ED_region_init(ar);
337         ED_region_tag_redraw(ar);
338
339         /* Reset zoom level (not well supported). */
340         ar->v2d.cur = ar->v2d.tot = (rctf){ .xmax = ar->winx, .ymax = ar->winy, };
341         ar->v2d.minzoom = 1.0f;
342         ar->v2d.maxzoom = 1.0f;
343
344         ar->visible = !(ar->flag & RGN_FLAG_HIDDEN);
345
346         /* We shouldn't need to do this every time :S */
347         /* XXX, this is evil! - it also makes the menu show on first draw. :( */
348         if (ar->visible) {
349                 ARegion *ar_prev = CTX_wm_region(C);
350                 CTX_wm_region_set((bContext *)C, ar);
351                 hud_region_layout(C, ar);
352                 if (was_hidden) {
353                         ar->winx = ar->v2d.winx;
354                         ar->winy = ar->v2d.winy;
355                         ar->v2d.cur = ar->v2d.tot = (rctf){ .xmax = ar->winx, .ymax = ar->winy, };
356                 }
357                 CTX_wm_region_set((bContext *)C, ar_prev);
358         }
359
360         ar->visible = !((ar->flag & RGN_FLAG_HIDDEN) || (ar->flag & RGN_FLAG_TOO_SMALL));
361 }
362
363 /** \} */