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