3479b60a43444f877f878aac476eab59912d04bc
[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
43 #include "BKE_context.h"
44 #include "BKE_screen.h"
45 #include "BKE_main.h"
46
47 #include "WM_api.h"
48 #include "WM_types.h"
49
50 #include "RNA_access.h"
51
52 #include "BIF_gl.h"
53
54 #include "UI_interface.h"
55 #include "UI_view2d.h"
56
57 #include "BLT_translation.h"
58
59 #include "ED_screen.h"
60 #include "ED_undo.h"
61
62 #include "interface_intern.h"
63 #include "GPU_framebuffer.h"
64
65
66 /* -------------------------------------------------------------------- */
67 /** \name Utilities
68  * \{ */
69
70 static bool last_redo_poll(const bContext *C)
71 {
72         wmOperator *op = WM_operator_last_redo(C);
73         if (op == NULL) {
74                 return false;
75         }
76         bool success = false;
77         if (WM_operator_repeat_check(C, op) &&
78             WM_operator_check_ui_empty(op->type) == false)
79         {
80                 success = WM_operator_poll((bContext *)C, op->type);
81         }
82         return success;
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         BLI_addtail(&art->paneltypes, pt);
129 }
130
131 /** \} */
132
133 /* -------------------------------------------------------------------- */
134 /** \name Callbacks for Floating Region
135  * \{ */
136
137 struct HudRegionData {
138         short regionid;
139 };
140
141 static void hud_region_init(wmWindowManager *wm, ARegion *ar)
142 {
143         ED_region_panels_init(wm, ar);
144         UI_region_handlers_add(&ar->handlers);
145         ar->flag |= RGN_FLAG_TEMP_REGIONDATA;
146 }
147
148 static void hud_region_free(ARegion *ar)
149 {
150         MEM_SAFE_FREE(ar->regiondata);
151 }
152
153 static void hud_region_layout(const bContext *C, ARegion *ar)
154 {
155         bool ok = false;
156
157         {
158                 struct HudRegionData *hrd = ar->regiondata;
159                 if (hrd != NULL) {
160                         ScrArea *sa = CTX_wm_area(C);
161                         ARegion *ar_op = (hrd->regionid != -1) ? BKE_area_find_region_type(sa, hrd->regionid) : NULL;
162                         ARegion *ar_prev = CTX_wm_region(C);
163                         CTX_wm_region_set((bContext *)C, ar_op);
164                         ok = last_redo_poll(C);
165                         CTX_wm_region_set((bContext *)C, ar_prev);
166                 }
167         }
168
169         if (!ok) {
170                 ED_region_tag_redraw(ar);
171                 ar->flag |= RGN_FLAG_HIDDEN;
172                 return;
173         }
174
175         int size_y = ar->sizey;
176
177         ED_region_panels_layout(C, ar);
178
179         if (ar->panels.first && (ar->sizey != size_y)) {
180                 View2D *v2d = &ar->v2d;
181                 ar->winx = ar->sizex;
182                 ar->winy = ar->sizey;
183
184                 ar->winrct.xmax = (ar->winrct.xmin + ar->winx) - 1;
185                 ar->winrct.ymax = (ar->winrct.ymin + ar->winy) - 1;
186
187                 UI_view2d_region_reinit(v2d, V2D_COMMONVIEW_PANELS_UI, ar->winx, ar->winy);
188
189                 /* Weak, but needed to avoid glitches, especially with hi-dpi (where resizing the view glitches often).
190                  * Fortunately this only happens occasionally. */
191                 ED_region_panels_layout(C, ar);
192         }
193
194         /* restore view matrix */
195         UI_view2d_view_restore(C);
196 }
197
198 static void hud_region_draw(const bContext *C, ARegion *ar)
199 {
200         UI_view2d_view_ortho(&ar->v2d);
201         wmOrtho2_region_pixelspace(ar);
202         GPU_clear_color(0, 0, 0, 0.0f);
203         GPU_clear(GPU_COLOR_BIT);
204
205         if ((ar->flag & RGN_FLAG_HIDDEN) == 0) {
206                 float color[4];
207                 UI_GetThemeColor4fv(TH_BUTBACK, color);
208                 if ((U.uiflag2 & USER_REGION_OVERLAP) == 0) {
209                         color[3] = 1.0f;
210                 }
211                 ui_draw_widget_back_color(UI_WTYPE_BOX, false, &(rcti){.xmax = ar->winx, .ymax = ar->winy}, color);
212                 ED_region_panels_draw(C, ar);
213         }
214 }
215
216 ARegionType *ED_area_type_hud(int space_type)
217 {
218         ARegionType *art = MEM_callocN(sizeof(ARegionType), __func__);
219         art->regionid = RGN_TYPE_HUD;
220         art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D;
221         art->layout = hud_region_layout;
222         art->draw = hud_region_draw;
223         art->init = hud_region_init;
224         art->free = hud_region_free;
225
226         hud_panels_register(art, space_type, art->regionid);
227
228         art->lock = 1;   /* can become flag, see BKE_spacedata_draw_locks */
229         return art;
230 }
231
232 static ARegion *hud_region_add(ScrArea *sa)
233 {
234         ARegion *ar = MEM_callocN(sizeof(ARegion), "area region");
235         ARegion *ar_win = BKE_area_find_region_type(sa, RGN_TYPE_WINDOW);
236         if (ar_win) {
237                 BLI_insertlinkbefore(&sa->regionbase, ar_win, ar);
238         }
239         else {
240                 BLI_addtail(&sa->regionbase, ar);
241         }
242         ar->regiontype = RGN_TYPE_HUD;
243         ar->alignment = RGN_ALIGN_FLOAT;
244         ar->overlap = true;
245         ar->flag |= RGN_FLAG_DYNAMIC_SIZE;
246
247         return ar;
248 }
249
250 void ED_area_type_hud_clear(wmWindowManager *wm, ScrArea *sa_keep)
251 {
252         for (wmWindow *win = wm->windows.first; win; win = win->next) {
253                 bScreen *screen = WM_window_get_active_screen(win);
254                 for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
255                         if (sa != sa_keep) {
256                                 for (ARegion *ar = sa->regionbase.first; ar; ar = ar->next) {
257                                         if (ar->regiontype == RGN_TYPE_HUD) {
258                                                 if ((ar->flag & RGN_FLAG_HIDDEN) == 0) {
259                                                         ar->flag |= RGN_FLAG_HIDDEN;
260                                                         ED_region_tag_redraw(ar);
261                                                         ED_area_tag_redraw(sa);
262                                                 }
263                                         }
264                                 }
265                         }
266                 }
267         }
268 }
269
270 void ED_area_type_hud_ensure(bContext *C, ScrArea *sa)
271 {
272         wmWindowManager *wm = CTX_wm_manager(C);
273         ED_area_type_hud_clear(wm, sa);
274
275         ARegionType *art = BKE_regiontype_from_id(sa->type, RGN_TYPE_HUD);
276         if (art == NULL) {
277                 return;
278         }
279
280         bool init = false;
281         ARegion *ar = BKE_area_find_region_type(sa, RGN_TYPE_HUD);
282         if (!last_redo_poll(C)) {
283                 if (ar) {
284                         ED_region_tag_redraw(ar);
285                         ar->flag |= RGN_FLAG_HIDDEN;
286                 }
287                 return;
288         }
289
290         if (ar == NULL) {
291                 init = true;
292                 ar = hud_region_add(sa);
293                 ar->type = art;
294         }
295
296         ED_region_init(ar);
297         ED_region_tag_redraw(ar);
298
299         /* Reset zoom level (not well supported). */
300         ar->v2d.cur = ar->v2d.tot = (rctf){.xmax = ar->winx, .ymax = ar->winy};
301         ar->v2d.minzoom = 1.0f;
302         ar->v2d.maxzoom = 1.0f;
303
304         /* Let 'ED_area_update_region_sizes' do the work of placing the region.
305          * Otherwise we could set the 'ar->winrct' & 'ar->winx/winy' here. */
306         if (init) {
307                 sa->flag |= AREA_FLAG_REGION_SIZE_UPDATE;
308         }
309         else {
310                 if (ar->flag & RGN_FLAG_HIDDEN) {
311                         sa->flag |= AREA_FLAG_REGION_SIZE_UPDATE;
312                 }
313                 ar->flag &= ~RGN_FLAG_HIDDEN;
314         }
315
316         {
317                 ARegion *ar_op = CTX_wm_region(C);
318                 BLI_assert((ar_op == NULL) || (ar_op->regiontype != RGN_TYPE_HUD));
319                 struct HudRegionData *hrd = ar->regiondata;
320                 if (hrd == NULL) {
321                         hrd = MEM_callocN(sizeof(*hrd), __func__);
322                         ar->regiondata = hrd;
323                 }
324                 if (ar_op) {
325                         hrd->regionid = ar_op->regiontype;
326                 }
327                 else {
328                         hrd->regionid = -1;
329                 }
330         }
331
332         /* XXX, should be handled in more general way. */
333         ar->visible = !((ar->flag & RGN_FLAG_HIDDEN) || (ar->flag & RGN_FLAG_TOO_SMALL));
334
335         /* We shouldn't need to do this every time :S */
336         /* XXX, this is evil! - it also makes the menu show on first draw. :( */
337         ARegion *ar_prev = CTX_wm_region(C);
338         CTX_wm_region_set((bContext *)C, ar);
339         hud_region_layout(C, ar);
340         CTX_wm_region_set((bContext *)C, ar_prev);
341 }
342
343 /** \} */