Blender V4.5
screen_ops.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2008 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <cmath>
10#include <cstring>
11#include <fmt/format.h>
12
13#include "MEM_guardedalloc.h"
14
15#include "BLI_build_config.h"
16#include "BLI_listbase.h"
17#include "BLI_math_rotation.h"
18#include "BLI_math_vector.h"
19#include "BLI_utildefines.h"
20
21#include "BLT_translation.hh"
22
23#include "DNA_anim_types.h"
24#include "DNA_armature_types.h"
25#include "DNA_curve_types.h"
26#include "DNA_lattice_types.h"
27#include "DNA_meta_types.h"
28#include "DNA_node_types.h"
29#include "DNA_object_types.h"
30#include "DNA_scene_types.h"
31#include "DNA_userdef_types.h"
32#include "DNA_workspace_types.h"
33
34#include "BKE_callbacks.hh"
35#include "BKE_context.hh"
36#include "BKE_editmesh.hh"
37#include "BKE_fcurve.hh"
38#include "BKE_global.hh"
39#include "BKE_icons.h"
40#include "BKE_lib_id.hh"
41#include "BKE_library.hh"
42#include "BKE_main.hh"
43#include "BKE_mask.h"
44#include "BKE_object.hh"
45#include "BKE_report.hh"
46#include "BKE_scene.hh"
47#include "BKE_screen.hh"
48#include "BKE_sound.h"
49#include "BKE_workspace.hh"
50
51#include "WM_api.hh"
52#include "WM_types.hh"
53
54#include "DEG_depsgraph.hh"
56
57#include "ED_anim_api.hh"
58#include "ED_armature.hh"
59#include "ED_buttons.hh"
60#include "ED_fileselect.hh"
61#include "ED_image.hh"
63#include "ED_mesh.hh"
64#include "ED_object.hh"
65#include "ED_scene.hh"
66#include "ED_screen.hh"
67#include "ED_screen_types.hh"
68#include "ED_sequencer.hh"
69#include "ED_space_graph.hh"
70#include "ED_view3d.hh"
71
72#include "RNA_access.hh"
73#include "RNA_define.hh"
74#include "RNA_enum_types.hh"
75#include "RNA_prototypes.hh"
76
77#include "UI_interface.hh"
78#include "UI_resources.hh"
79#include "UI_view2d.hh"
80
81#include "GPU_capabilities.hh"
82
83#include "wm_window.hh"
84
85#include "screen_intern.hh" /* own module include */
86
87using blender::Vector;
88
89#define KM_MODAL_CANCEL 1
90#define KM_MODAL_APPLY 2
91#define KM_MODAL_SNAP_ON 3
92#define KM_MODAL_SNAP_OFF 4
93
94/* -------------------------------------------------------------------- */
97
99{
100 if (CTX_wm_window(C) == nullptr) {
101 return false;
102 }
103 if (CTX_wm_screen(C) == nullptr) {
104 return false;
105 }
106 if (CTX_wm_region(C) == nullptr) {
107 return false;
108 }
109 return true;
110}
111
113{
114 if (CTX_wm_window(C) == nullptr) {
115 return false;
116 }
117 if (CTX_wm_screen(C) == nullptr) {
118 return false;
119 }
120 if (CTX_wm_area(C) == nullptr) {
121 return false;
122 }
123 return true;
124}
125
127{
128 if (CTX_wm_window(C) == nullptr) {
129 return false;
130 }
131 if (CTX_wm_screen(C) == nullptr) {
132 return false;
133 }
134 return true;
135}
136
138{
139 if (G.background) {
140 return false;
141 }
143}
144
145/* XXX added this to prevent anim state to change during renders */
147{
148 if (G.is_rendering) {
149 return false;
150 }
151 if (CTX_wm_window(C) == nullptr) {
152 return false;
153 }
154 if (CTX_wm_screen(C) == nullptr) {
155 return false;
156 }
157 return true;
158}
159
161{
162 if (CTX_wm_window(C) == nullptr) {
163 return false;
164 }
165 bScreen *screen = CTX_wm_screen(C);
166 if (screen == nullptr) {
167 return false;
168 }
169 if (screen->active_region != nullptr) {
170 return false;
171 }
172 return true;
173}
174
176{
177 Scene *scene = CTX_data_scene(C);
178 if (scene) {
179 return true;
180 }
181 return false;
182}
183
185{
186 Scene *scene = CTX_data_scene(C);
187 if (scene == nullptr || !BKE_id_is_editable(CTX_data_main(C), &scene->id)) {
188 return false;
189 }
190 return true;
191}
192
194{
195 Scene *scene = CTX_data_scene(C);
197
198 if (scene == nullptr || !ID_IS_EDITABLE(scene)) {
199 return false;
200 }
201 if (CTX_data_edit_object(C)) {
202 return false;
203 }
204
205 /* add a check for ob->mode too? */
206 if (obact && (obact->mode != OB_MODE_OBJECT)) {
207 return false;
208 }
209
210 return true;
211}
212
214{
216 CTX_wm_operator_poll_msg_set(C, "Only supported in object mode");
217 return false;
218 }
219
220 return true;
221}
222
224{
226 return false;
227 }
229 return false;
230 }
231 return true;
232}
233
234static bool ed_spacetype_test(bContext *C, int type)
235{
238 return sl && (sl->spacetype == type);
239 }
240 return false;
241}
242
247
249{
250 if (CTX_wm_region_view3d(C)) {
251 return true;
252 }
253
254 CTX_wm_operator_poll_msg_set(C, "expected a view3d region");
255 return false;
256}
257
259{
260 ARegion *region = CTX_wm_region(C);
261 if (region == nullptr) {
262 return false;
263 }
264 wmGizmoMap *gzmap = region->runtime->gizmo_map;
265 if (gzmap == nullptr) {
266 return false;
267 }
268 return true;
269}
270
272{
276 return true;
277 }
278 }
279
280 CTX_wm_operator_poll_msg_set(C, "expected a timeline/animation area to be active");
281 return false;
282}
283
288
290{
292 CTX_wm_operator_poll_msg_set(C, "Expected an active Outliner");
293 return false;
294 }
295 const ARegion *region = CTX_wm_region(C);
296 if (!(region && region->regiontype == RGN_TYPE_WINDOW)) {
297 CTX_wm_operator_poll_msg_set(C, "Expected an Outliner region");
298 return false;
299 }
300 return true;
301}
302
304{
307 Object *obedit = CTX_data_edit_object(C);
308 if (ob && ob == obedit) {
309 return false;
310 }
311 return true;
312 }
313 return false;
314}
315
320
328
336
341
346
351
353{
354 SpaceNode *snode = CTX_wm_space_node(C);
355
356 if (snode && snode->edittree) {
357 return true;
358 }
359
360 return false;
361}
362
364{
365 SpaceNode *snode = CTX_wm_space_node(C);
366
367 if (snode && snode->edittree && BKE_id_is_editable(CTX_data_main(C), &snode->edittree->id)) {
368 return true;
369 }
370
371 return false;
372}
373
378
383
388
393
398
403
408
409static bool ed_object_hidden(const Object *ob)
410{
411 /* if hidden but in edit mode, we still display, can happen with animation */
412 return ((ob->visibility_flag & OB_HIDE_VIEWPORT) && !(ob->mode & OB_MODE_EDIT));
413}
414
416{
418 return (ob != nullptr);
419}
420
422{
424 return ((ob != nullptr) && !ed_object_hidden(ob));
425}
426
428{
429 if (ob == nullptr) {
430 CTX_wm_operator_poll_msg_set(C, "Context missing active object");
431 return false;
432 }
433
434 if (!BKE_id_is_editable(CTX_data_main(C), (ID *)ob)) {
435 CTX_wm_operator_poll_msg_set(C, "Cannot edit library linked or non-editable override object");
436 return false;
437 }
438
439 if (ed_object_hidden(ob)) {
440 CTX_wm_operator_poll_msg_set(C, "Cannot edit hidden object");
441 return false;
442 }
443
444 return true;
445}
446
452
457
463
470
477
479{
480 Mesh *mesh = ED_mesh_context(C);
481 return (mesh != nullptr) && ID_IS_EDITABLE(mesh) && !ID_IS_OVERRIDE_LIBRARY(mesh);
482}
483
485{
486 Object *obedit = CTX_data_edit_object(C);
487 if (obedit && obedit->type == OB_MESH) {
488 return nullptr != BKE_editmesh_from_object(obedit);
489 }
490 return false;
491}
492
497
499{
501 return true;
502 }
503
504 CTX_wm_operator_poll_msg_set(C, "expected a view3d region & editmesh");
505 return false;
506}
507
509{
510 Object *obedit = CTX_data_edit_object(C);
511 if (obedit && obedit->type == OB_ARMATURE) {
512 return nullptr != ((bArmature *)obedit->data)->edbo;
513 }
514 return false;
515}
516
525{
526 if (obact != nullptr && !(obact->mode & OB_MODE_EDIT)) {
527 if (obact == BKE_object_pose_armature_get(obact)) {
528 return true;
529 }
530 }
531
532 CTX_wm_operator_poll_msg_set(C, "No object, or not exclusively in pose mode");
533 return false;
534}
535
542
544{
546
548 return false;
549 }
550
551 if (ID_IS_OVERRIDE_LIBRARY(obact)) {
552 CTX_wm_operator_poll_msg_set(C, "Object is a local library override");
553 return false;
554 }
555
556 return true;
557}
558
560{
562
563 if (obpose && !(obpose->mode & OB_MODE_EDIT)) {
564 if (BKE_object_pose_context_check(obpose)) {
565 return true;
566 }
567 }
568
569 return false;
570}
571
573{
575
576 if (obact && !(obact->mode & OB_MODE_EDIT)) {
577 Object *obpose = BKE_object_pose_armature_get(obact);
578 if (obpose != nullptr) {
579 if ((obact == obpose) || (obact->mode & OB_MODE_ALL_WEIGHT_PAINT)) {
580 return true;
581 }
582 }
583 }
584
585 return false;
586}
587
589{
590 if (ED_operator_posemode(C)) {
591 Main *bmain = CTX_data_main(C);
593 bArmature *arm = static_cast<bArmature *>(ob->data);
594 return (BKE_id_is_editable(bmain, &ob->id) && BKE_id_is_editable(bmain, &arm->id));
595 }
596 return false;
597}
598
600{
602 Object *obedit = CTX_data_edit_object(C);
603 return ED_space_image_show_uvedit(sima, obedit);
604}
605
607{
609 Object *obedit = CTX_data_edit_object(C);
610 return sima && ED_space_image_show_uvedit(sima, obedit);
611}
612
614{
615 Object *obedit = CTX_data_edit_object(C);
616 BMEditMesh *em = nullptr;
617
618 if (obedit && obedit->type == OB_MESH) {
619 em = BKE_editmesh_from_object(obedit);
620 }
621
622 if (em && (em->bm->totface)) {
623 return true;
624 }
625
626 return false;
627}
628
630{
631 Object *obedit = CTX_data_edit_object(C);
632 if (obedit && ELEM(obedit->type, OB_CURVES_LEGACY, OB_SURF)) {
633 return nullptr != ((Curve *)obedit->data)->editnurb;
634 }
635 return false;
636}
637
639{
641 return true;
642 }
643
644 CTX_wm_operator_poll_msg_set(C, "expected a view3d region & editcurve");
645 return false;
646}
647
649{
650 Object *obedit = CTX_data_edit_object(C);
651 if (obedit && obedit->type == OB_CURVES_LEGACY) {
652 return nullptr != ((Curve *)obedit->data)->editnurb;
653 }
654 return false;
655}
656
658{
659 Object *obedit = CTX_data_edit_object(C);
660 if (obedit && obedit->type == OB_CURVES_LEGACY) {
661 Curve *cu = (Curve *)obedit->data;
662
663 return (cu->flag & CU_3D) && (nullptr != cu->editnurb);
664 }
665 return false;
666}
667
669{
670 Object *obedit = CTX_data_edit_object(C);
671 if (obedit && obedit->type == OB_SURF) {
672 return nullptr != ((Curve *)obedit->data)->editnurb;
673 }
674 return false;
675}
676
678{
679 Object *obedit = CTX_data_edit_object(C);
680 if (obedit && obedit->type == OB_FONT) {
681 return nullptr != ((Curve *)obedit->data)->editfont;
682 }
683 return false;
684}
685
687{
688 Object *obedit = CTX_data_edit_object(C);
689 if (obedit && obedit->type == OB_LATTICE) {
690 return nullptr != ((Lattice *)obedit->data)->editlatt;
691 }
692 return false;
693}
694
696{
697 Object *obedit = CTX_data_edit_object(C);
698 if (obedit && obedit->type == OB_MBALL) {
699 return nullptr != ((MetaBall *)obedit->data)->editelems;
700 }
701 return false;
702}
703
705{
706 Camera *cam = static_cast<Camera *>(CTX_data_pointer_get_type(C, "camera", &RNA_Camera).data);
707 return (cam != nullptr && ID_IS_EDITABLE(cam));
708}
709
711
712/* -------------------------------------------------------------------- */
715
717{
719 /* no full window splitting allowed */
721 return false;
722 }
723 return true;
724 }
725 return false;
726}
727
733{
734 G.moving |= G_TRANSFORM_WM;
735}
736
739{
740 G.moving &= ~G_TRANSFORM_WM;
741
742 /* Full refresh after `G.moving` is cleared otherwise tool gizmos
743 * won't be refreshed with the modified flag, see: #143629. */
745}
746
748
749/* -------------------------------------------------------------------- */
752
753/* operator state vars used:
754 * none
755 *
756 * functions:
757 *
758 * apply() set action-zone event
759 *
760 * exit() free customdata
761 *
762 * callbacks:
763 *
764 * exec() never used
765 *
766 * invoke() check if in zone
767 * add customdata, put mouseco and area in it
768 * add modal handler
769 *
770 * modal() accept modal events while doing it
771 * call apply() with gesture info, active window, nonactive window
772 * call exit() and remove handler when LMB confirm
773 */
774
782
783/* quick poll to save operators to be created and handled */
785{
786 wmWindow *win = CTX_wm_window(C);
787 if (win && win->eventstate) {
789 if (screen) {
790 const int *xy = &win->eventstate->xy[0];
791
792 LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
793 LISTBASE_FOREACH (AZone *, az, &area->actionzones) {
794 if (BLI_rcti_isect_pt_v(&az->rect, xy)) {
795 return true;
796 }
797 }
798 }
799 }
800 }
801 return false;
802}
803
804/* the debug drawing of the click_rect is in area_draw_azone_fullscreen, keep both in sync */
806 rcti *rect, const short /*x1*/, const short /*y1*/, const short x2, const short y2)
807{
808 BLI_rcti_init(rect, x2 - U.widget_unit, x2, y2 - U.widget_unit, y2);
809}
810
811static bool azone_clipped_rect_calc(const AZone *az, rcti *r_rect_clip)
812{
813 const ARegion *region = az->region;
814 *r_rect_clip = az->rect;
815 if (az->type == AZONE_REGION) {
816 if (region->overlap && (region->v2d.keeptot != V2D_KEEPTOT_STRICT) &&
817 /* Only when this isn't hidden (where it's displayed as an button that expands). */
818 region->runtime->visible)
819 {
820 /* A floating region to be resized, clip by the visible region. */
821 switch (az->edge) {
824 r_rect_clip->xmin = max_ii(
825 r_rect_clip->xmin,
826 (region->winrct.xmin +
827 UI_view2d_view_to_region_x(&region->v2d, region->v2d.tot.xmin)) -
829 r_rect_clip->xmax = min_ii(
830 r_rect_clip->xmax,
831 (region->winrct.xmin +
832 UI_view2d_view_to_region_x(&region->v2d, region->v2d.tot.xmax)) +
834 return true;
835 }
837 case AE_RIGHT_TO_TOPLEFT: {
838 r_rect_clip->ymin = max_ii(
839 r_rect_clip->ymin,
840 (region->winrct.ymin +
841 UI_view2d_view_to_region_y(&region->v2d, region->v2d.tot.ymin)) -
843 r_rect_clip->ymax = min_ii(
844 r_rect_clip->ymax,
845 (region->winrct.ymin +
846 UI_view2d_view_to_region_y(&region->v2d, region->v2d.tot.ymax)) +
848 return true;
849 }
850 }
851 }
852 }
853 return false;
854}
855
856/* Return the azone's calculated rect. */
857static void area_actionzone_get_rect(AZone *az, rcti *r_rect)
858{
859 if (az->type == AZONE_REGION_SCROLL) {
860 const bool is_horizontal = az->direction == AZ_SCROLL_HOR;
861 const bool is_vertical = az->direction == AZ_SCROLL_VERT;
862 const bool is_right = is_vertical && bool(az->region->v2d.scroll & V2D_SCROLL_RIGHT);
863 const bool is_left = is_vertical && bool(az->region->v2d.scroll & V2D_SCROLL_LEFT);
864 const bool is_top = is_horizontal && bool(az->region->v2d.scroll & V2D_SCROLL_TOP);
865 const bool is_bottom = is_horizontal && bool(az->region->v2d.scroll & V2D_SCROLL_BOTTOM);
866 /* For scroll azones use the area around the region's scroll-bar location. */
867 rcti scroller_vert = is_horizontal ? az->region->v2d.hor : az->region->v2d.vert;
868 BLI_rcti_translate(&scroller_vert, az->region->winrct.xmin, az->region->winrct.ymin);
869
870 /* Pull the zone in from edge and match the visible hit zone. */
871 const int edge_padding = int(-3.0f * UI_SCALE_FAC);
872 r_rect->xmin = scroller_vert.xmin - (is_right ? V2D_SCROLL_HIDE_HEIGHT : edge_padding);
873 r_rect->ymin = scroller_vert.ymin - (is_top ? V2D_SCROLL_HIDE_WIDTH : edge_padding);
874 r_rect->xmax = scroller_vert.xmax + (is_left ? V2D_SCROLL_HIDE_HEIGHT : edge_padding);
875 r_rect->ymax = scroller_vert.ymax + (is_bottom ? V2D_SCROLL_HIDE_WIDTH : edge_padding);
876 }
877 else {
878 azone_clipped_rect_calc(az, r_rect);
879 }
880}
881
882static AZone *area_actionzone_refresh_xy(ScrArea *area, const int xy[2], const bool test_only)
883{
884 AZone *az = nullptr;
885
886 for (az = static_cast<AZone *>(area->actionzones.first); az; az = az->next) {
887 rcti az_rect;
888 area_actionzone_get_rect(az, &az_rect);
889 if (BLI_rcti_isect_pt_v(&az_rect, xy)) {
890
891 if (az->type == AZONE_AREA) {
892 break;
893 }
894 if (az->type == AZONE_REGION) {
895 const ARegion *region = az->region;
896 const int local_xy[2] = {xy[0] - region->winrct.xmin, xy[1] - region->winrct.ymin};
897
898 /* Respect button sections: Clusters of buttons (separated using separator-spacers) are
899 * drawn with a background, in-between them the region is fully transparent (if "Region
900 * Overlap" is enabled). Only allow dragging visible edges, so at the button sections. */
901 if (region->runtime->visible && region->overlap &&
904 {
905 az = nullptr;
906 break;
907 }
908
909 break;
910 }
911 if (az->type == AZONE_FULLSCREEN) {
912 rcti click_rect;
913 fullscreen_click_rcti_init(&click_rect, az->x1, az->y1, az->x2, az->y2);
914 const bool click_isect = BLI_rcti_isect_pt_v(&click_rect, xy);
915
916 if (test_only) {
917 if (click_isect) {
918 break;
919 }
920 }
921 else {
922 if (click_isect) {
923 az->alpha = 1.0f;
924 }
925 else {
926 const int mouse_sq = square_i(xy[0] - az->x2) + square_i(xy[1] - az->y2);
927 const int spot_sq = square_i(UI_AZONESPOTW);
928 const int fadein_sq = square_i(AZONEFADEIN);
929 const int fadeout_sq = square_i(AZONEFADEOUT);
930
931 if (mouse_sq < spot_sq) {
932 az->alpha = 1.0f;
933 }
934 else if (mouse_sq < fadein_sq) {
935 az->alpha = 1.0f;
936 }
937 else if (mouse_sq < fadeout_sq) {
938 az->alpha = 1.0f - float(mouse_sq - fadein_sq) / float(fadeout_sq - fadein_sq);
939 }
940 else {
941 az->alpha = 0.0f;
942 }
943
944 /* fade in/out but no click */
945 az = nullptr;
946 }
947
948 /* XXX force redraw to show/hide the action zone */
949 ED_area_tag_redraw(area);
950 break;
951 }
952 }
953 else if (az->type == AZONE_REGION_SCROLL && az->region->runtime->visible) {
954 /* If the region is not visible we can ignore this scroll-bar zone. */
955 ARegion *region = az->region;
956 View2D *v2d = &region->v2d;
957 int scroll_flag = 0;
958 const int isect_value = UI_view2d_mouse_in_scrollers_ex(region, v2d, xy, &scroll_flag);
959
960 /* Check if we even have scroll bars. */
961 if (((az->direction == AZ_SCROLL_HOR) && !(scroll_flag & V2D_SCROLL_HORIZONTAL)) ||
962 ((az->direction == AZ_SCROLL_VERT) && !(scroll_flag & V2D_SCROLL_VERTICAL)))
963 {
964 /* No scroll-bars, do nothing. */
965 }
966 else if (test_only) {
967 if (isect_value != 0) {
968 break;
969 }
970 }
971 else {
972 bool redraw = false;
973
974 if (isect_value == 'h') {
975 if (az->direction == AZ_SCROLL_HOR) {
976 az->alpha = 1.0f;
977 v2d->alpha_hor = 255;
978 redraw = true;
979 }
980 }
981 else if (isect_value == 'v') {
982 if (az->direction == AZ_SCROLL_VERT) {
983 az->alpha = 1.0f;
984 v2d->alpha_vert = 255;
985 redraw = true;
986 }
987 }
988 else {
989 const int local_xy[2] = {xy[0] - region->winrct.xmin, xy[1] - region->winrct.ymin};
990 float dist_fac = 0.0f, alpha = 0.0f;
991
992 if (az->direction == AZ_SCROLL_HOR) {
993 dist_fac = BLI_rcti_length_y(&v2d->hor, local_xy[1]) / V2D_SCROLL_HIDE_WIDTH;
994 CLAMP(dist_fac, 0.0f, 1.0f);
995 alpha = 1.0f - dist_fac;
996
997 v2d->alpha_hor = alpha * 255;
998 }
999 else if (az->direction == AZ_SCROLL_VERT) {
1000 dist_fac = BLI_rcti_length_x(&v2d->vert, local_xy[0]) / V2D_SCROLL_HIDE_HEIGHT;
1001 CLAMP(dist_fac, 0.0f, 1.0f);
1002 alpha = 1.0f - dist_fac;
1003
1004 v2d->alpha_vert = alpha * 255;
1005 }
1006 az->alpha = alpha;
1007 redraw = true;
1008 }
1009
1010 if (redraw) {
1012 }
1013 /* Don't return! */
1014 }
1015 }
1016 }
1017 else if (!test_only && !IS_EQF(az->alpha, 0.0f)) {
1018 if (az->type == AZONE_FULLSCREEN) {
1019 az->alpha = 0.0f;
1022 }
1023 else if (az->type == AZONE_REGION_SCROLL && az->region->runtime->visible) {
1024 /* If the region is not visible we can ignore this scroll-bar zone. */
1025 if (az->direction == AZ_SCROLL_VERT) {
1026 az->alpha = az->region->v2d.alpha_vert = 0;
1029 }
1030 else if (az->direction == AZ_SCROLL_HOR) {
1031 az->alpha = az->region->v2d.alpha_hor = 0;
1034 }
1035 else {
1036 BLI_assert(false);
1037 }
1038 }
1039 }
1040 }
1041
1042 return az;
1043}
1044
1045/* Finds an action-zone by position in entire screen so azones can overlap. */
1046static AZone *screen_actionzone_find_xy(bScreen *screen, const int xy[2])
1047{
1048 LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
1049 AZone *az = area_actionzone_refresh_xy(area, xy, true);
1050 if (az != nullptr) {
1051 return az;
1052 }
1053 }
1054 return nullptr;
1055}
1056
1057/* Returns the area that the azone belongs to */
1058static ScrArea *screen_actionzone_area(bScreen *screen, const AZone *az)
1059{
1060 LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
1061 LISTBASE_FOREACH (AZone *, zone, &area->actionzones) {
1062 if (zone == az) {
1063 return area;
1064 }
1065 }
1066 }
1067 return nullptr;
1068}
1069
1071{
1072 return area_actionzone_refresh_xy(area, xy, true);
1073}
1074
1076{
1077 return area_actionzone_refresh_xy(area, xy, false);
1078}
1079
1081{
1082 sActionzoneData *sad = static_cast<sActionzoneData *>(op->customdata);
1083 if (sad) {
1084 MEM_freeN(sad);
1085 }
1086 op->customdata = nullptr;
1087
1089}
1090
1091/* send EVT_ACTIONZONE event */
1092static void actionzone_apply(bContext *C, wmOperator *op, int type)
1093{
1094 wmWindow *win = CTX_wm_window(C);
1095
1096 wmEvent event;
1097 wm_event_init_from_window(win, &event);
1098
1099 if (type == AZONE_AREA) {
1100 event.type = EVT_ACTIONZONE_AREA;
1101 }
1102 else if (type == AZONE_FULLSCREEN) {
1103 event.type = EVT_ACTIONZONE_FULLSCREEN;
1104 }
1105 else {
1106 event.type = EVT_ACTIONZONE_REGION;
1107 }
1108
1109 event.val = KM_NOTHING;
1110 event.flag = eWM_EventFlag(0);
1111 event.customdata = op->customdata;
1112 event.customdata_free = true;
1113 op->customdata = nullptr;
1114
1115 WM_event_add(win, &event);
1116}
1117
1119{
1120 bScreen *screen = CTX_wm_screen(C);
1121 AZone *az = screen_actionzone_find_xy(screen, event->xy);
1122
1123 /* Quick escape - Scroll azones only hide/unhide the scroll-bars,
1124 * they have their own handling. */
1125 if (az == nullptr || ELEM(az->type, AZONE_REGION_SCROLL)) {
1126 return OPERATOR_PASS_THROUGH;
1127 }
1128
1129 /* ok we do the action-zone */
1130 sActionzoneData *sad = static_cast<sActionzoneData *>(
1131 op->customdata = MEM_callocN(sizeof(sActionzoneData), "sActionzoneData"));
1132 sad->sa1 = screen_actionzone_area(screen, az);
1133 sad->az = az;
1134 sad->x = event->xy[0];
1135 sad->y = event->xy[1];
1136 sad->modifier = RNA_int_get(op->ptr, "modifier");
1137
1138 /* region azone directly reacts on mouse clicks */
1139 if (ELEM(sad->az->type, AZONE_REGION, AZONE_FULLSCREEN)) {
1140 actionzone_apply(C, op, sad->az->type);
1141 actionzone_exit(op);
1142 return OPERATOR_FINISHED;
1143 }
1144
1145 if (sad->az->type == AZONE_AREA && sad->modifier == 0) {
1146 actionzone_apply(C, op, sad->az->type);
1147 actionzone_exit(op);
1148 return OPERATOR_FINISHED;
1149 }
1150
1151 /* add modal handler */
1155}
1156
1158{
1159 bScreen *screen = CTX_wm_screen(C);
1160 sActionzoneData *sad = static_cast<sActionzoneData *>(op->customdata);
1161
1162 switch (event->type) {
1163 case MOUSEMOVE: {
1164 const int delta_x = (event->xy[0] - sad->x);
1165 const int delta_y = (event->xy[1] - sad->y);
1166
1167 /* Movement in dominant direction. */
1168 const int delta_max = max_ii(abs(delta_x), abs(delta_y));
1169
1170 /* Movement in dominant direction before action taken. */
1171 const int join_threshold = (0.6 * U.widget_unit);
1172 const int split_threshold = (1.2 * U.widget_unit);
1173 const int area_threshold = (0.1 * U.widget_unit);
1174
1175 /* Calculate gesture cardinal direction. */
1176 if (delta_y > abs(delta_x)) {
1178 }
1179 else if (delta_x >= abs(delta_y)) {
1181 }
1182 else if (delta_y < -abs(delta_x)) {
1184 }
1185 else {
1187 }
1188
1189 bool is_gesture;
1190 if (sad->az->type == AZONE_AREA) {
1191 wmWindow *win = CTX_wm_window(C);
1192
1193 rcti screen_rect;
1194 WM_window_screen_rect_calc(win, &screen_rect);
1195
1196 /* Have we dragged off the zone and are not on an edge? */
1197 if ((ED_area_actionzone_find_xy(sad->sa1, event->xy) != sad->az) &&
1199 AREAMAP_FROM_SCREEN(screen), &screen_rect, event->xy[0], event->xy[1]) ==
1200 nullptr))
1201 {
1202
1203 /* What area are we now in? */
1204 ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, event->xy);
1205
1206 if (sad->modifier == 1) {
1207 /* Duplicate area into new window. */
1209 is_gesture = (delta_max > area_threshold);
1210 }
1211 else if (sad->modifier == 2) {
1212 /* Swap areas. */
1214 is_gesture = true;
1215 }
1216 else if (area == sad->sa1) {
1217 /* Same area, so possible split. */
1218 WM_cursor_set(win,
1221 is_gesture = (delta_max > split_threshold);
1222 }
1223 else if (!area || area->global) {
1224 /* No area or Top bar or Status bar. */
1226 is_gesture = false;
1227 }
1228 else {
1229 /* Different area, so possible join. */
1230 if (sad->gesture_dir == SCREEN_DIR_N) {
1232 }
1233 else if (sad->gesture_dir == SCREEN_DIR_S) {
1235 }
1236 else if (sad->gesture_dir == SCREEN_DIR_E) {
1238 }
1239 else {
1242 }
1243 is_gesture = (delta_max > join_threshold);
1244 }
1245 }
1246 else {
1247#if defined(__APPLE__)
1248 const int cursor = WM_CURSOR_HAND_CLOSED;
1249#else
1250 const int cursor = WM_CURSOR_MOVE;
1251#endif
1252 WM_cursor_set(win, cursor);
1253 is_gesture = false;
1254 }
1255 }
1256 else {
1257 is_gesture = (delta_max > area_threshold);
1258 }
1259
1260 /* gesture is large enough? */
1261 if (is_gesture) {
1262 /* second area, for join when (sa1 != sa2) */
1263 sad->sa2 = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, event->xy);
1264 /* apply sends event */
1265 actionzone_apply(C, op, sad->az->type);
1266 actionzone_exit(op);
1267
1268 return OPERATOR_FINISHED;
1269 }
1270 break;
1271 }
1272 case EVT_ESCKEY:
1273 actionzone_exit(op);
1274 return OPERATOR_CANCELLED;
1275 case LEFTMOUSE:
1276 actionzone_exit(op);
1277 return OPERATOR_CANCELLED;
1278 default: {
1279 break;
1280 }
1281 }
1282
1284}
1285
1286static void actionzone_cancel(bContext * /*C*/, wmOperator *op)
1287{
1288 actionzone_exit(op);
1289}
1290
1292{
1293 /* identifiers */
1294 ot->name = "Handle Area Action Zones";
1295 ot->description = "Handle area action zones for mouse actions/gestures";
1296 ot->idname = "SCREEN_OT_actionzone";
1297
1298 ot->invoke = actionzone_invoke;
1299 ot->modal = actionzone_modal;
1300 ot->poll = actionzone_area_poll;
1301 ot->cancel = actionzone_cancel;
1302
1303 /* flags */
1305
1306 RNA_def_int(ot->srna, "modifier", 0, 0, 2, "Modifier", "Modifier state", 0, 2);
1307}
1308
1310
1311/* -------------------------------------------------------------------- */
1314
1316 const int cursor[2],
1317 ScrArea **r_sa1,
1318 ScrArea **r_sa2)
1319{
1320 wmWindow *win = CTX_wm_window(C);
1321 bScreen *screen = CTX_wm_screen(C);
1322 rcti window_rect;
1323 WM_window_rect_calc(win, &window_rect);
1325 AREAMAP_FROM_SCREEN(screen), &window_rect, cursor[0], cursor[1]);
1326 *r_sa1 = nullptr;
1327 *r_sa2 = nullptr;
1328 if (actedge == nullptr) {
1329 return nullptr;
1330 }
1331 int borderwidth = (4 * UI_SCALE_FAC);
1332 ScrArea *sa1, *sa2;
1333 if (screen_geom_edge_is_horizontal(actedge)) {
1335 screen, SPACE_TYPE_ANY, blender::int2{cursor[0], cursor[1] + borderwidth});
1337 screen, SPACE_TYPE_ANY, blender::int2{cursor[0], cursor[1] - borderwidth});
1338 }
1339 else {
1341 screen, SPACE_TYPE_ANY, blender::int2{cursor[0] + borderwidth, cursor[1]});
1343 screen, SPACE_TYPE_ANY, blender::int2{cursor[0] - borderwidth, cursor[1]});
1344 }
1345 bool isGlobal = ((sa1 && ED_area_is_global(sa1)) || (sa2 && ED_area_is_global(sa2)));
1346 if (!isGlobal) {
1347 *r_sa1 = sa1;
1348 *r_sa2 = sa2;
1349 }
1350 return actedge;
1351}
1352
1354
1355/* -------------------------------------------------------------------- */
1358
1359/* operator state vars used:
1360 * sa1 start area
1361 * sa2 area to swap with
1362 *
1363 * functions:
1364 *
1365 * init() set custom data for operator, based on action-zone event custom data
1366 *
1367 * cancel() cancel the operator
1368 *
1369 * exit() cleanup, send notifier
1370 *
1371 * callbacks:
1372 *
1373 * invoke() gets called on Shift-LMB drag in action-zone
1374 * exec() execute without any user interaction, based on properties
1375 * call init(), add handler
1376 *
1377 * modal() accept modal events while doing it
1378 */
1379
1382};
1383
1384static bool area_swap_init(wmOperator *op, const wmEvent *event)
1385{
1386 sActionzoneData *sad = static_cast<sActionzoneData *>(event->customdata);
1387
1388 if (sad == nullptr || sad->sa1 == nullptr) {
1389 return false;
1390 }
1391
1392 sAreaSwapData *sd = MEM_callocN<sAreaSwapData>("sAreaSwapData");
1393 sd->sa1 = sad->sa1;
1394 sd->sa2 = sad->sa2;
1395 op->customdata = sd;
1396
1397 return true;
1398}
1399
1401{
1402 sAreaSwapData *sd = static_cast<sAreaSwapData *>(op->customdata);
1403 MEM_freeN(sd);
1404 op->customdata = nullptr;
1405
1407 ED_workspace_status_text(C, nullptr);
1408}
1409
1411{
1412 area_swap_exit(C, op);
1413}
1414
1416{
1417 if (!area_swap_init(op, event)) {
1418 return OPERATOR_PASS_THROUGH;
1419 }
1420
1421 /* add modal handler */
1424
1426}
1427
1429{
1430 sActionzoneData *sad = static_cast<sActionzoneData *>(op->customdata);
1431
1432 switch (event->type) {
1433 case MOUSEMOVE: {
1434 /* Second area to swap with. */
1437 WorkspaceStatus status(C);
1438 status.item(IFACE_("Select Area"), ICON_MOUSE_LMB);
1439 status.item(IFACE_("Cancel"), ICON_EVENT_ESC);
1440 break;
1441 }
1442 case LEFTMOUSE: /* release LMB */
1443 if (event->val == KM_RELEASE) {
1444 if (!sad->sa2 || sad->sa1 == sad->sa2) {
1445 area_swap_cancel(C, op);
1446 return OPERATOR_CANCELLED;
1447 }
1448
1449 ED_area_tag_redraw(sad->sa1);
1450 ED_area_tag_redraw(sad->sa2);
1451
1452 ED_area_swapspace(C, sad->sa1, sad->sa2);
1453
1454 area_swap_exit(C, op);
1455
1457
1458 return OPERATOR_FINISHED;
1459 }
1460 break;
1461
1462 case EVT_ESCKEY:
1463 area_swap_cancel(C, op);
1464 return OPERATOR_CANCELLED;
1465 default: {
1466 break;
1467 }
1468 }
1470}
1471
1473{
1474 ScrArea *sa1, *sa2;
1475 int cursor[2];
1476 RNA_int_get_array(op->ptr, "cursor", cursor);
1477 screen_area_edge_from_cursor(C, cursor, &sa1, &sa2);
1478 if (sa1 == nullptr || sa2 == nullptr) {
1479 return OPERATOR_CANCELLED;
1480 }
1481 ED_area_swapspace(C, sa1, sa2);
1482 return OPERATOR_FINISHED;
1483}
1484
1486{
1487 ot->name = "Swap Areas";
1488 ot->description = "Swap selected areas screen positions";
1489 ot->idname = "SCREEN_OT_area_swap";
1490
1491 ot->invoke = area_swap_invoke;
1492 ot->modal = area_swap_modal;
1493 ot->exec = area_swap_exec;
1494 ot->poll = screen_active_editable;
1495 ot->cancel = area_swap_cancel;
1496
1497 ot->flag = OPTYPE_BLOCKING;
1498
1499 /* rna */
1501 ot->srna, "cursor", 2, nullptr, INT_MIN, INT_MAX, "Cursor", "", INT_MIN, INT_MAX);
1502}
1503
1505
1506/* -------------------------------------------------------------------- */
1511
1513static void area_dupli_fn(bScreen * /*screen*/, ScrArea *area, void *user_data)
1514{
1515 ScrArea *area_src = static_cast<ScrArea *>(user_data);
1516 ED_area_data_copy(area, area_src, true);
1517 ED_area_tag_redraw(area);
1518}
1519
1520/* operator callback */
1521static bool area_dupli_open(bContext *C, ScrArea *area, const blender::int2 position)
1522{
1523 const rcti window_rect = {
1524 position.x, position.x + area->winx, position.y, position.y + area->winy};
1525
1526 /* Create new window. No need to set space_type since it will be copied over. */
1527 wmWindow *newwin = WM_window_open(C,
1528 nullptr,
1529 &window_rect,
1531 false,
1532 false,
1533 false,
1535 /* Initialize area from callback. */
1537 (void *)area);
1538 return (newwin != nullptr);
1539}
1540
1542{
1543 ScrArea *area = CTX_wm_area(C);
1544 if (event && event->customdata) {
1545 sActionzoneData *sad = static_cast<sActionzoneData *>(event->customdata);
1546 if (sad == nullptr) {
1547 return OPERATOR_PASS_THROUGH;
1548 }
1549 area = sad->sa1;
1550 }
1551
1552 bool newwin = area_dupli_open(C, area, blender::int2(area->totrct.xmin, area->totrct.ymin));
1553
1554 if (newwin) {
1555 /* screen, areas init */
1557 }
1558 else {
1559 BKE_report(op->reports, RPT_ERROR, "Failed to open window!");
1560 }
1561
1562 if (event && event->customdata) {
1563 actionzone_exit(op);
1564 }
1565
1566 return newwin ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
1567}
1568
1570{
1571 ot->name = "Duplicate Area into New Window";
1572 ot->description = "Duplicate selected area into new window";
1573 ot->idname = "SCREEN_OT_area_dupli";
1574
1575 ot->invoke = area_dupli_invoke;
1576 ot->poll = ED_operator_areaactive;
1577}
1578
1580
1581/* -------------------------------------------------------------------- */
1586
1595{
1596 bScreen *screen = CTX_wm_screen(C);
1597 ScrArea *area = CTX_wm_area(C);
1598
1599 /* This operator is script-able, so the area passed could be invalid. */
1600 if (BLI_findindex(&screen->areabase, area) == -1) {
1601 BKE_report(op->reports, RPT_ERROR, "Area not found in the active screen");
1602 return OPERATOR_CANCELLED;
1603 }
1604
1605 if (!screen_area_close(C, op->reports, screen, area)) {
1606 BKE_report(op->reports, RPT_ERROR, "Unable to close area");
1607 return OPERATOR_CANCELLED;
1608 }
1609
1610 /* Ensure the event loop doesn't attempt to continue handling events.
1611 *
1612 * This causes execution from the Python console fail to return to the prompt as it should.
1613 * This glitch could be solved in the event loop handling as other operators may also
1614 * destructively manipulate windowing data. */
1615 CTX_wm_window_set(C, nullptr);
1616
1618
1619 return OPERATOR_FINISHED;
1620}
1621
1623{
1624 if (!ED_operator_areaactive(C)) {
1625 return false;
1626 }
1627
1628 ScrArea *area = CTX_wm_area(C);
1629
1630 if (ED_area_is_global(area)) {
1631 return false;
1632 }
1633
1634 bScreen *screen = CTX_wm_screen(C);
1635
1636 /* Can this area join with ANY other area? */
1637 LISTBASE_FOREACH (ScrArea *, ar, &screen->areabase) {
1638 if (area_getorientation(ar, area) != -1) {
1639 return true;
1640 }
1641 }
1642
1643 return false;
1644}
1645
1647{
1648 ot->name = "Close Area";
1649 ot->description = "Close selected area";
1650 ot->idname = "SCREEN_OT_area_close";
1651 ot->exec = area_close_exec;
1652 ot->poll = area_close_poll;
1653}
1654
1656
1657/* -------------------------------------------------------------------- */
1660
1661/* operator state vars used:
1662 * x, y mouse coord near edge
1663 * delta movement of edge
1664 *
1665 * functions:
1666 *
1667 * init() set default property values, find edge based on mouse coords, test
1668 * if the edge can be moved, select edges, calculate min and max movement
1669 *
1670 * apply() apply delta on selection
1671 *
1672 * exit() cleanup, send notifier
1673 *
1674 * cancel() cancel moving
1675 *
1676 * callbacks:
1677 *
1678 * exec() execute without any user interaction, based on properties
1679 * call init(), apply(), exit()
1680 *
1681 * invoke() gets called on mouse click near edge
1682 * call init(), add handler
1683 *
1684 * modal() accept modal events while doing it
1685 * call apply() with delta motion
1686 * call exit() and remove handler
1687 */
1688
1690 /* Snapping disabled */
1691 SNAP_NONE = 0, /* Snap to an invisible grid with a unit defined in AREAGRID */
1692 SNAP_AREAGRID, /* Snap to fraction (half, third.. etc) and adjacent edges. */
1693 SNAP_FRACTION_AND_ADJACENT, /* Snap to either bigger or smaller, nothing in-between (used for
1694 * global areas). This has priority over other snap types, if it is
1695 * used, toggling SNAP_FRACTION_AND_ADJACENT doesn't work. */
1697};
1698
1706
1707/* helper call to move area-edge, sets limits
1708 * need window bounds in order to get correct limits */
1710 bScreen *screen,
1711 const eScreenAxis dir_axis,
1712 int *bigger,
1713 int *smaller,
1714 bool *use_bigger_smaller_snap)
1715{
1716 /* we check all areas and test for free space with MINSIZE */
1717 *bigger = *smaller = 100000;
1718
1719 if (use_bigger_smaller_snap != nullptr) {
1720 *use_bigger_smaller_snap = false;
1722 int size_min = ED_area_global_min_size_y(area) - 1;
1723 int size_max = ED_area_global_max_size_y(area) - 1;
1724
1725 size_min = max_ii(size_min, 0);
1726 BLI_assert(size_min <= size_max);
1727
1728 /* logic here is only tested for lower edge :) */
1729 /* left edge */
1730 if (area->v1->editflag && area->v2->editflag) {
1731 *smaller = area->v4->vec.x - size_max;
1732 *bigger = area->v4->vec.x - size_min;
1733 *use_bigger_smaller_snap = true;
1734 return;
1735 }
1736 /* top edge */
1737 if (area->v2->editflag && area->v3->editflag) {
1738 *smaller = area->v1->vec.y + size_min;
1739 *bigger = area->v1->vec.y + size_max;
1740 *use_bigger_smaller_snap = true;
1741 return;
1742 }
1743 /* right edge */
1744 if (area->v3->editflag && area->v4->editflag) {
1745 *smaller = area->v1->vec.x + size_min;
1746 *bigger = area->v1->vec.x + size_max;
1747 *use_bigger_smaller_snap = true;
1748 return;
1749 }
1750 /* lower edge */
1751 if (area->v4->editflag && area->v1->editflag) {
1752 *smaller = area->v2->vec.y - size_max;
1753 *bigger = area->v2->vec.y - size_min;
1754 *use_bigger_smaller_snap = true;
1755 return;
1756 }
1757 }
1758 }
1759
1760 rcti window_rect;
1761 WM_window_rect_calc(win, &window_rect);
1762
1763 LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
1764 if (dir_axis == SCREEN_AXIS_H) {
1765 const int y1 = area->winy - ED_area_headersize();
1766 /* if top or down edge selected, test height */
1767 if (area->v1->editflag && area->v4->editflag) {
1768 *bigger = min_ii(*bigger, y1);
1769 }
1770 else if (area->v2->editflag && area->v3->editflag) {
1771 *smaller = min_ii(*smaller, y1);
1772 }
1773 }
1774 else {
1775 const int x1 = area->winx - int(AREAMINX * UI_SCALE_FAC) - 1;
1776 /* if left or right edge selected, test width */
1777 if (area->v1->editflag && area->v2->editflag) {
1778 *bigger = min_ii(*bigger, x1);
1779 }
1780 else if (area->v3->editflag && area->v4->editflag) {
1781 *smaller = min_ii(*smaller, x1);
1782 }
1783 }
1784 }
1785}
1786
1787static void area_move_draw_cb(const wmWindow *win, void *userdata)
1788{
1789 const wmOperator *op = static_cast<const wmOperator *>(userdata);
1790 const sAreaMoveData *md = static_cast<sAreaMoveData *>(op->customdata);
1792}
1793
1794/* validate selection inside screen, set variables OK */
1795/* return false: init failed */
1797{
1798 bScreen *screen = CTX_wm_screen(C);
1799 wmWindow *win = CTX_wm_window(C);
1800 ScrArea *area = CTX_wm_area(C);
1801
1802 /* required properties */
1803 int x = RNA_int_get(op->ptr, "x");
1804 int y = RNA_int_get(op->ptr, "y");
1805
1806 /* setup */
1807 ScrEdge *actedge = screen_geom_find_active_scredge(win, screen, x, y);
1808
1809 if (area) {
1810 /* Favor scroll bars and action zones over expanded edge zone. */
1811 const int xy[2] = {x, y};
1812 if (ED_area_actionzone_find_xy(area, xy)) {
1813 return false;
1814 }
1815 }
1816
1817 if (actedge == nullptr) {
1818 return false;
1819 }
1820
1821 sAreaMoveData *md = MEM_callocN<sAreaMoveData>("sAreaMoveData");
1822 op->customdata = md;
1823
1825 if (md->dir_axis == SCREEN_AXIS_H) {
1826 md->origval = actedge->v1->vec.y;
1827 }
1828 else {
1829 md->origval = actedge->v1->vec.x;
1830 }
1831
1833 /* now all vertices with 'flag == 1' are the ones that can be moved. Move this to editflag */
1834 ED_screen_verts_iter(win, screen, v1)
1835 {
1836 v1->editflag = v1->flag;
1837 }
1838
1839 bool use_bigger_smaller_snap = false;
1841 win, screen, md->dir_axis, &md->bigger, &md->smaller, &use_bigger_smaller_snap);
1842
1843 md->snap_type = use_bigger_smaller_snap ? SNAP_BIGGER_SMALLER_ONLY : SNAP_AREAGRID;
1844
1845 md->screen = screen;
1847
1848 return true;
1849}
1850
1851static int area_snap_calc_location(const bScreen *screen,
1852 const enum AreaMoveSnapType snap_type,
1853 const int delta,
1854 const int origval,
1855 const eScreenAxis dir_axis,
1856 const int bigger,
1857 const int smaller)
1858{
1859 BLI_assert(snap_type != SNAP_NONE);
1860 int m_cursor_final = -1;
1861 const int m_cursor = origval + delta;
1862 const int m_span = float(bigger + smaller);
1863 const int m_min = origval - smaller;
1864 // const int axis_max = axis_min + m_span;
1865
1866 switch (snap_type) {
1867 case SNAP_AREAGRID: {
1868 m_cursor_final = m_cursor;
1869 if (!ELEM(delta, bigger, -smaller)) {
1870 m_cursor_final -= (m_cursor % AREAGRID);
1871 CLAMP(m_cursor_final, origval - smaller, origval + bigger);
1872 }
1873
1874 /* Slight snap to vertical minimum and maximum. */
1875 const int snap_threshold = int(float(ED_area_headersize()) * 0.6f);
1876 if (m_cursor_final < (m_min + snap_threshold)) {
1877 m_cursor_final = m_min;
1878 }
1879 else if (m_cursor_final > (origval + bigger - snap_threshold)) {
1880 m_cursor_final = origval + bigger;
1881 }
1882 } break;
1883
1885 m_cursor_final = (m_cursor >= bigger) ? bigger : smaller;
1886 break;
1887
1889 const int axis = (dir_axis == SCREEN_AXIS_V) ? 0 : 1;
1890 int snap_dist_best = INT_MAX;
1891 {
1892 const float div_array[] = {
1893 0.0f,
1894 1.0f / 12.0f,
1895 2.0f / 12.0f,
1896 3.0f / 12.0f,
1897 4.0f / 12.0f,
1898 5.0f / 12.0f,
1899 6.0f / 12.0f,
1900 7.0f / 12.0f,
1901 8.0f / 12.0f,
1902 9.0f / 12.0f,
1903 10.0f / 12.0f,
1904 11.0f / 12.0f,
1905 1.0f,
1906 };
1907 /* Test the snap to the best division. */
1908 for (int i = 0; i < ARRAY_SIZE(div_array); i++) {
1909 const int m_cursor_test = m_min + round_fl_to_int(m_span * div_array[i]);
1910 const int snap_dist_test = abs(m_cursor - m_cursor_test);
1911 if (snap_dist_best >= snap_dist_test) {
1912 snap_dist_best = snap_dist_test;
1913 m_cursor_final = m_cursor_test;
1914 }
1915 }
1916 }
1917
1918 LISTBASE_FOREACH (const ScrVert *, v1, &screen->vertbase) {
1919 if (!v1->editflag) {
1920 continue;
1921 }
1922 const int v_loc = (&v1->vec.x)[!axis];
1923
1924 LISTBASE_FOREACH (const ScrVert *, v2, &screen->vertbase) {
1925 if (v2->editflag) {
1926 continue;
1927 }
1928 if (v_loc == (&v2->vec.x)[!axis]) {
1929 const int v_loc2 = (&v2->vec.x)[axis];
1930 /* Do not snap to the vertices at the ends. */
1931 if ((origval - smaller) < v_loc2 && v_loc2 < (origval + bigger)) {
1932 const int snap_dist_test = abs(m_cursor - v_loc2);
1933 if (snap_dist_best >= snap_dist_test) {
1934 snap_dist_best = snap_dist_test;
1935 m_cursor_final = v_loc2;
1936 }
1937 }
1938 }
1939 }
1940 }
1941 break;
1942 }
1943 case SNAP_NONE:
1944 break;
1945 }
1946
1948 IN_RANGE_INCL(m_cursor_final, origval - smaller, origval + bigger));
1949
1950 return m_cursor_final;
1951}
1952
1953/* moves selected screen edge amount of delta, used by split & move */
1955 int delta,
1956 const int origval,
1957 const eScreenAxis dir_axis,
1958 const int bigger,
1959 const int smaller,
1960 const enum AreaMoveSnapType snap_type)
1961{
1962 WorkspaceStatus status(C);
1963 status.item(IFACE_("Confirm"), ICON_MOUSE_LMB);
1964 status.item(IFACE_("Cancel"), ICON_EVENT_ESC);
1965 status.item_bool(IFACE_("Snap"), snap_type == SNAP_FRACTION_AND_ADJACENT, ICON_EVENT_CTRL);
1966
1967 wmWindow *win = CTX_wm_window(C);
1968 bScreen *screen = CTX_wm_screen(C);
1969 short final_loc = -1;
1970 bool doredraw = false;
1971
1972 if (snap_type != SNAP_BIGGER_SMALLER_ONLY) {
1973 CLAMP(delta, -smaller, bigger);
1974 }
1975
1976 if (snap_type == SNAP_NONE) {
1977 final_loc = origval + delta;
1978 }
1979 else {
1980 final_loc = area_snap_calc_location(
1981 screen, snap_type, delta, origval, dir_axis, bigger, smaller);
1982 }
1983
1984 BLI_assert(final_loc != -1);
1985 short axis = (dir_axis == SCREEN_AXIS_V) ? 0 : 1;
1986
1987 ED_screen_verts_iter(win, screen, v1)
1988 {
1989 if (v1->editflag) {
1990 short oldval = (&v1->vec.x)[axis];
1991 (&v1->vec.x)[axis] = final_loc;
1992
1993 if (oldval == final_loc) {
1994 /* nothing will change to the other vertices either. */
1995 break;
1996 }
1997 doredraw = true;
1998 }
1999 }
2000
2001 /* only redraw if we actually moved a screen vert, for AREAGRID */
2002 if (doredraw) {
2003 bool redraw_all = false;
2004 ED_screen_areas_iter (win, screen, area) {
2005 if (area->v1->editflag || area->v2->editflag || area->v3->editflag || area->v4->editflag) {
2006 if (ED_area_is_global(area)) {
2007 /* Snap to minimum or maximum for global areas. */
2009 if (abs(height - area->global->size_min) < abs(height - area->global->size_max)) {
2010 area->global->cur_fixed_height = area->global->size_min;
2011 }
2012 else {
2013 area->global->cur_fixed_height = area->global->size_max;
2014 }
2015
2016 screen->do_refresh = true;
2017 redraw_all = true;
2018 }
2020 }
2021 }
2022 if (redraw_all) {
2023 ED_screen_areas_iter (win, screen, area) {
2024 ED_area_tag_redraw(area);
2025 }
2026 }
2027
2029
2030 WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, nullptr); /* redraw everything */
2031 /* Update preview thumbnail */
2032 BKE_icon_changed(screen->id.icon_id);
2033 }
2034}
2035
2037{
2038 sAreaMoveData *md = static_cast<sAreaMoveData *>(op->customdata);
2039 int delta = RNA_int_get(op->ptr, "delta");
2040
2041 area_move_apply_do(C, delta, md->origval, md->dir_axis, md->bigger, md->smaller, md->snap_type);
2042}
2043
2045{
2046 sAreaMoveData *md = static_cast<sAreaMoveData *>(op->customdata);
2047 if (md->draw_callback) {
2049 }
2050
2051 MEM_freeN(md);
2052 op->customdata = nullptr;
2053
2054 /* this makes sure aligned edges will result in aligned grabbing */
2057 ED_workspace_status_text(C, nullptr);
2058
2060}
2061
2063{
2064 if (!area_move_init(C, op)) {
2065 return OPERATOR_CANCELLED;
2066 }
2067
2068 area_move_apply(C, op);
2069 area_move_exit(C, op);
2070
2071 return OPERATOR_FINISHED;
2072}
2073
2074/* interaction callback */
2076{
2077 RNA_int_set(op->ptr, "x", event->xy[0]);
2078 RNA_int_set(op->ptr, "y", event->xy[1]);
2079
2080 if (!area_move_init(C, op)) {
2081 return OPERATOR_PASS_THROUGH;
2082 }
2083
2084 sAreaMoveData *md = static_cast<sAreaMoveData *>(op->customdata);
2085
2086 WorkspaceStatus status(C);
2087 status.item(IFACE_("Confirm"), ICON_MOUSE_LMB);
2088 status.item(IFACE_("Cancel"), ICON_EVENT_ESC);
2089 status.item_bool(IFACE_("Snap"), md->snap_type == SNAP_FRACTION_AND_ADJACENT, ICON_EVENT_CTRL);
2090
2091 /* add temp handler */
2094
2096}
2097
2099{
2100
2101 RNA_int_set(op->ptr, "delta", 0);
2102 area_move_apply(C, op);
2103 area_move_exit(C, op);
2104}
2105
2106/* modal callback for while moving edges */
2108{
2109 sAreaMoveData *md = static_cast<sAreaMoveData *>(op->customdata);
2110
2111 /* execute the events */
2112 switch (event->type) {
2113 case MOUSEMOVE: {
2114 int x = RNA_int_get(op->ptr, "x");
2115 int y = RNA_int_get(op->ptr, "y");
2116
2117 const int delta = (md->dir_axis == SCREEN_AXIS_V) ? event->xy[0] - x : event->xy[1] - y;
2118 RNA_int_set(op->ptr, "delta", delta);
2119
2120 area_move_apply(C, op);
2121 break;
2122 }
2123 case RIGHTMOUSE: {
2124 area_move_cancel(C, op);
2125 return OPERATOR_CANCELLED;
2126 }
2127 case EVT_MODAL_MAP: {
2128 switch (event->val) {
2129 case KM_MODAL_APPLY:
2130 area_move_exit(C, op);
2131 return OPERATOR_FINISHED;
2132
2133 case KM_MODAL_CANCEL:
2134 area_move_cancel(C, op);
2135 return OPERATOR_CANCELLED;
2136
2137 case KM_MODAL_SNAP_ON:
2140 }
2141 break;
2142
2143 case KM_MODAL_SNAP_OFF:
2146 }
2147 break;
2148 }
2149 WorkspaceStatus status(C);
2150 status.item(IFACE_("Confirm"), ICON_MOUSE_LMB);
2151 status.item(IFACE_("Cancel"), ICON_EVENT_ESC);
2152 status.item_bool(
2153 IFACE_("Snap"), md->snap_type == SNAP_FRACTION_AND_ADJACENT, ICON_EVENT_CTRL);
2154 break;
2155 }
2156 default: {
2157 break;
2158 }
2159 }
2160
2162}
2163
2165{
2166 /* identifiers */
2167 ot->name = "Move Area Edges";
2168 ot->description = "Move selected area edges";
2169 ot->idname = "SCREEN_OT_area_move";
2170
2171 ot->exec = area_move_exec;
2172 ot->invoke = area_move_invoke;
2173 ot->cancel = area_move_cancel;
2174 ot->modal = area_move_modal;
2175 ot->poll = ED_operator_screen_mainwinactive; /* when mouse is over area-edge */
2176
2177 /* flags */
2179
2180 /* rna */
2181 RNA_def_int(ot->srna, "x", 0, INT_MIN, INT_MAX, "X", "", INT_MIN, INT_MAX);
2182 RNA_def_int(ot->srna, "y", 0, INT_MIN, INT_MAX, "Y", "", INT_MIN, INT_MAX);
2183 RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
2184}
2185
2187
2188/* -------------------------------------------------------------------- */
2191
2192/*
2193 * operator state vars:
2194 * fac spit point
2195 * dir direction #SCREEN_AXIS_V or #SCREEN_AXIS_H
2196 *
2197 * operator customdata:
2198 * area pointer to (active) area
2199 * x, y last used mouse pos
2200 * (more, see below)
2201 *
2202 * functions:
2203 *
2204 * init() set default property values, find area based on context
2205 *
2206 * apply() split area based on state vars
2207 *
2208 * exit() cleanup, send notifier
2209 *
2210 * cancel() remove duplicated area
2211 *
2212 * callbacks:
2213 *
2214 * exec() execute without any user interaction, based on state vars
2215 * call init(), apply(), exit()
2216 *
2217 * invoke() gets called on mouse click in action-widget
2218 * call init(), add modal handler
2219 * call apply() with initial motion
2220 *
2221 * modal() accept modal events while doing it
2222 * call move-areas code with delta motion
2223 * call exit() or cancel() and remove handler
2224 */
2225
2227 int origval; /* for move areas */
2228 int bigger, smaller; /* constraints for moving new edge */
2229 int delta; /* delta move edge */
2230 int origmin, origsize; /* to calculate fac, for property storage */
2231 int previewmode; /* draw preview-line, then split. */
2232 void *draw_callback; /* call `screen_draw_split_preview` */
2234
2235 ScrEdge *nedge; /* new edge */
2236 ScrArea *sarea; /* start area */
2237 ScrArea *narea; /* new area */
2238};
2239
2240static bool area_split_allowed(const ScrArea *area, const eScreenAxis dir_axis)
2241{
2242 if (!area || area->global) {
2243 /* Must be a non-global area. */
2244 return false;
2245 }
2246
2247 if ((dir_axis == SCREEN_AXIS_V && area->winx <= 2 * AREAMINX * UI_SCALE_FAC) ||
2248 (dir_axis == SCREEN_AXIS_H && area->winy <= 2 * ED_area_headersize()))
2249 {
2250 /* Must be at least double minimum sizes to split into two. */
2251 return false;
2252 }
2253
2254 return true;
2255}
2256
2257static void area_split_draw_cb(const wmWindow * /*win*/, void *userdata)
2258{
2259 const wmOperator *op = static_cast<const wmOperator *>(userdata);
2260
2261 sAreaSplitData *sd = static_cast<sAreaSplitData *>(op->customdata);
2262 const eScreenAxis dir_axis = eScreenAxis(RNA_enum_get(op->ptr, "direction"));
2263
2264 if (area_split_allowed(sd->sarea, dir_axis)) {
2265 float fac = RNA_float_get(op->ptr, "factor");
2266 screen_draw_split_preview(sd->sarea, dir_axis, fac);
2267 }
2268}
2269
2270/* generic init, menu case, doesn't need active area */
2272{
2273 /* custom data */
2274 sAreaSplitData *sd = MEM_callocN<sAreaSplitData>("op_area_split");
2275 op->customdata = sd;
2276
2277 sd->sarea = CTX_wm_area(C);
2278
2279 return true;
2280}
2281
2282/* generic init, no UI stuff here, assumes active area */
2284{
2285 ScrArea *area = CTX_wm_area(C);
2286
2287 /* required context */
2288 if (area == nullptr) {
2289 return false;
2290 }
2291
2292 /* required properties */
2293 const eScreenAxis dir_axis = eScreenAxis(RNA_enum_get(op->ptr, "direction"));
2294
2295 /* custom data */
2296 sAreaSplitData *sd = MEM_callocN<sAreaSplitData>("op_area_split");
2297 op->customdata = sd;
2298
2299 sd->sarea = area;
2300 if (dir_axis == SCREEN_AXIS_V) {
2301 sd->origmin = area->v1->vec.x;
2302 sd->origsize = area->v4->vec.x - sd->origmin;
2303 }
2304 else {
2305 sd->origmin = area->v1->vec.y;
2306 sd->origsize = area->v2->vec.y - sd->origmin;
2307 }
2308
2309 return true;
2310}
2311
2312/* with area as center, sb is located at: 0=W, 1=N, 2=E, 3=S */
2313/* used with split operator */
2315{
2316 ScrVert *sav1 = area->v1;
2317 ScrVert *sav2 = area->v2;
2318 ScrVert *sav3 = area->v3;
2319 ScrVert *sav4 = area->v4;
2320 ScrVert *sbv1 = sb->v1;
2321 ScrVert *sbv2 = sb->v2;
2322 ScrVert *sbv3 = sb->v3;
2323 ScrVert *sbv4 = sb->v4;
2324
2325 if (sav1 == sbv4 && sav2 == sbv3) { /* Area to right of sb = W. */
2326 return BKE_screen_find_edge(screen, sav1, sav2);
2327 }
2328 if (sav2 == sbv1 && sav3 == sbv4) { /* Area to bottom of sb = N. */
2329 return BKE_screen_find_edge(screen, sav2, sav3);
2330 }
2331 if (sav3 == sbv2 && sav4 == sbv1) { /* Area to left of sb = E. */
2332 return BKE_screen_find_edge(screen, sav3, sav4);
2333 }
2334 if (sav1 == sbv2 && sav4 == sbv3) { /* Area on top of sb = S. */
2335 return BKE_screen_find_edge(screen, sav1, sav4);
2336 }
2337
2338 return nullptr;
2339}
2340
2341/* do the split, return success */
2343{
2344 const wmWindow *win = CTX_wm_window(C);
2345 bScreen *screen = CTX_wm_screen(C);
2347
2348 float fac = RNA_float_get(op->ptr, "factor");
2349 const eScreenAxis dir_axis = eScreenAxis(RNA_enum_get(op->ptr, "direction"));
2350
2351 if (!area_split_allowed(sd->sarea, dir_axis)) {
2352 return false;
2353 }
2354
2355 sd->narea = area_split(win, screen, sd->sarea, dir_axis, fac, false); /* false = no merge */
2356
2357 if (sd->narea == nullptr) {
2358 return false;
2359 }
2360
2361 sd->nedge = area_findsharededge(screen, sd->sarea, sd->narea);
2362
2363 /* select newly created edge, prepare for moving edge */
2364 ED_screen_verts_iter(win, screen, sv)
2365 {
2366 sv->editflag = 0;
2367 }
2368
2369 sd->nedge->v1->editflag = 1;
2370 sd->nedge->v2->editflag = 1;
2371
2372 if (dir_axis == SCREEN_AXIS_H) {
2373 sd->origval = sd->nedge->v1->vec.y;
2374 }
2375 else {
2376 sd->origval = sd->nedge->v1->vec.x;
2377 }
2378
2381
2383 /* Update preview thumbnail */
2384 BKE_icon_changed(screen->id.icon_id);
2385
2386 /* We have more than one area now, so reset window title. */
2388
2389 return true;
2390}
2391
2393{
2394 if (op->customdata) {
2396 if (sd->sarea) {
2398 }
2399 if (sd->narea) {
2401 }
2402
2403 if (sd->draw_callback) {
2405 }
2406
2407 MEM_freeN(sd);
2408 op->customdata = nullptr;
2409 }
2410
2413 ED_workspace_status_text(C, nullptr);
2414
2415 /* this makes sure aligned edges will result in aligned grabbing */
2418
2420}
2421
2423{
2425 const eScreenAxis dir_axis = eScreenAxis(RNA_enum_get(op->ptr, "direction"));
2426 if (area_split_allowed(sd->sarea, dir_axis)) {
2429 }
2430 else {
2432 }
2433}
2434
2435/* UI callback, adds new handler */
2437{
2438 wmWindow *win = CTX_wm_window(C);
2439 bScreen *screen = CTX_wm_screen(C);
2440
2441 /* no full window splitting allowed */
2442 BLI_assert(screen->state == SCREENNORMAL);
2443
2444 PropertyRNA *prop_dir = RNA_struct_find_property(op->ptr, "direction");
2445 PropertyRNA *prop_factor = RNA_struct_find_property(op->ptr, "factor");
2446 PropertyRNA *prop_cursor = RNA_struct_find_property(op->ptr, "cursor");
2447
2448 eScreenAxis dir_axis;
2449 if (event->type == EVT_ACTIONZONE_AREA) {
2450 sActionzoneData *sad = static_cast<sActionzoneData *>(event->customdata);
2451
2452 if (sad == nullptr || sad->modifier > 0) {
2453 return OPERATOR_PASS_THROUGH;
2454 }
2455
2456 /* verify *sad itself */
2457 if (sad->sa1 == nullptr || sad->az == nullptr) {
2458 return OPERATOR_PASS_THROUGH;
2459 }
2460
2461 /* is this our *sad? if areas not equal it should be passed on */
2462 if (CTX_wm_area(C) != sad->sa1 || sad->sa1 != sad->sa2) {
2463 return OPERATOR_PASS_THROUGH;
2464 }
2465
2466 /* The factor will be close to 1.0f when near the top-left and the bottom-right corners. */
2467 const float factor_v = float(event->xy[1] - sad->sa1->v1->vec.y) / float(sad->sa1->winy);
2468 const float factor_h = float(event->xy[0] - sad->sa1->v1->vec.x) / float(sad->sa1->winx);
2469 const bool is_left = factor_v < 0.5f;
2470 const bool is_bottom = factor_h < 0.5f;
2471 const bool is_right = !is_left;
2472 const bool is_top = !is_bottom;
2473 float factor;
2474
2475 /* Prepare operator state vars. */
2477 dir_axis = SCREEN_AXIS_H;
2478 factor = factor_h;
2479 }
2480 else {
2481 dir_axis = SCREEN_AXIS_V;
2482 factor = factor_v;
2483 }
2484
2485 if ((is_top && is_left) || (is_bottom && is_right)) {
2486 factor = 1.0f - factor;
2487 }
2488
2489 RNA_property_float_set(op->ptr, prop_factor, factor);
2490
2491 RNA_property_enum_set(op->ptr, prop_dir, dir_axis);
2492
2493 /* general init, also non-UI case, adds customdata, sets area and defaults */
2494 if (!area_split_init(C, op)) {
2495 return OPERATOR_PASS_THROUGH;
2496 }
2497 }
2498 else if (RNA_property_is_set(op->ptr, prop_dir)) {
2499 ScrArea *area = CTX_wm_area(C);
2500 if (area == nullptr) {
2501 return OPERATOR_CANCELLED;
2502 }
2503 dir_axis = eScreenAxis(RNA_property_enum_get(op->ptr, prop_dir));
2504 if (dir_axis == SCREEN_AXIS_H) {
2506 op->ptr, prop_factor, float(event->xy[0] - area->v1->vec.x) / float(area->winx));
2507 }
2508 else {
2510 op->ptr, prop_factor, float(event->xy[1] - area->v1->vec.y) / float(area->winy));
2511 }
2512
2513 if (!area_split_init(C, op)) {
2514 return OPERATOR_CANCELLED;
2515 }
2516 }
2517 else {
2518 int event_co[2];
2519
2520 /* retrieve initial mouse coord, so we can find the active edge */
2521 if (RNA_property_is_set(op->ptr, prop_cursor)) {
2522 RNA_property_int_get_array(op->ptr, prop_cursor, event_co);
2523 }
2524 else {
2525 copy_v2_v2_int(event_co, event->xy);
2526 }
2527
2528 rcti window_rect;
2529 WM_window_rect_calc(win, &window_rect);
2530
2532 AREAMAP_FROM_SCREEN(screen), &window_rect, event_co[0], event_co[1]);
2533 if (actedge == nullptr) {
2534 return OPERATOR_CANCELLED;
2535 }
2536
2538
2539 RNA_property_enum_set(op->ptr, prop_dir, dir_axis);
2540
2541 /* special case, adds customdata, sets defaults */
2542 if (!area_split_menu_init(C, op)) {
2543 return OPERATOR_CANCELLED;
2544 }
2545 }
2546
2548
2549 if (event->type == EVT_ACTIONZONE_AREA) {
2550 /* do the split */
2551 if (area_split_apply(C, op)) {
2552 area_move_set_limits(win, screen, dir_axis, &sd->bigger, &sd->smaller, nullptr);
2553
2554 /* add temp handler for edge move or cancel */
2557
2559 }
2560 }
2561 else {
2562 sd->previewmode = 1;
2564 /* add temp handler for edge move or cancel */
2567
2569 }
2570
2571 return OPERATOR_PASS_THROUGH;
2572}
2573
2574/* function to be called outside UI context, or for redo */
2576{
2577 if (!area_split_init(C, op)) {
2578 return OPERATOR_CANCELLED;
2579 }
2580
2581 area_split_apply(C, op);
2582 area_split_exit(C, op);
2583
2584 return OPERATOR_FINISHED;
2585}
2586
2588{
2590
2591 if (sd->previewmode) {
2592 /* pass */
2593 }
2594 else {
2595 if (screen_area_join(C, op->reports, CTX_wm_screen(C), sd->sarea, sd->narea)) {
2596 if (CTX_wm_area(C) == sd->narea) {
2597 CTX_wm_area_set(C, nullptr);
2598 CTX_wm_region_set(C, nullptr);
2599 }
2600 sd->narea = nullptr;
2601 }
2602 }
2603 area_split_exit(C, op);
2604}
2605
2607{
2609 PropertyRNA *prop_dir = RNA_struct_find_property(op->ptr, "direction");
2610 bool update_factor = false;
2611
2612 /* execute the events */
2613 switch (event->type) {
2614 case MOUSEMOVE:
2615 update_factor = true;
2616 break;
2617
2618 case LEFTMOUSE:
2619 if (sd->previewmode) {
2620 area_split_apply(C, op);
2621 area_split_exit(C, op);
2622 return OPERATOR_FINISHED;
2623 }
2624 else {
2625 if (event->val == KM_RELEASE) { /* mouse up */
2626 area_split_exit(C, op);
2627 return OPERATOR_FINISHED;
2628 }
2629 }
2630 break;
2631
2632 case MIDDLEMOUSE:
2633 case EVT_TABKEY:
2634 if (sd->previewmode == 0) {
2635 /* pass */
2636 }
2637 else {
2638 if (event->val == KM_PRESS) {
2639 if (sd->sarea) {
2640 const eScreenAxis dir_axis = eScreenAxis(RNA_property_enum_get(op->ptr, prop_dir));
2642 op->ptr, prop_dir, (dir_axis == SCREEN_AXIS_V) ? SCREEN_AXIS_H : SCREEN_AXIS_V);
2644 update_factor = true;
2645 }
2646 }
2647 }
2648
2649 break;
2650
2651 case RIGHTMOUSE: /* cancel operation */
2652 case EVT_ESCKEY:
2653 area_split_cancel(C, op);
2654 return OPERATOR_CANCELLED;
2655
2656 case EVT_LEFTCTRLKEY:
2657 case EVT_RIGHTCTRLKEY:
2658 sd->do_snap = event->val == KM_PRESS;
2659 update_factor = true;
2660 break;
2661 default: {
2662 break;
2663 }
2664 }
2665
2666 if (update_factor) {
2667 const eScreenAxis dir_axis = eScreenAxis(RNA_property_enum_get(op->ptr, prop_dir));
2668
2669 sd->delta = (dir_axis == SCREEN_AXIS_V) ? event->xy[0] - sd->origval :
2670 event->xy[1] - sd->origval;
2671
2672 if (sd->previewmode == 0) {
2673 if (sd->do_snap) {
2674 const int snap_loc = area_snap_calc_location(CTX_wm_screen(C),
2676 sd->delta,
2677 sd->origval,
2678 dir_axis,
2679 sd->bigger,
2680 sd->smaller);
2681 sd->delta = snap_loc - sd->origval;
2683 sd->delta,
2684 sd->origval,
2685 dir_axis,
2686 sd->bigger,
2687 sd->smaller,
2689 }
2690 else {
2692 C, sd->delta, sd->origval, dir_axis, sd->bigger, sd->smaller, SNAP_NONE);
2693 }
2694 }
2695 else {
2696 if (sd->sarea) {
2698 }
2699
2701
2702 /* area context not set */
2704
2705 if (sd->sarea) {
2706 ScrArea *area = sd->sarea;
2707 if (dir_axis == SCREEN_AXIS_V) {
2708 sd->origmin = area->v1->vec.x;
2709 sd->origsize = area->v4->vec.x - sd->origmin;
2710 }
2711 else {
2712 sd->origmin = area->v1->vec.y;
2713 sd->origsize = area->v2->vec.y - sd->origmin;
2714 }
2715
2716 if (sd->do_snap) {
2717 area->v1->editflag = area->v2->editflag = area->v3->editflag = area->v4->editflag = 1;
2718
2719 const int snap_loc = area_snap_calc_location(CTX_wm_screen(C),
2721 sd->delta,
2722 sd->origval,
2723 dir_axis,
2724 sd->origmin + sd->origsize,
2725 -sd->origmin);
2726
2727 area->v1->editflag = area->v2->editflag = area->v3->editflag = area->v4->editflag = 0;
2728 sd->delta = snap_loc - sd->origval;
2729 }
2730
2732 }
2733
2734 CTX_wm_screen(C)->do_draw = true;
2735 }
2736
2737 float fac = float(sd->delta + sd->origval - sd->origmin) / sd->origsize;
2738 RNA_float_set(op->ptr, "factor", fac);
2739 }
2740
2742}
2743
2745 {SCREEN_AXIS_H, "HORIZONTAL", 0, "Horizontal", ""},
2746 {SCREEN_AXIS_V, "VERTICAL", 0, "Vertical", ""},
2747 {0, nullptr, 0, nullptr, nullptr},
2748};
2749
2751{
2752 ot->name = "Split Area";
2753 ot->description = "Split selected area into new windows";
2754 ot->idname = "SCREEN_OT_area_split";
2755
2756 ot->exec = area_split_exec;
2757 ot->invoke = area_split_invoke;
2758 ot->modal = area_split_modal;
2759 ot->cancel = area_split_cancel;
2760
2761 ot->poll = screen_active_editable;
2762
2763 /* flags */
2765
2766 /* rna */
2767 RNA_def_enum(ot->srna, "direction", prop_direction_items, SCREEN_AXIS_H, "Direction", "");
2768 RNA_def_float(ot->srna, "factor", 0.5f, 0.0, 1.0, "Factor", "", 0.0, 1.0);
2770 ot->srna, "cursor", 2, nullptr, INT_MIN, INT_MAX, "Cursor", "", INT_MIN, INT_MAX);
2771}
2772
2774
2775/* -------------------------------------------------------------------- */
2778
2790
2791static bool is_split_edge(const int alignment, const AZEdge edge)
2792{
2793 return ((alignment == RGN_ALIGN_BOTTOM) && (edge == AE_TOP_TO_BOTTOMRIGHT)) ||
2794 ((alignment == RGN_ALIGN_TOP) && (edge == AE_BOTTOM_TO_TOPLEFT)) ||
2795 ((alignment == RGN_ALIGN_LEFT) && (edge == AE_RIGHT_TO_TOPLEFT)) ||
2796 ((alignment == RGN_ALIGN_RIGHT) && (edge == AE_LEFT_TO_TOPRIGHT));
2797}
2798
2799static void region_scale_draw_cb(const wmWindow * /*win*/, void *userdata)
2800{
2801 const wmOperator *op = static_cast<const wmOperator *>(userdata);
2802 RegionMoveData *rmd = static_cast<RegionMoveData *>(op->customdata);
2804}
2805
2807{
2808 RegionMoveData *rmd = static_cast<RegionMoveData *>(op->customdata);
2809 WM_draw_cb_exit(rmd->win, rmd->draw_callback);
2810
2811 MEM_freeN(rmd);
2812 op->customdata = nullptr;
2813
2815}
2816
2818{
2819 sActionzoneData *sad = static_cast<sActionzoneData *>(event->customdata);
2820
2821 if (event->type != EVT_ACTIONZONE_REGION) {
2822 BKE_report(op->reports, RPT_ERROR, "Can only scale region size from an action zone");
2823 return OPERATOR_CANCELLED;
2824 }
2825
2826 AZone *az = sad->az;
2827
2828 if (az->region) {
2829 RegionMoveData *rmd = MEM_callocN<RegionMoveData>("RegionMoveData");
2830
2831 op->customdata = rmd;
2832
2833 rmd->az = az;
2834 /* special case for region within region - this allows the scale of
2835 * the parent region if the azone edge is not the edge splitting
2836 * both regions */
2837 if ((az->region->alignment & RGN_SPLIT_PREV) && az->region->prev &&
2839 {
2840 rmd->region = az->region->prev;
2841 }
2842 /* Flag to always forward scaling to the previous region. */
2843 else if (az->region->prev && (az->region->alignment & RGN_SPLIT_SCALE_PREV)) {
2844 rmd->region = az->region->prev;
2845 }
2846 else {
2847 rmd->region = az->region;
2848 }
2849 rmd->area = sad->sa1;
2850 rmd->edge = az->edge;
2851 copy_v2_v2_int(rmd->orig_xy, event->xy);
2852 rmd->maxsize = ED_area_max_regionsize(rmd->area, rmd->region, rmd->edge);
2853
2854 /* if not set we do now, otherwise it uses type */
2855 if (rmd->region->sizex == 0) {
2856 rmd->region->sizex = rmd->region->winx;
2857 }
2858 if (rmd->region->sizey == 0) {
2859 rmd->region->sizey = rmd->region->winy;
2860 }
2861
2862 /* Reset our saved widths if the region is hidden.
2863 * Otherwise you can't drag it out a second time. */
2864 if (rmd->region->flag & RGN_FLAG_HIDDEN) {
2866 rmd->region->winx = rmd->region->sizex = 0;
2867 }
2868 else {
2869 rmd->region->winy = rmd->region->sizey = 0;
2870 }
2871 }
2872
2873 /* Now copy to region-move-data. */
2875 rmd->origval = rmd->region->sizex;
2876 }
2877 else {
2878 rmd->origval = rmd->region->sizey;
2879 }
2880
2881 CLAMP(rmd->maxsize, 0, 1000);
2882
2883 rmd->win = CTX_wm_window(C);
2886
2887 /* add temp handler */
2890
2892 }
2893
2894 return OPERATOR_FINISHED;
2895}
2896
2898{
2899 if ((rmd->region->flag & RGN_FLAG_HIDDEN) == 0) {
2900 short *size, maxsize = -1;
2901
2903 size = &rmd->region->sizex;
2904 }
2905 else {
2906 size = &rmd->region->sizey;
2907 }
2908
2909 maxsize = rmd->maxsize - (UI_UNIT_Y / UI_SCALE_FAC);
2910
2911 if (*size > maxsize && maxsize > 0) {
2912 *size = maxsize;
2913 }
2914 }
2915}
2916
2918{
2919 /* hidden areas may have bad 'View2D.cur' value,
2920 * correct before displaying. see #45156 */
2921 if (rmd->region->flag & RGN_FLAG_HIDDEN) {
2923 }
2924
2925 region_toggle_hidden(C, rmd->region, false);
2927
2928 if ((rmd->region->flag & RGN_FLAG_HIDDEN) == 0) {
2929 if (rmd->region->regiontype == RGN_TYPE_HEADER) {
2930 ARegion *region_tool_header = BKE_area_find_region_type(rmd->area, RGN_TYPE_TOOL_HEADER);
2931 if (region_tool_header != nullptr) {
2932 if ((region_tool_header->flag & RGN_FLAG_HIDDEN_BY_USER) == 0 &&
2933 (region_tool_header->flag & RGN_FLAG_HIDDEN) != 0)
2934 {
2935 region_toggle_hidden(C, region_tool_header, false);
2936 }
2937 }
2938 }
2939 }
2940}
2941
2943{
2944 RegionMoveData *rmd = static_cast<RegionMoveData *>(op->customdata);
2945 int delta;
2946
2947 /* execute the events */
2948 switch (event->type) {
2949 case MOUSEMOVE: {
2950 const float aspect = (rmd->region->v2d.flag & V2D_IS_INIT) ?
2951 (BLI_rctf_size_x(&rmd->region->v2d.cur) /
2952 (BLI_rcti_size_x(&rmd->region->v2d.mask) + 1)) :
2953 1.0f;
2954 const int snap_size_threshold = (U.widget_unit * 2) / aspect;
2955 bool size_changed = false;
2956
2958 delta = event->xy[0] - rmd->orig_xy[0];
2959 if (rmd->edge == AE_LEFT_TO_TOPRIGHT) {
2960 delta = -delta;
2961 }
2962
2963 /* region sizes now get multiplied */
2964 delta /= UI_SCALE_FAC;
2965
2966 const int size_no_snap = rmd->origval + delta;
2967 rmd->region->sizex = size_no_snap;
2968 /* Clamp before snapping, so the snapping doesn't use a size that's invalid anyway. It will
2969 * check for and respect the max-width too. */
2970 CLAMP(rmd->region->sizex, 0, rmd->maxsize);
2971
2972 if (rmd->region->runtime->type->snap_size) {
2973 short sizex_test = rmd->region->runtime->type->snap_size(
2974 rmd->region, rmd->region->sizex, 0);
2975 if ((abs(rmd->region->sizex - sizex_test) < snap_size_threshold) &&
2976 /* Don't snap to a new size if that would exceed the maximum width. */
2977 sizex_test <= rmd->maxsize)
2978 {
2979 rmd->region->sizex = sizex_test;
2980 }
2981 }
2982 BLI_assert(rmd->region->sizex <= rmd->maxsize);
2983
2984 if (size_no_snap < UI_UNIT_X / aspect) {
2985 rmd->region->sizex = rmd->origval;
2986 if (!(rmd->region->flag & RGN_FLAG_HIDDEN)) {
2988 }
2989 }
2990 else if (rmd->region->flag & RGN_FLAG_HIDDEN) {
2992 }
2993
2994 /* Hiding/unhiding is handled above, but still fix the size as requested. */
2995 if (rmd->region->flag & RGN_FLAG_NO_USER_RESIZE) {
2996 rmd->region->sizex = rmd->origval;
2997 }
2998
2999 if (rmd->region->sizex != rmd->origval) {
3000 size_changed = true;
3001 }
3002 }
3003 else {
3004 delta = event->xy[1] - rmd->orig_xy[1];
3005 if (rmd->edge == AE_BOTTOM_TO_TOPLEFT) {
3006 delta = -delta;
3007 }
3008
3009 /* region sizes now get multiplied */
3010 delta /= UI_SCALE_FAC;
3011
3012 const int size_no_snap = rmd->origval + delta;
3013 rmd->region->sizey = size_no_snap;
3014 /* Clamp before snapping, so the snapping doesn't use a size that's invalid anyway. It will
3015 * check for and respect the max-height too. */
3016 CLAMP(rmd->region->sizey, 0, rmd->maxsize);
3017
3018 if (rmd->region->runtime->type->snap_size) {
3019 short sizey_test = rmd->region->runtime->type->snap_size(
3020 rmd->region, rmd->region->sizey, 1);
3021 if ((abs(rmd->region->sizey - sizey_test) < snap_size_threshold) &&
3022 /* Don't snap to a new size if that would exceed the maximum height. */
3023 (sizey_test <= rmd->maxsize))
3024 {
3025 rmd->region->sizey = sizey_test;
3026 }
3027 }
3028 BLI_assert(rmd->region->sizey <= rmd->maxsize);
3029
3030 /* NOTE: `UI_UNIT_Y / 4` means you need to drag the footer and execute region
3031 * almost all the way down for it to become hidden, this is done
3032 * otherwise its too easy to do this by accident. */
3033 if (size_no_snap < (UI_UNIT_Y / 4) / aspect) {
3034 rmd->region->sizey = rmd->origval;
3035 if (!(rmd->region->flag & RGN_FLAG_HIDDEN)) {
3037 }
3038 }
3039 else if (rmd->region->flag & RGN_FLAG_HIDDEN) {
3041 }
3042
3043 /* Hiding/unhiding is handled above, but still fix the size as requested. */
3044 if (rmd->region->flag & RGN_FLAG_NO_USER_RESIZE) {
3045 rmd->region->sizey = rmd->origval;
3046 }
3047
3048 if (rmd->region->sizey != rmd->origval) {
3049 size_changed = true;
3050 }
3051 }
3052 if (size_changed && rmd->region->runtime->type->on_user_resize) {
3053 rmd->region->runtime->type->on_user_resize(rmd->region);
3054 }
3055 if (size_changed) {
3058 }
3059 else {
3061 }
3062 }
3065
3066 break;
3067 }
3068 case LEFTMOUSE:
3069 if (event->val == KM_RELEASE) {
3070 if (len_manhattan_v2v2_int(event->xy, rmd->orig_xy) <= WM_event_drag_threshold(event)) {
3071 if (rmd->region->flag & RGN_FLAG_HIDDEN) {
3073 }
3074 else if (rmd->region->flag & RGN_FLAG_TOO_SMALL) {
3076 }
3077
3080 }
3081
3083
3084 return OPERATOR_FINISHED;
3085 }
3086 break;
3087
3088 case EVT_ESCKEY:
3091 return OPERATOR_CANCELLED;
3092 default: {
3093 break;
3094 }
3095 }
3096
3098}
3099
3100static void region_scale_cancel(bContext * /*C*/, wmOperator *op)
3101{
3103}
3104
3106{
3107 /* identifiers */
3108 ot->name = "Scale Region Size";
3109 ot->description = "Scale selected area";
3110 ot->idname = "SCREEN_OT_region_scale";
3111
3112 ot->invoke = region_scale_invoke;
3113 ot->modal = region_scale_modal;
3114 ot->cancel = region_scale_cancel;
3115
3116 ot->poll = ED_operator_areaactive;
3117
3118 /* flags */
3120}
3121
3123
3124/* -------------------------------------------------------------------- */
3127
3129 eRegion_Type regiontype)
3130{
3131 return (regiontype == RGN_TYPE_WINDOW &&
3133 (spacetype == SPACE_CLIP && regiontype == RGN_TYPE_PREVIEW);
3134}
3135
3136void ED_areas_do_frame_follow(bContext *C, bool center_view)
3137{
3138 bScreen *screen_ctx = CTX_wm_screen(C);
3139 if (!(screen_ctx->redraws_flag & TIME_FOLLOW)) {
3140 return;
3141 }
3142
3143 const int current_frame = CTX_data_scene(C)->r.cfra;
3145 LISTBASE_FOREACH (wmWindow *, window, &wm->windows) {
3146 const bScreen *screen = WM_window_get_active_screen(window);
3147
3148 LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
3149 LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
3150 /* Only frame/center the current-frame indicator here if editor type supports it */
3152 eRegion_Type(region->regiontype)))
3153 {
3154 continue;
3155 }
3156
3157 if ((current_frame >= region->v2d.cur.xmin) && (current_frame <= region->v2d.cur.xmax)) {
3158 /* The current-frame indicator is already in view, do nothing. */
3159 continue;
3160 }
3161
3162 const float w = BLI_rctf_size_x(&region->v2d.cur);
3163
3164 if (center_view) {
3165 region->v2d.cur.xmax = current_frame + (w / 2);
3166 region->v2d.cur.xmin = current_frame - (w / 2);
3167 continue;
3168 }
3169 if (current_frame < region->v2d.cur.xmin) {
3170 region->v2d.cur.xmax = current_frame;
3171 region->v2d.cur.xmin = region->v2d.cur.xmax - w;
3172 }
3173 else {
3174 region->v2d.cur.xmin = current_frame;
3175 region->v2d.cur.xmax = region->v2d.cur.xmin + w;
3176 }
3177 }
3178 }
3179 }
3180}
3181
3182/* function to be called outside UI context, or for redo */
3184{
3185 Scene *scene = CTX_data_scene(C);
3186
3187 int delta = RNA_int_get(op->ptr, "delta");
3188
3189 /* In order to jump from e.g. 1.5 to 1 the delta needs to be incremented by 1 since the sub-frame
3190 * is always zeroed. Otherwise it would jump to 0. */
3191 if (delta < 0 && scene->r.subframe > 0) {
3192 delta += 1;
3193 }
3194 scene->r.cfra += delta;
3196 scene->r.subframe = 0.0f;
3197
3199
3201
3203
3204 return OPERATOR_FINISHED;
3205}
3206
3208{
3209 ot->name = "Frame Offset";
3210 ot->idname = "SCREEN_OT_frame_offset";
3211 ot->description = "Move current frame forward/backward by a given number";
3212
3213 ot->exec = frame_offset_exec;
3214
3216 ot->flag = OPTYPE_UNDO_GROUPED;
3217 ot->undo_group = "Frame Change";
3218
3219 /* rna */
3220 RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
3221}
3222
3224
3225/* -------------------------------------------------------------------- */
3228
3229/* function to be called outside UI context, or for redo */
3231{
3232 Scene *scene = CTX_data_scene(C);
3233 wmTimer *animtimer = CTX_wm_screen(C)->animtimer;
3234
3235 /* Don't change scene->r.cfra directly if animtimer is running as this can cause
3236 * first/last frame not to be actually shown (bad since for example physics
3237 * simulations aren't reset properly).
3238 */
3239 if (animtimer) {
3240 ScreenAnimData *sad = static_cast<ScreenAnimData *>(animtimer->customdata);
3241
3243
3244 if (RNA_boolean_get(op->ptr, "end")) {
3245 sad->nextfra = PEFRA;
3246 }
3247 else {
3248 sad->nextfra = PSFRA;
3249 }
3250 }
3251 else {
3252 if (RNA_boolean_get(op->ptr, "end")) {
3253 scene->r.cfra = PEFRA;
3254 }
3255 else {
3256 scene->r.cfra = PSFRA;
3257 }
3258
3260
3262
3264 }
3265
3266 return OPERATOR_FINISHED;
3267}
3268
3270{
3271 ot->name = "Jump to Endpoint";
3272 ot->description = "Jump to first/last frame in frame range";
3273 ot->idname = "SCREEN_OT_frame_jump";
3274
3275 ot->exec = frame_jump_exec;
3276
3278 ot->flag = OPTYPE_UNDO_GROUPED;
3279 ot->undo_group = "Frame Change";
3280
3281 /* rna */
3283 ot->srna, "end", false, "Last Frame", "Jump to the last frame of the frame range");
3284}
3285
3287
3288/* -------------------------------------------------------------------- */
3291
3293{
3294 bAnimContext ac;
3295
3296 if (ANIM_animdata_get_context(&C, &ac) == 0) {
3297 return;
3298 }
3300 summary_to_keylist(&ac, &keylist, 0, {-FLT_MAX, FLT_MAX});
3301}
3302
3304{
3305 bAnimContext ac;
3306
3307 if (ANIM_animdata_get_context(&C, &ac) == 0) {
3308 return;
3309 }
3310
3312
3313 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
3314 FCurve *fcu = static_cast<FCurve *>(ale->key_data);
3315 if (!fcu->bezt) {
3316 continue;
3317 }
3318
3319 const bool use_nla_mapping = ANIM_nla_mapping_allowed(ale);
3320 fcurve_to_keylist(ale->adt, fcu, &keylist, 0, {-FLT_MAX, FLT_MAX}, use_nla_mapping);
3321 }
3322
3323 ANIM_animdata_freelist(&anim_data);
3324}
3325
3326/* This is used for all editors where a more specific function isn't implemented. */
3328{
3329 bDopeSheet ads = {nullptr};
3330 Scene *scene = CTX_data_scene(&C);
3331
3332 /* Speed up dummy dope-sheet context with flags to perform necessary filtering. */
3333 if ((scene->flag & SCE_KEYS_NO_SELONLY) == 0) {
3334 /* Only selected channels are included. */
3336 }
3337
3338 /* populate tree with keyframe nodes */
3339 scene_to_keylist(&ads, scene, &keylist, 0, {-FLT_MAX, FLT_MAX});
3340
3342 if (ob) {
3343 ob_to_keylist(&ads, ob, &keylist, 0, {-FLT_MAX, FLT_MAX});
3344
3345 if (ob->type == OB_GREASE_PENCIL) {
3346 const bool active_layer_only = !(scene->flag & SCE_KEYS_NO_SELONLY);
3348 nullptr, static_cast<const GreasePencil *>(ob->data), &keylist, 0, active_layer_only);
3349 }
3350 }
3351
3352 {
3354 if (mask) {
3356 mask_to_keylist(&ads, masklay, &keylist);
3357 }
3358 }
3359}
3360
3361/* function to be called outside UI context, or for redo */
3363{
3364 Scene *scene = CTX_data_scene(C);
3365 const bool next = RNA_boolean_get(op->ptr, "next");
3366 bool done = false;
3367
3368 /* sanity checks */
3369 if (scene == nullptr) {
3370 return OPERATOR_CANCELLED;
3371 }
3372
3373 AnimKeylist *keylist = ED_keylist_create();
3374
3375 ScrArea *area = CTX_wm_area(C);
3376 switch (area ? eSpace_Type(area->spacetype) : SPACE_EMPTY) {
3377 case SPACE_ACTION: {
3378 keylist_from_dopesheet(*C, *keylist);
3379 break;
3380 }
3381
3382 case SPACE_GRAPH:
3383 keylist_from_graph_editor(*C, *keylist);
3384 break;
3385
3386 default:
3388 break;
3389 }
3390
3391 /* Initialize binary-tree-list for getting keyframes. */
3393
3394 const float cfra = BKE_scene_frame_get(scene);
3395 /* find matching keyframe in the right direction */
3396 const ActKeyColumn *ak;
3397
3398 if (next) {
3399 ak = ED_keylist_find_next(keylist, cfra);
3400 while ((ak != nullptr) && (done == false)) {
3401 if (cfra < ak->cfra) {
3402 BKE_scene_frame_set(scene, ak->cfra);
3403 done = true;
3404 }
3405 else {
3406 ak = ak->next;
3407 }
3408 }
3409 }
3410
3411 else {
3412 ak = ED_keylist_find_prev(keylist, cfra);
3413 while ((ak != nullptr) && (done == false)) {
3414 if (cfra > ak->cfra) {
3415 BKE_scene_frame_set(scene, ak->cfra);
3416 done = true;
3417 }
3418 else {
3419 ak = ak->prev;
3420 }
3421 }
3422 }
3423
3424 /* free temp stuff */
3425 ED_keylist_free(keylist);
3426
3427 /* any success? */
3428 if (done == false) {
3429 BKE_report(op->reports, RPT_INFO, "No more keyframes to jump to in this direction");
3430
3431 return OPERATOR_CANCELLED;
3432 }
3433
3435
3437
3439
3440 return OPERATOR_FINISHED;
3441}
3442
3444{
3446}
3447
3449{
3450 ot->name = "Jump to Keyframe";
3451 ot->description = "Jump to previous/next keyframe";
3452 ot->idname = "SCREEN_OT_keyframe_jump";
3453
3454 ot->exec = keyframe_jump_exec;
3455
3456 ot->poll = keyframe_jump_poll;
3457 ot->flag = OPTYPE_UNDO_GROUPED;
3458 ot->undo_group = "Frame Change";
3459
3460 /* properties */
3461 RNA_def_boolean(ot->srna, "next", true, "Next Keyframe", "");
3462}
3463
3465
3466/* -------------------------------------------------------------------- */
3469
3470/* function to be called outside UI context, or for redo */
3472{
3473 Scene *scene = CTX_data_scene(C);
3474 int closest = scene->r.cfra;
3475 const bool next = RNA_boolean_get(op->ptr, "next");
3476 bool found = false;
3477
3478 /* find matching marker in the right direction */
3479 LISTBASE_FOREACH (TimeMarker *, marker, &scene->markers) {
3480 if (next) {
3481 if ((marker->frame > scene->r.cfra) && (!found || closest > marker->frame)) {
3482 closest = marker->frame;
3483 found = true;
3484 }
3485 }
3486 else {
3487 if ((marker->frame < scene->r.cfra) && (!found || closest < marker->frame)) {
3488 closest = marker->frame;
3489 found = true;
3490 }
3491 }
3492 }
3493
3494 /* any success? */
3495 if (!found) {
3496 BKE_report(op->reports, RPT_INFO, "No more markers to jump to in this direction");
3497
3498 return OPERATOR_CANCELLED;
3499 }
3500
3501 scene->r.cfra = closest;
3502
3504
3506
3508
3509 return OPERATOR_FINISHED;
3510}
3511
3513{
3514 ot->name = "Jump to Marker";
3515 ot->description = "Jump to previous/next marker";
3516 ot->idname = "SCREEN_OT_marker_jump";
3517
3518 ot->exec = marker_jump_exec;
3519
3521 ot->flag = OPTYPE_UNDO_GROUPED;
3522 ot->undo_group = "Frame Change";
3523
3524 /* properties */
3525 RNA_def_boolean(ot->srna, "next", true, "Next Marker", "");
3526}
3527
3529
3530/* -------------------------------------------------------------------- */
3533
3534/* function to be called outside UI context, or for redo */
3536{
3537 WorkSpace *workspace = CTX_wm_workspace(C);
3538 int delta = RNA_int_get(op->ptr, "delta");
3539
3540 if (ED_workspace_layout_cycle(workspace, delta, C)) {
3541 return OPERATOR_FINISHED;
3542 }
3543
3544 return OPERATOR_CANCELLED;
3545}
3546
3548{
3549 ot->name = "Set Screen";
3550 ot->description = "Cycle through available screens";
3551 ot->idname = "SCREEN_OT_screen_set";
3552
3553 ot->exec = screen_set_exec;
3555
3556 /* rna */
3557 RNA_def_int(ot->srna, "delta", 1, -1, 1, "Delta", "", -1, 1);
3558}
3559
3561
3562/* -------------------------------------------------------------------- */
3565
3566/* function to be called outside UI context, or for redo */
3568{
3569 bScreen *screen = CTX_wm_screen(C);
3570 ScrArea *area = nullptr;
3571 const bool hide_panels = RNA_boolean_get(op->ptr, "use_hide_panels");
3572
3573 BLI_assert(!screen->temp);
3574
3575 /* search current screen for 'full-screen' areas */
3576 /* prevents restoring info header, when mouse is over it */
3577 LISTBASE_FOREACH (ScrArea *, area_iter, &screen->areabase) {
3578 if (area_iter->full) {
3579 area = area_iter;
3580 break;
3581 }
3582 }
3583
3584 if (area == nullptr) {
3585 area = CTX_wm_area(C);
3586 }
3587
3588 if (hide_panels) {
3589 if (!ELEM(screen->state, SCREENNORMAL, SCREENFULL)) {
3590 return OPERATOR_CANCELLED;
3591 }
3593 }
3594 else {
3595 if (!ELEM(screen->state, SCREENNORMAL, SCREENMAXIMIZED)) {
3596 return OPERATOR_CANCELLED;
3597 }
3599 }
3600
3601 return OPERATOR_FINISHED;
3602}
3603
3605{
3606 const wmWindow *win = CTX_wm_window(C);
3607 const bScreen *screen = CTX_wm_screen(C);
3608 const ScrArea *area = CTX_wm_area(C);
3609 const wmWindowManager *wm = CTX_wm_manager(C);
3610 return ED_operator_areaactive(C) &&
3611 /* Don't allow maximizing global areas but allow minimizing from them. */
3612 ((screen->state != SCREENNORMAL) || !ED_area_is_global(area)) &&
3613 /* Don't change temporary screens. */
3615 /* Don't maximize when dragging. */
3617}
3618
3620{
3621 PropertyRNA *prop;
3622
3623 ot->name = "Toggle Maximize Area";
3624 ot->description = "Toggle display selected area as fullscreen/maximized";
3625 ot->idname = "SCREEN_OT_screen_full_area";
3626
3629 ot->flag = 0;
3630
3631 prop = RNA_def_boolean(ot->srna, "use_hide_panels", false, "Hide Panels", "Hide all the panels");
3633}
3634
3636
3637/* -------------------------------------------------------------------- */
3640
3641/* operator state vars used:
3642 * x1, y1 mouse coord in first area, which will disappear
3643 * x2, y2 mouse coord in 2nd area, which will become joined
3644 *
3645 * functions:
3646 *
3647 * init() find edge based on state vars
3648 * test if the edge divides two areas,
3649 * store active and nonactive area,
3650 *
3651 * apply() do the actual join
3652 *
3653 * exit() cleanup, send notifier
3654 *
3655 * callbacks:
3656 *
3657 * exec() calls init, apply, exit
3658 *
3659 * invoke() sets mouse coords in x,y
3660 * call init()
3661 * add modal handler
3662 *
3663 * modal() accept modal events while doing it
3664 * call apply() with active window and nonactive window
3665 * call exit() and remove handler when LMB confirm
3666 */
3667
3669 ScrArea *sa1; /* Potential source area (kept). */
3670 ScrArea *sa2; /* Potential target area (removed or reduced). */
3671 eScreenDir dir; /* Direction of potential join. */
3672 eScreenAxis split_dir; /* Direction of split within the source area. */
3673 AreaDockTarget dock_target; /* Position within target we are pointing to. */
3674 float factor; /* dock target size can vary. */
3675 int start_x, start_y; /* Starting mouse position. */
3676 int current_x, current_y; /* Current mouse position. */
3677 float split_fac; /* Split factor in split_dir direction. */
3678 wmWindow *win1; /* Window of source area. */
3679 wmWindow *win2; /* Window of the target area. */
3680 wmWindow *draw_dock_win; /* Window getting docking highlight. */
3681 bool close_win; /* Close the source window when done. */
3682 void *draw_callback; /* call #screen_draw_join_highlight */
3683 void *draw_dock_callback; /* call #screen_draw_dock_highlight, overlay on draw_dock_win. */
3684};
3685
3686static void area_join_draw_cb(const wmWindow *win, void *userdata)
3687{
3688 const wmOperator *op = static_cast<const wmOperator *>(userdata);
3689 sAreaJoinData *sd = static_cast<sAreaJoinData *>(op->customdata);
3690 if (!sd || !sd->sa1) {
3691 return;
3692 }
3693
3694 if (sd->sa1 == sd->sa2) {
3696 }
3697 else {
3698 screen_draw_join_highlight(win, sd->sa1, sd->sa2, sd->dir);
3699 }
3700}
3701
3702static void area_join_dock_cb(const wmWindow *win, void *userdata)
3703{
3704 const wmOperator *op = static_cast<wmOperator *>(userdata);
3705 sAreaJoinData *jd = static_cast<sAreaJoinData *>(op->customdata);
3706 if (!jd || !jd->sa2 || jd->dir != SCREEN_DIR_NONE || jd->sa1 == jd->sa2) {
3707 return;
3708 }
3710 win, jd->sa1, jd->sa2, jd->dock_target, jd->factor, jd->current_x, jd->current_y);
3711}
3712
3714{
3715 if (jd->sa2 && jd->win2 && jd->win2 != jd->draw_dock_win) {
3716 /* Change of highlight window. */
3717 if (jd->draw_dock_callback) {
3719 /* Refresh the entire window. */
3722 {
3723 ED_area_tag_redraw(area);
3724 }
3725 }
3726 if (jd->win2) {
3727 jd->draw_dock_win = jd->win2;
3729 }
3730 }
3731}
3732
3733/* validate selection inside screen, set variables OK */
3734/* return false: init failed */
3735static bool area_join_init(bContext *C, wmOperator *op, ScrArea *sa1, ScrArea *sa2)
3736{
3737 if (sa1 == nullptr && sa2 == nullptr) {
3738 /* Get areas from cursor location if not specified. */
3739 PropertyRNA *prop;
3740 int cursor[2];
3741
3742 prop = RNA_struct_find_property(op->ptr, "source_xy");
3743 if (RNA_property_is_set(op->ptr, prop)) {
3744 RNA_property_int_get_array(op->ptr, prop, cursor);
3746 }
3747
3748 prop = RNA_struct_find_property(op->ptr, "target_xy");
3749 if (RNA_property_is_set(op->ptr, prop)) {
3750 RNA_property_int_get_array(op->ptr, prop, cursor);
3752 }
3753 }
3754 if (sa1 == nullptr) {
3755 return false;
3756 }
3757
3758 sAreaJoinData *jd = MEM_callocN<sAreaJoinData>("op_area_join");
3759 jd->sa1 = sa1;
3760 jd->sa2 = sa2;
3761 jd->dir = area_getorientation(sa1, sa2);
3764
3765 op->customdata = jd;
3766 return true;
3767}
3768
3769/* apply the join of the areas (space types) */
3771{
3773 if (!jd || (jd->dir == SCREEN_DIR_NONE)) {
3774 return false;
3775 }
3776
3777 bScreen *screen = CTX_wm_screen(C);
3778
3779 if (!screen_area_join(C, op->reports, screen, jd->sa1, jd->sa2)) {
3780 return false;
3781 }
3782 if (CTX_wm_area(C) == jd->sa2) {
3783 CTX_wm_area_set(C, nullptr);
3784 CTX_wm_region_set(C, nullptr);
3785 }
3786
3787 if (BLI_listbase_is_single(&screen->areabase)) {
3788 /* Areas reduced to just one, so show nicer title. */
3790 }
3791
3792 return true;
3793}
3794
3795/* finish operation */
3797{
3799
3800 if (jd) {
3801 if (jd->draw_callback) {
3803 }
3804 if (jd->draw_dock_callback) {
3806 }
3807
3808 MEM_freeN(jd);
3809 op->customdata = nullptr;
3810 }
3811
3812 /* this makes sure aligned edges will result in aligned grabbing */
3816
3817 ED_workspace_status_text(C, nullptr);
3818
3820}
3821
3823{
3824 if (!area_join_init(C, op, nullptr, nullptr)) {
3825 return OPERATOR_CANCELLED;
3826 }
3827
3829
3830 if (jd->sa2 == nullptr || area_getorientation(jd->sa1, jd->sa2) == SCREEN_DIR_NONE) {
3831 return OPERATOR_CANCELLED;
3832 }
3833
3835
3836 area_join_apply(C, op);
3837 area_join_exit(C, op);
3839
3840 return OPERATOR_FINISHED;
3841}
3842
3843static void area_join_update_data(bContext *C, sAreaJoinData *jd, const wmEvent *event);
3844static int area_join_cursor(sAreaJoinData *jd, const wmEvent *event);
3845
3846/* interaction callback */
3848{
3849 if (event->type == EVT_ACTIONZONE_AREA) {
3850 sActionzoneData *sad = static_cast<sActionzoneData *>(event->customdata);
3851
3852 if (sad == nullptr || sad->modifier > 0 || sad->sa1 == nullptr) {
3853 return OPERATOR_PASS_THROUGH;
3854 }
3855
3856 if (!area_join_init(C, op, sad->sa1, sad->sa2)) {
3857 return OPERATOR_CANCELLED;
3858 }
3859
3861 jd->start_x = sad->x;
3862 jd->start_y = sad->y;
3864
3867 }
3868
3869 /* Launched from menu item or keyboard shortcut. */
3870 if (!area_join_init(C, op, nullptr, nullptr)) {
3871 ScrArea *sa1 = CTX_wm_area(C);
3872 if (!sa1 || ED_area_is_global(sa1) || !area_join_init(C, op, sa1, nullptr)) {
3873 return OPERATOR_CANCELLED;
3874 }
3875 }
3877 jd->sa2 = jd->sa1;
3878 jd->start_x = jd->sa1->totrct.xmin;
3879 jd->start_y = jd->sa1->totrct.ymax;
3880 jd->current_x = event->xy[0];
3881 jd->current_y = event->xy[1];
3883 WM_cursor_set(jd->win1, area_join_cursor(jd, event));
3884 area_join_update_data(C, jd, event);
3889}
3890
3891/* Apply the docking of the area. */
3893{
3895
3896 int offset1;
3897 int offset2;
3898 area_getoffsets(jd->sa1, jd->sa2, area_getorientation(jd->sa1, jd->sa2), &offset1, &offset2);
3899
3900 /* Check before making changes. */
3901 bool aligned_neighbors = (offset1 == 0 && offset2 == 0);
3902 bool same_area = (jd->sa1 == jd->sa2);
3903
3904 if (!(jd->dock_target == AreaDockTarget::Center)) {
3908
3909 float fac = jd->factor;
3911 fac = 1.0f - fac;
3912 }
3913
3914 ScrArea *newa = area_split(
3915 jd->win2, WM_window_get_active_screen(jd->win2), jd->sa2, dir, fac, true);
3916
3917 if (jd->factor <= 0.5f) {
3918 jd->sa2 = newa;
3919 }
3920 else {
3921 /* Force full rebuild. #130732 */
3922 ED_area_tag_redraw(newa);
3923 }
3924 }
3925
3926 if (same_area) {
3928 return;
3929 }
3930
3931 if (!aligned_neighbors || !screen_area_join(C, op->reports, CTX_wm_screen(C), jd->sa1, jd->sa2))
3932 {
3933 ED_area_swapspace(C, jd->sa2, jd->sa1);
3936 {
3937 jd->close_win = true;
3938 /* Clear the active region in each screen, otherwise they are pointing
3939 * at incorrect regions and will cause errors in uiTemplateInputStatus. */
3942 }
3943 else {
3945 }
3946 }
3947
3948 if (jd && jd->sa2 == CTX_wm_area(C)) {
3949 CTX_wm_area_set(C, nullptr);
3950 CTX_wm_region_set(C, nullptr);
3951 }
3952}
3953
3954static int area_join_cursor(sAreaJoinData *jd, const wmEvent *event)
3955{
3956 if (!jd->sa2 && jd->dock_target == AreaDockTarget::None) {
3957 /* Mouse outside window, so can open new window. */
3958 if (event->xy[0] < 0 || event->xy[0] > jd->win1->sizex || event->xy[1] < 1 ||
3959 event->xy[1] > jd->win1->sizey)
3960 {
3961 return WM_CURSOR_PICK_AREA;
3962 }
3963 return WM_CURSOR_STOP;
3964 }
3965
3966 if (jd->win2 && jd->win2->workspace_hook) {
3968 if (screen && screen->temp) {
3969 return WM_CURSOR_STOP;
3970 }
3971 }
3972
3973 if (jd->sa1 && jd->sa1 == jd->sa2) {
3974 if (jd->split_fac >= 0.0001f) {
3975 /* Mouse inside source area, so allow splitting. */
3977 }
3978 return WM_CURSOR_EDIT;
3979 }
3980
3981 if (jd->dir != SCREEN_DIR_NONE) {
3982 /* Joining */
3983 switch (jd->dir) {
3984 case SCREEN_DIR_N:
3985 return WM_CURSOR_N_ARROW;
3986 break;
3987 case SCREEN_DIR_S:
3988 return WM_CURSOR_S_ARROW;
3989 break;
3990 case SCREEN_DIR_W:
3991 return WM_CURSOR_W_ARROW;
3992 break;
3993 default:
3994 return WM_CURSOR_E_ARROW;
3995 }
3996 }
3997
3998 if (jd->dir != SCREEN_DIR_NONE || jd->dock_target != AreaDockTarget::None) {
3999#if defined(__APPLE__)
4000 return WM_CURSOR_HAND_CLOSED;
4001#else
4002 return WM_CURSOR_MOVE;
4003#endif
4004 }
4005
4006 return WM_CURSOR_PICK_AREA;
4007}
4008
4009static float area_docking_snap(const float pos, const wmEvent *event)
4010{
4011 const bool alt = event->modifier & KM_ALT;
4012 const bool ctrl = event->modifier & KM_CTRL;
4013 const float accel = (alt || ctrl) ? 2.5f : 2.0;
4014
4015 float factor = pos * accel;
4016
4017 if (!alt) {
4018 if (factor >= 0.4375f && factor < 0.5f) {
4019 factor = 0.499999f;
4020 }
4021 else if (factor >= 0.5f && factor < 0.5625f) {
4022 factor = 0.500001f;
4023 }
4024 }
4025
4026 if (ctrl) {
4027 if (factor < 0.1875f) {
4028 factor = 0.125f;
4029 }
4030 else if (factor >= 0.1875f && factor < 0.3125f) {
4031 factor = 0.25f;
4032 }
4033 else if (factor >= 0.3125f && factor < 0.4375f) {
4034 factor = 0.375f;
4035 }
4036 else if (factor >= 0.5625f && factor < 0.6875f) {
4037 factor = 0.625f;
4038 }
4039 else if (factor >= 0.6875f && factor < 0.8125f) {
4040 factor = 0.75f;
4041 }
4042 else if (factor > 0.8125f) {
4043 factor = 0.875f;
4044 }
4045 }
4046
4047 return factor;
4048}
4049
4051{
4052 if (!jd->sa2 || !jd->win2) {
4053 return AreaDockTarget::None;
4054 }
4055
4056 if (jd->sa1 == jd->sa2) {
4057 return AreaDockTarget::None;
4058 }
4059
4060 if (jd->win2 && jd->win2->workspace_hook) {
4062 if (screen && screen->temp) {
4063 return AreaDockTarget::None;
4064 }
4065 }
4066
4067 /* Convert to local coordinates in sa2. */
4068 int win1_posx = jd->win1->posx;
4069 int win1_posy = jd->win1->posy;
4070 int win2_posx = jd->win2->posx;
4071 int win2_posy = jd->win2->posy;
4072 WM_window_native_pixel_coords(jd->win1, &win1_posx, &win1_posy);
4073 WM_window_native_pixel_coords(jd->win2, &win2_posx, &win2_posy);
4074
4075 const int x = event->xy[0] + win1_posx - win2_posx - jd->sa2->totrct.xmin;
4076 const int y = event->xy[1] + win1_posy - win2_posy - jd->sa2->totrct.ymin;
4077
4078 jd->current_x = x + jd->sa2->totrct.xmin;
4079 jd->current_y = y + jd->sa2->totrct.ymin;
4080
4081 const float fac_x = float(x) / float(jd->sa2->winx);
4082 const float fac_y = float(y) / float(jd->sa2->winy);
4083 const int min_x = 2 * AREAMINX * UI_SCALE_FAC;
4084 const int min_y = 2 * HEADERY * UI_SCALE_FAC;
4085
4086 if (ELEM(jd->dir, SCREEN_DIR_N, SCREEN_DIR_S)) {
4087 /* Up or Down to immediate neighbor. */
4088 if (event->xy[0] <= jd->sa1->totrct.xmax && event->xy[0] >= jd->sa1->totrct.xmin) {
4089 const int join_y = std::min(jd->sa2->winy * 0.25f, 5 * HEADERY * UI_SCALE_FAC);
4090 if (jd->sa2->winy < min_y || (jd->dir == SCREEN_DIR_N && y < join_y) ||
4091 (jd->dir == SCREEN_DIR_S && (jd->sa2->winy - y) < join_y))
4092 {
4093 return AreaDockTarget::None;
4094 }
4095 }
4096 }
4097
4098 if (ELEM(jd->dir, SCREEN_DIR_W, SCREEN_DIR_E)) {
4099 /* Left or Right to immediate neighbor. */
4100 if (event->xy[1] <= jd->sa1->totrct.ymax && event->xy[1] >= jd->sa1->totrct.ymin) {
4101 const int join_x = std::min(jd->sa2->winx * 0.25f, 5 * AREAMINX * UI_SCALE_FAC);
4102 if (jd->sa2->winx < min_x || (jd->dir == SCREEN_DIR_W && (jd->sa2->winx - x) < join_x) ||
4103 (jd->dir == SCREEN_DIR_E && x < join_x))
4104 {
4105 return AreaDockTarget::None;
4106 }
4107 }
4108 }
4109
4110 /* If we've made it here, then there can be no joining possible. */
4111 jd->dir = SCREEN_DIR_NONE;
4112 jd->factor = 0.5f;
4113
4114 const float min_fac_x = 1.5f * AREAMINX * UI_SCALE_FAC / float(jd->sa2->winx);
4115 const float min_fac_y = 1.5f * HEADERY * UI_SCALE_FAC / float(jd->sa2->winy);
4116
4117 /* if the area is narrow then there are only two docking targets. */
4118 if (jd->sa2->winx < (min_x * 3)) {
4119 if (fac_y > 0.4f && fac_y < 0.6f) {
4121 }
4122 if (float(y) > float(jd->sa2->winy) / 2.0f) {
4123 jd->factor = area_docking_snap(std::max(1.0f - fac_y, min_fac_y), event);
4124 return AreaDockTarget::Top;
4125 }
4126 jd->factor = area_docking_snap(std::max(fac_y, min_fac_y), event);
4128 }
4129 if (jd->sa2->winy < (min_y * 3)) {
4130 if (fac_x > 0.4f && fac_x < 0.6f) {
4132 }
4133 if (float(x) > float(jd->sa2->winx) / 2.0f) {
4134 jd->factor = area_docking_snap(std::max(1.0f - fac_x, min_fac_x), event);
4135 return AreaDockTarget::Right;
4136 }
4137 jd->factor = area_docking_snap(std::max(fac_x, min_fac_x), event);
4138 return AreaDockTarget::Left;
4139 }
4140
4141 /* Are we in the center? But not in same area! */
4142 if (fac_x > 0.4f && fac_x < 0.6f && fac_y > 0.4f && fac_y < 0.6f) {
4144 }
4145
4146 /* Area is large enough for four docking targets. */
4147 const float area_ratio = float(jd->sa2->winx) / float(jd->sa2->winy);
4148 /* Split the area diagonally from top-right to bottom-left. */
4149 const bool upper_left = float(x) / float(y + 1) < area_ratio;
4150 /* Split the area diagonally from top-left to bottom-right. */
4151 const bool lower_left = float(x) / float(jd->sa2->winy - y + 1) < area_ratio;
4152
4153 if (upper_left && !lower_left) {
4154 jd->factor = area_docking_snap(std::max(1.0f - fac_y, min_fac_y), event);
4155 return AreaDockTarget::Top;
4156 }
4157 if (!upper_left && lower_left) {
4158 jd->factor = area_docking_snap(std::max(fac_y, min_fac_y), event);
4160 }
4161 if (upper_left && lower_left) {
4162 jd->factor = area_docking_snap(std::max(fac_x, min_fac_x), event);
4163 return AreaDockTarget::Left;
4164 }
4165 if (!upper_left && !lower_left) {
4166 jd->factor = area_docking_snap(std::max(1.0f - fac_x, min_fac_x), event);
4167 return AreaDockTarget::Right;
4168 }
4169 return AreaDockTarget::None;
4170}
4171
4172static float area_split_factor(bContext *C, sAreaJoinData *jd, const wmEvent *event)
4173{
4174 float fac = (jd->split_dir == SCREEN_AXIS_V) ?
4175 float(event->xy[0] - jd->sa1->totrct.xmin) / float(jd->sa1->winx + 1) :
4176 float(event->xy[1] - jd->sa1->totrct.ymin) / float(jd->sa1->winy + 1);
4177
4178 if (event->modifier & KM_CTRL) {
4179 /* Snapping on. */
4180
4181 /* Find nearest neighboring vertex. */
4182 const int axis = (jd->split_dir == SCREEN_AXIS_V) ? 0 : 1;
4183 int dist = INT_MAX;
4184 int loc = 0;
4185 LISTBASE_FOREACH (const ScrVert *, v1, &CTX_wm_screen(C)->vertbase) {
4186 const int v_loc = (&v1->vec.x)[axis];
4187 const int v_dist = abs(v_loc - event->xy[axis]);
4188 if (v_dist < dist) {
4189 loc = v_loc;
4190 dist = v_dist;
4191 }
4192 }
4193 float near_fac = (axis) ? float(loc - jd->sa1->totrct.ymin) / float(jd->sa1->winy + 1) :
4194 float(loc - jd->sa1->totrct.xmin) / float(jd->sa1->winx + 1);
4195
4196 /* Rounded to nearest 12th. */
4197 float frac_fac = round(fac * 12.0f) / 12.0f;
4198
4199 /* Use nearest neighbor or fractional, whichever is closest. */
4200 fac = (fabs(near_fac - fac) < fabs(frac_fac - fac)) ? near_fac : frac_fac;
4201 }
4202 else {
4203 /* Slight snap to center when no modifiers are held. */
4204 if (fac >= 0.48f && fac < 0.5f) {
4205 fac = 0.499999f;
4206 }
4207 else if (fac >= 0.5f && fac < 0.52f) {
4208 fac = 0.500001f;
4209 }
4210 }
4211
4212 /* Don't allow a new area to be created that is very small. */
4213 const float min_size = float(2.0f * ED_area_headersize());
4214 const float min_fac = min_size / ((jd->split_dir == SCREEN_AXIS_V) ? float(jd->sa1->winx + 1) :
4215 float(jd->sa1->winy + 1));
4216 if (min_fac < 0.5f) {
4217 return std::clamp(fac, min_fac, 1.0f - min_fac);
4218 }
4219 return 0.5f;
4220}
4221
4222static void area_join_update_data(bContext *C, sAreaJoinData *jd, const wmEvent *event)
4223{
4224 ScrArea *area = nullptr;
4225
4226 /* TODO: The following is needed until we have linux-specific implementations of
4227 * getWindowUnderCursor. See #130242. Use active window if there are overlapping. */
4228
4229#if (OS_WINDOWS || OS_MAC)
4231#else
4232 int win_count = 0;
4233 LISTBASE_FOREACH (wmWindow *, win, &CTX_wm_manager(C)->windows) {
4234 int cursor[2];
4235 if (wm_cursor_position_get(win, &cursor[0], &cursor[1])) {
4236 rcti rect;
4237 WM_window_rect_calc(win, &rect);
4238 if (BLI_rcti_isect_pt_v(&rect, cursor)) {
4239 win_count++;
4240 }
4241 }
4242 }
4243
4244 if (win_count > 1) {
4246 }
4247 else {
4249 }
4250#endif
4251
4253 jd->dir = SCREEN_DIR_NONE;
4255 jd->dir = area_getorientation(jd->sa1, jd->sa2);
4256 jd->dock_target = area_docking_target(jd, event);
4257
4258 if (jd->sa1 == area) {
4259 const int drag_threshold = 30 * UI_SCALE_FAC;
4260 jd->sa2 = area;
4261 if (!(abs(jd->start_x - event->xy[0]) > drag_threshold ||
4262 abs(jd->start_y - event->xy[1]) > drag_threshold))
4263 {
4264 /* We haven't moved enough to start a split. */
4265 jd->dir = SCREEN_DIR_NONE;
4266 jd->split_fac = 0.0f;
4268 return;
4269 }
4270
4271 jd->split_dir = (abs(event->xy[0] - jd->start_x) > abs(event->xy[1] - jd->start_y)) ?
4274 jd->split_fac = area_split_factor(C, jd, event);
4275 return;
4276 }
4277
4278 jd->sa2 = area;
4280 jd->dir = area_getorientation(jd->sa1, jd->sa2);
4281 jd->dock_target = area_docking_target(jd, event);
4282}
4283
4290
4291/* modal callback while selecting area (space) that will be removed */
4293{
4294 if (event->type == WINDEACTIVATE) {
4295 /* This operator can close windows, which can cause it to be re-run. */
4296 area_join_exit(C, op);
4297 return OPERATOR_FINISHED;
4298 }
4299
4300 if (op->customdata == nullptr) {
4301 if (!area_join_init(C, op, nullptr, nullptr)) {
4302 return OPERATOR_CANCELLED;
4303 }
4304 }
4306 if (jd == nullptr) {
4307 return OPERATOR_CANCELLED;
4308 }
4309
4310 /* execute the events */
4311 switch (event->type) {
4312
4313 case MOUSEMOVE: {
4314 area_join_update_data(C, jd, event);
4316 WM_cursor_set(jd->win1, area_join_cursor(jd, event));
4318
4319 WorkspaceStatus status(C);
4320 if (jd->sa1 && jd->sa1 == jd->sa2) {
4321 if (jd->split_fac == 0.0f) {
4322 status.item(IFACE_("Split/Dock"), ICON_MOUSE_LMB_DRAG);
4323 status.item(IFACE_("Cancel"), ICON_EVENT_ESC);
4324 }
4325 else {
4326 status.item(IFACE_("Select Split"), ICON_MOUSE_LMB_DRAG);
4327 status.item(IFACE_("Cancel"), ICON_EVENT_ESC);
4328 status.item_bool(IFACE_("Snap"), event->modifier & KM_CTRL, ICON_EVENT_CTRL);
4329 }
4330 }
4331 else {
4332 if (jd->dock_target == AreaDockTarget::None) {
4333 status.item(IFACE_("Select Area"), ICON_MOUSE_LMB_DRAG);
4334 status.item(IFACE_("Cancel"), ICON_EVENT_ESC);
4335 }
4336 else {
4337 status.item(IFACE_("Select Location"), ICON_MOUSE_LMB_DRAG);
4338 status.item(IFACE_("Cancel"), ICON_EVENT_ESC);
4339 status.item_bool(CTX_IFACE_(BLT_I18NCONTEXT_ID_SCREEN, "Precision"),
4340 event->modifier & KM_ALT,
4341 ICON_EVENT_ALT);
4342 status.item_bool(IFACE_("Snap"), event->modifier & KM_CTRL, ICON_EVENT_CTRL);
4343 }
4344 }
4345 break;
4346 }
4347 case LEFTMOUSE:
4348 if (event->val == KM_RELEASE) {
4349 area_join_update_data(C, jd, event);
4353 if (jd->sa1 && !jd->sa2) {
4354 /* Break out into new window if we are really outside the source window bounds. */
4355 if (event->xy[0] < 0 || event->xy[0] > jd->win1->sizex || event->xy[1] < 1 ||
4356 event->xy[1] > jd->win1->sizey)
4357 {
4358 /* We have to clear handlers or we get an error in wm_gizmomap_modal_get. */
4360 area_dupli_open(C, jd->sa1, blender::int2(event->xy[0], event->xy[1] - jd->sa1->winy));
4362 {
4365 {
4366 /* We've pulled a single editor out of the window into empty space.
4367 * Close the source window so we don't end up with a duplicate. */
4368 jd->close_win = true;
4369 }
4370 }
4371 }
4372 }
4373 else if (jd->sa1 && jd->sa1 == jd->sa2) {
4374 /* Same area so split. */
4375 if (area_split_allowed(jd->sa1, jd->split_dir) && jd->split_fac > 0.0001) {
4376 jd->sa2 = area_split(jd->win2,
4378 jd->sa1,
4379 jd->split_dir,
4380 jd->split_fac,
4381 true);
4382
4383 const bool large_v = jd->split_dir == SCREEN_AXIS_V &&
4384 ((jd->start_x < event->xy[0] && jd->split_fac > 0.5f) ||
4385 (jd->start_x > event->xy[0] && jd->split_fac < 0.5f));
4386
4387 const bool large_h = jd->split_dir == SCREEN_AXIS_H &&
4388 ((jd->start_y < event->xy[1] && jd->split_fac > 0.5f) ||
4389 (jd->start_y > event->xy[1] && jd->split_fac < 0.5f));
4390
4391 if (large_v || large_h) {
4392 /* Swap areas to follow old behavior of new area added based on starting location. If
4393 * from above the new area is above, if from below the new area is below, etc. Note
4394 * that this preserves runtime data, unlike ED_area_swapspace. */
4395 std::swap(jd->sa1->v1, jd->sa2->v1);
4396 std::swap(jd->sa1->v2, jd->sa2->v2);
4397 std::swap(jd->sa1->v3, jd->sa2->v3);
4398 std::swap(jd->sa1->v4, jd->sa2->v4);
4399 std::swap(jd->sa1->totrct, jd->sa2->totrct);
4400 std::swap(jd->sa1->winx, jd->sa2->winx);
4401 std::swap(jd->sa1->winy, jd->sa2->winy);
4402 }
4403
4406 }
4407 }
4408 else if (jd->sa1 && jd->sa2 && jd->dock_target != AreaDockTarget::None) {
4409 /* Dock this to the new location. */
4410 area_docking_apply(C, op);
4411 }
4412 else if (jd->sa1 && jd->sa2 && jd->dir != SCREEN_DIR_NONE) {
4413 /* Join to neighbor. */
4414 area_join_apply(C, op);
4415 }
4416 else {
4417 area_join_cancel(C, op);
4418 return OPERATOR_CANCELLED;
4419 }
4420
4421 /* Areas changed, update window titles. */
4422 if (jd->win2 && jd->win2 != jd->win1) {
4424 }
4425 if (jd->win1 && !jd->close_win) {
4427 }
4428
4429 const bool do_close_win = jd->close_win;
4430 wmWindow *close_win = jd->win1;
4431 area_join_exit(C, op);
4432 if (do_close_win) {
4433 wm_window_close(C, CTX_wm_manager(C), close_win);
4434 }
4435
4437 return OPERATOR_FINISHED;
4438 }
4439 break;
4440
4441 case RIGHTMOUSE:
4442 case EVT_ESCKEY:
4443 area_join_cancel(C, op);
4444 return OPERATOR_CANCELLED;
4445 default: {
4446 break;
4447 }
4448 }
4449
4451}
4452
4453/* Operator for joining two areas (space types) */
4455{
4456 /* identifiers */
4457 ot->name = "Join Area";
4458 ot->description = "Join selected areas into new window";
4459 ot->idname = "SCREEN_OT_area_join";
4460
4461 /* API callbacks. */
4462 ot->exec = area_join_exec;
4463 ot->invoke = area_join_invoke;
4464 ot->modal = area_join_modal;
4465 ot->poll = screen_active_editable;
4466 ot->cancel = area_join_cancel;
4467
4468 /* flags */
4469 ot->flag = OPTYPE_BLOCKING;
4470
4471 /* rna */
4472 RNA_def_int_vector(ot->srna,
4473 "source_xy",
4474 2,
4475 nullptr,
4476 INT_MIN,
4477 INT_MAX,
4478 "Source location",
4479 "",
4480 INT_MIN,
4481 INT_MAX);
4482 RNA_def_int_vector(ot->srna,
4483 "target_xy",
4484 2,
4485 nullptr,
4486 INT_MIN,
4487 INT_MAX,
4488 "Target location",
4489 "",
4490 INT_MIN,
4491 INT_MAX);
4492}
4493
4495
4496/* -------------------------------------------------------------------- */
4499
4501 wmOperator *op,
4502 const wmEvent *event)
4503{
4504 ScrArea *sa1, *sa2;
4505 if (screen_area_edge_from_cursor(C, event->xy, &sa1, &sa2) == nullptr) {
4506 return OPERATOR_CANCELLED;
4507 }
4508
4510 C, WM_operatortype_name(op->type, op->ptr).c_str(), ICON_NONE);
4511 uiLayout *layout = UI_popup_menu_layout(pup);
4512
4513 /* Vertical Split */
4515 ptr = layout->op("SCREEN_OT_area_split",
4516 IFACE_("Vertical Split"),
4517 ICON_SPLIT_VERTICAL,
4519 UI_ITEM_NONE);
4520 /* store initial mouse cursor position. */
4521 RNA_int_set_array(&ptr, "cursor", event->xy);
4522 RNA_enum_set(&ptr, "direction", SCREEN_AXIS_V);
4523
4524 /* Horizontal Split */
4525 ptr = layout->op("SCREEN_OT_area_split",
4526 IFACE_("Horizontal Split"),
4527 ICON_SPLIT_HORIZONTAL,
4529 UI_ITEM_NONE);
4530 /* store initial mouse cursor position. */
4531 RNA_int_set_array(&ptr, "cursor", event->xy);
4532 RNA_enum_set(&ptr, "direction", SCREEN_AXIS_H);
4533
4534 if (sa1 && sa2) {
4535 layout->separator();
4536 }
4537
4538 /* Join needs two very similar areas. */
4539 if (sa1 && sa2) {
4540 eScreenDir dir = area_getorientation(sa1, sa2);
4541 if (dir != SCREEN_DIR_NONE) {
4542 ptr = layout->op("SCREEN_OT_area_join",
4543 ELEM(dir, SCREEN_DIR_N, SCREEN_DIR_S) ? IFACE_("Join Up") :
4544 IFACE_("Join Right"),
4545 ELEM(dir, SCREEN_DIR_N, SCREEN_DIR_S) ? ICON_AREA_JOIN_UP : ICON_AREA_JOIN,
4547 UI_ITEM_NONE);
4548 RNA_int_set_array(&ptr, "source_xy", blender::int2{sa2->totrct.xmin, sa2->totrct.ymin});
4549 RNA_int_set_array(&ptr, "target_xy", blender::int2{sa1->totrct.xmin, sa1->totrct.ymin});
4550
4551 ptr = layout->op(
4552 "SCREEN_OT_area_join",
4553 ELEM(dir, SCREEN_DIR_N, SCREEN_DIR_S) ? IFACE_("Join Down") : IFACE_("Join Left"),
4554 ELEM(dir, SCREEN_DIR_N, SCREEN_DIR_S) ? ICON_AREA_JOIN_DOWN : ICON_AREA_JOIN_LEFT,
4556 UI_ITEM_NONE);
4557 RNA_int_set_array(&ptr, "source_xy", blender::int2{sa1->totrct.xmin, sa1->totrct.ymin});
4558 RNA_int_set_array(&ptr, "target_xy", blender::int2{sa2->totrct.xmin, sa2->totrct.ymin});
4559
4560 layout->separator();
4561 }
4562 }
4563
4564 /* Swap just needs two areas. */
4565 if (sa1 && sa2) {
4566 ptr = layout->op("SCREEN_OT_area_swap",
4567 IFACE_("Swap Areas"),
4568 ICON_AREA_SWAP,
4570 UI_ITEM_NONE);
4571 RNA_int_set_array(&ptr, "cursor", event->xy);
4572 }
4573
4574 UI_popup_menu_end(C, pup);
4575
4576 return OPERATOR_INTERFACE;
4577}
4578
4580{
4581 /* identifiers */
4582 ot->name = "Area Options";
4583 ot->description = "Operations for splitting and merging";
4584 ot->idname = "SCREEN_OT_area_options";
4585
4586 /* API callbacks. */
4588
4590
4591 /* flags */
4592 ot->flag = OPTYPE_INTERNAL;
4593}
4594
4596
4597/* -------------------------------------------------------------------- */
4600
4602{
4603 Main *bmain = CTX_data_main(C);
4604 int tot = 0;
4605
4606 LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
4607 LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
4608 if (area->spacedata.first != area->spacedata.last) {
4609 SpaceLink *sl = static_cast<SpaceLink *>(area->spacedata.first);
4610
4611 BLI_remlink(&area->spacedata, sl);
4612 tot += BLI_listbase_count(&area->spacedata);
4613 BKE_spacedata_freelist(&area->spacedata);
4614 BLI_addtail(&area->spacedata, sl);
4615 }
4616 }
4617 }
4618 BKE_reportf(op->reports, RPT_INFO, "Removed amount of editors: %d", tot);
4619
4620 return OPERATOR_FINISHED;
4621}
4622
4624{
4625 /* identifiers */
4626 ot->name = "Clean Up Space Data";
4627 ot->description = "Remove unused settings for invisible editors";
4628 ot->idname = "SCREEN_OT_spacedata_cleanup";
4629
4630 /* API callbacks. */
4631 ot->exec = spacedata_cleanup_exec;
4632 ot->poll = WM_operator_winactive;
4633}
4634
4636
4637/* -------------------------------------------------------------------- */
4640
4642{
4644 return false;
4645 }
4647 return !BLI_listbase_is_empty(&wm->operators);
4648}
4649
4651{
4653 wmOperator *lastop = static_cast<wmOperator *>(wm->operators.last);
4654
4655 /* Seek last registered operator */
4656 while (lastop) {
4657 if (lastop->type->flag & OPTYPE_REGISTER) {
4658 break;
4659 }
4660 lastop = lastop->prev;
4661 }
4662
4663 if (lastop) {
4664 WM_operator_free_all_after(wm, lastop);
4665 WM_operator_repeat_last(C, lastop);
4666 }
4667
4668 return OPERATOR_CANCELLED;
4669}
4670
4672{
4673 /* identifiers */
4674 ot->name = "Repeat Last";
4675 ot->description = "Repeat last action";
4676 ot->idname = "SCREEN_OT_repeat_last";
4677
4678 /* API callbacks. */
4679 ot->exec = repeat_last_exec;
4680
4681 ot->poll = repeat_history_poll;
4682}
4683
4685
4686/* -------------------------------------------------------------------- */
4689
4691 wmOperator *op,
4692 const wmEvent * /*event*/)
4693{
4695
4696 int items = BLI_listbase_count(&wm->operators);
4697 if (items == 0) {
4698 return OPERATOR_CANCELLED;
4699 }
4700
4702 C, WM_operatortype_name(op->type, op->ptr).c_str(), ICON_NONE);
4703 uiLayout *layout = UI_popup_menu_layout(pup);
4704
4705 wmOperator *lastop;
4706 int i;
4707 for (i = items - 1, lastop = static_cast<wmOperator *>(wm->operators.last); lastop;
4708 lastop = lastop->prev, i--)
4709 {
4710 if ((lastop->type->flag & OPTYPE_REGISTER) && WM_operator_repeat_check(C, lastop)) {
4711 PointerRNA op_ptr = layout->op(
4712 op->type, WM_operatortype_name(lastop->type, lastop->ptr), ICON_NONE);
4713 RNA_int_set(&op_ptr, "index", i);
4714 }
4715 }
4716
4717 UI_popup_menu_end(C, pup);
4718
4719 return OPERATOR_INTERFACE;
4720}
4721
4723{
4725
4726 op = static_cast<wmOperator *>(BLI_findlink(&wm->operators, RNA_int_get(op->ptr, "index")));
4727 if (op) {
4728 /* let's put it as last operator in list */
4729 BLI_remlink(&wm->operators, op);
4730 BLI_addtail(&wm->operators, op);
4731
4732 WM_operator_repeat(C, op);
4733 }
4734
4735 return OPERATOR_FINISHED;
4736}
4737
4739{
4740 /* identifiers */
4741 ot->name = "Repeat History";
4742 ot->description = "Display menu for previous actions performed";
4743 ot->idname = "SCREEN_OT_repeat_history";
4744
4745 /* API callbacks. */
4746 ot->invoke = repeat_history_invoke;
4747 ot->exec = repeat_history_exec;
4748 ot->poll = repeat_history_poll;
4749
4750 RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "", 0, 1000);
4751}
4752
4754
4755/* -------------------------------------------------------------------- */
4758
4760 wmOperator * /*op*/,
4761 const wmEvent * /*event*/)
4762{
4764
4765 if (lastop) {
4766 WM_operator_redo_popup(C, lastop);
4767 }
4768
4769 return OPERATOR_CANCELLED;
4770}
4771
4773{
4774 /* identifiers */
4775 ot->name = "Redo Last";
4776 ot->description = "Display parameters for last action performed";
4777 ot->idname = "SCREEN_OT_redo_last";
4778
4779 /* API callbacks. */
4780 ot->invoke = redo_last_invoke;
4781 ot->poll = repeat_history_poll;
4782}
4783
4785
4786/* -------------------------------------------------------------------- */
4789
4791{
4792 if (rv3d->localvd) {
4793 rv3d->localvd->view = rv3d->view;
4794 rv3d->localvd->view_axis_roll = rv3d->view_axis_roll;
4795 rv3d->localvd->persp = rv3d->persp;
4796 copy_qt_qt(rv3d->localvd->viewquat, rv3d->viewquat);
4797 }
4798}
4799
4801 ScrArea *area, ARegion *region, const char viewlock, const char view, const char persp)
4802{
4803 RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata);
4804
4805 if (persp == RV3D_CAMOB) {
4807 }
4808
4809 rv3d->viewlock = viewlock;
4810 rv3d->runtime_viewlock = 0;
4811 rv3d->view = view;
4813 rv3d->persp = persp;
4814
4815 ED_view3d_lock(rv3d);
4817 if ((viewlock & RV3D_BOXCLIP) && (persp == RV3D_ORTHO)) {
4818 ED_view3d_quadview_update(area, region, true);
4819 }
4820}
4821
4822/* insert a region in the area region list */
4824{
4825 ARegion *region = CTX_wm_region(C);
4826
4827 /* some rules... */
4828 if (region->regiontype != RGN_TYPE_WINDOW) {
4829 BKE_report(op->reports, RPT_ERROR, "Only window region can be 4-split");
4830 }
4831 else if (region->alignment == RGN_ALIGN_QSPLIT) {
4832 /* Exit quad-view */
4833 bScreen *screen = CTX_wm_screen(C);
4834 ScrArea *area = CTX_wm_area(C);
4835
4836 /* keep current region */
4837 region->alignment = 0;
4838
4839 if (area->spacetype == SPACE_VIEW3D) {
4840 RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata);
4841
4842 /* if this is a locked view, use settings from 'User' view */
4843 if (rv3d->viewlock) {
4844 View3D *v3d_user;
4845 ARegion *region_user;
4846
4847 if (ED_view3d_context_user_region(C, &v3d_user, &region_user)) {
4848 if (region != region_user) {
4849 std::swap(region->regiondata, region_user->regiondata);
4850 rv3d = static_cast<RegionView3D *>(region->regiondata);
4851 }
4852 }
4853 }
4854
4856 rv3d->viewlock = 0;
4857
4858 /* FIXME: This fixes missing update to workbench TAA. (see #76216)
4859 * However, it would be nice if the tagging should be done in a more conventional way. */
4860 rv3d->rflag |= RV3D_GPULIGHT_UPDATE;
4861
4862 /* Accumulate locks, in case they're mixed. */
4863 LISTBASE_FOREACH (ARegion *, region_iter, &area->regionbase) {
4864 if (region_iter->regiontype == RGN_TYPE_WINDOW) {
4865 RegionView3D *rv3d_iter = static_cast<RegionView3D *>(region_iter->regiondata);
4866 rv3d->viewlock_quad |= rv3d_iter->viewlock;
4867 }
4868 }
4869 }
4870
4871 LISTBASE_FOREACH_MUTABLE (ARegion *, region_iter, &area->regionbase) {
4872 if (region_iter->alignment == RGN_ALIGN_QSPLIT) {
4873 ED_region_remove(C, area, region_iter);
4874 if (region_iter == screen->active_region) {
4875 screen->active_region = nullptr;
4876 }
4877 }
4878 }
4879 ED_area_tag_redraw(area);
4881 }
4882 else if (region->next) {
4883 BKE_report(op->reports, RPT_ERROR, "Only last region can be 4-split");
4884 }
4885 else {
4886 /* Enter quad-view */
4887 ScrArea *area = CTX_wm_area(C);
4888
4889 region->alignment = RGN_ALIGN_QSPLIT;
4890
4891 for (int count = 0; count < 3; count++) {
4892 ARegion *new_region = BKE_area_region_copy(area->type, region);
4893 BLI_addtail(&area->regionbase, new_region);
4894 }
4895
4896 /* lock views and set them */
4897 if (area->spacetype == SPACE_VIEW3D) {
4898 View3D *v3d = static_cast<View3D *>(area->spacedata.first);
4899 int index_qsplit = 0;
4900
4901 /* run ED_view3d_lock() so the correct 'rv3d->viewquat' is set,
4902 * otherwise when restoring rv3d->localvd the 'viewquat' won't
4903 * match the 'view', set on entering localview See: #26315,
4904 *
4905 * We could avoid manipulating rv3d->localvd here if exiting
4906 * localview with a 4-split would assign these view locks */
4907 RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata);
4908 const char viewlock = (rv3d->viewlock_quad & RV3D_VIEWLOCK_INIT) ?
4911
4913 area, region, viewlock, ED_view3d_lock_view_from_index(index_qsplit++), RV3D_ORTHO);
4915 (region = region->next),
4916 viewlock,
4917 ED_view3d_lock_view_from_index(index_qsplit++),
4918 RV3D_ORTHO);
4920 (region = region->next),
4921 viewlock,
4922 ED_view3d_lock_view_from_index(index_qsplit++),
4923 RV3D_ORTHO);
4924/* forcing camera is distracting */
4925#if 0
4926 if (v3d->camera) {
4927 region_quadview_init_rv3d(area, (region = region->next), 0, RV3D_VIEW_CAMERA, RV3D_CAMOB);
4928 }
4929 else {
4930 region_quadview_init_rv3d(area, (region = region->next), 0, RV3D_VIEW_USER, RV3D_PERSP);
4931 }
4932#else
4933 (void)v3d;
4934#endif
4935 }
4936 ED_area_tag_redraw(area);
4938 }
4939
4940 return OPERATOR_FINISHED;
4941}
4942
4944{
4945 /* identifiers */
4946 ot->name = "Toggle Quad View";
4947 ot->description = "Split selected area into camera, front, right, and top views";
4948 ot->idname = "SCREEN_OT_region_quadview";
4949
4950 /* API callbacks. */
4951 ot->exec = region_quadview_exec;
4953 ot->flag = 0;
4954}
4955
4957
4958/* -------------------------------------------------------------------- */
4961
4963{
4964 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "region_type");
4965
4966 ARegion *region;
4967 if (RNA_property_is_set(op->ptr, prop)) {
4969 }
4970 else {
4971 region = CTX_wm_region(C);
4972 }
4973
4974 if (region && (region->alignment != RGN_ALIGN_NONE)) {
4975 ED_region_toggle_hidden(C, region);
4976 }
4977 ED_region_tag_redraw(region);
4978
4979 return OPERATOR_FINISHED;
4980}
4981
4983{
4984 ScrArea *area = CTX_wm_area(C);
4985
4986 /* Don't flip anything around in top-bar. */
4987 if (area && area->spacetype == SPACE_TOPBAR) {
4988 CTX_wm_operator_poll_msg_set(C, "Toggling regions in the Top-bar is not allowed");
4989 return false;
4990 }
4991
4992 return ED_operator_areaactive(C);
4993}
4994
4996{
4997 /* identifiers */
4998 ot->name = "Toggle Region";
4999 ot->idname = "SCREEN_OT_region_toggle";
5000 ot->description = "Hide or unhide the region";
5001
5002 /* API callbacks. */
5003 ot->exec = region_toggle_exec;
5004 ot->poll = region_toggle_poll;
5005 ot->flag = 0;
5006
5007 RNA_def_enum(ot->srna,
5008 "region_type",
5010 0,
5011 "Region Type",
5012 "Type of the region to toggle");
5013}
5014
5016
5017/* -------------------------------------------------------------------- */
5020
5021/* flip a region alignment */
5023{
5024 ARegion *region = CTX_wm_region(C);
5025
5026 if (!region) {
5027 return OPERATOR_CANCELLED;
5028 }
5029
5030 if (region->alignment == RGN_ALIGN_TOP) {
5031 region->alignment = RGN_ALIGN_BOTTOM;
5032 }
5033 else if (region->alignment == RGN_ALIGN_BOTTOM) {
5034 region->alignment = RGN_ALIGN_TOP;
5035 }
5036 else if (region->alignment == RGN_ALIGN_LEFT) {
5037 region->alignment = RGN_ALIGN_RIGHT;
5038 }
5039 else if (region->alignment == RGN_ALIGN_RIGHT) {
5040 region->alignment = RGN_ALIGN_LEFT;
5041 }
5042
5046
5047 return OPERATOR_FINISHED;
5048}
5049
5051{
5052 ScrArea *area = CTX_wm_area(C);
5053
5054 /* Don't flip anything around in top-bar. */
5055 if (area && area->spacetype == SPACE_TOPBAR) {
5056 CTX_wm_operator_poll_msg_set(C, "Flipping regions in the Top-bar is not allowed");
5057 return false;
5058 }
5059
5060 return ED_operator_areaactive(C);
5061}
5062
5064{
5065 /* identifiers */
5066 ot->name = "Flip Region";
5067 ot->idname = "SCREEN_OT_region_flip";
5068 ot->description = "Toggle the region's alignment (left/right or top/bottom)";
5069
5070 /* API callbacks. */
5071 ot->exec = region_flip_exec;
5072 ot->poll = region_flip_poll;
5073 ot->flag = 0;
5074}
5075
5077
5078/* -------------------------------------------------------------------- */
5081
5082/* show/hide header text menus */
5084{
5085 ScrArea *area = CTX_wm_area(C);
5086
5087 area->flag = area->flag ^ HEADER_NO_PULLDOWN;
5088
5089 ED_area_tag_redraw(area);
5091
5092 return OPERATOR_FINISHED;
5093}
5094
5096{
5097 /* identifiers */
5098 ot->name = "Expand/Collapse Header Menus";
5099 ot->idname = "SCREEN_OT_header_toggle_menus";
5100 ot->description = "Expand or collapse the header pull-down menus";
5101
5102 /* API callbacks. */
5104 ot->poll = ED_operator_areaactive;
5105 ot->flag = 0;
5106}
5107
5109
5110/* -------------------------------------------------------------------- */
5113
5114static void screen_area_menu_items(ScrArea *area, uiLayout *layout)
5115{
5116 if (ED_area_is_global(area)) {
5117 return;
5118 }
5119
5121
5122 ptr = layout->op("SCREEN_OT_area_join",
5123 IFACE_("Move/Split Area"),
5124 ICON_AREA_DOCK,
5126 UI_ITEM_NONE);
5127
5128 layout->separator();
5129
5130 layout->op("SCREEN_OT_screen_full_area",
5131 area->full ? IFACE_("Restore Areas") : IFACE_("Maximize Area"),
5132 ICON_NONE);
5133
5134 if (area->spacetype != SPACE_FILE && !area->full) {
5135 ptr = layout->op("SCREEN_OT_screen_full_area",
5136 IFACE_("Full Screen Area"),
5137 ICON_NONE,
5139 UI_ITEM_NONE);
5140 RNA_boolean_set(&ptr, "use_hide_panels", true);
5141 }
5142
5143 layout->op("SCREEN_OT_area_dupli", std::nullopt, ICON_NONE);
5144 layout->separator();
5145 layout->op("SCREEN_OT_area_close", std::nullopt, ICON_X);
5146}
5147
5149{
5150 ScrArea *area = CTX_wm_area(C);
5151 {
5153 (ID *)CTX_wm_screen(C), &RNA_Space, area->spacedata.first);
5154 if (!ELEM(area->spacetype, SPACE_TOPBAR)) {
5155 layout->prop(&ptr, "show_region_header", UI_ITEM_NONE, IFACE_("Show Header"), ICON_NONE);
5156 }
5157
5158 ARegion *region_header = BKE_area_find_region_type(area, RGN_TYPE_HEADER);
5159 uiLayout *col = &layout->column(false);
5160 uiLayoutSetActive(col, (region_header->flag & RGN_FLAG_HIDDEN) == 0);
5161
5163 col->prop(
5164 &ptr, "show_region_tool_header", UI_ITEM_NONE, IFACE_("Show Tool Settings"), ICON_NONE);
5165 }
5166
5167 col->op("SCREEN_OT_header_toggle_menus",
5168 IFACE_("Show Menus"),
5169 (area->flag & HEADER_NO_PULLDOWN) ? ICON_CHECKBOX_DEHLT : ICON_CHECKBOX_HLT);
5170 }
5171
5172 if (!ELEM(area->spacetype, SPACE_TOPBAR)) {
5173 layout->separator();
5174 ED_screens_region_flip_menu_create(C, layout, nullptr);
5175 layout->separator();
5176 screen_area_menu_items(area, layout);
5177 }
5178}
5179
5181{
5182 ScrArea *area = CTX_wm_area(C);
5183
5184 {
5186 (ID *)CTX_wm_screen(C), &RNA_Space, area->spacedata.first);
5187 layout->prop(&ptr, "show_region_footer", UI_ITEM_NONE, IFACE_("Show Footer"), ICON_NONE);
5188 }
5189
5190 ED_screens_region_flip_menu_create(C, layout, nullptr);
5191 layout->separator();
5192 screen_area_menu_items(area, layout);
5193}
5194
5196{
5197 const ARegion *region = CTX_wm_region(C);
5198 const short region_alignment = RGN_ALIGN_ENUM_FROM_MASK(region->alignment);
5199 const char *but_flip_str = region_alignment == RGN_ALIGN_LEFT ? IFACE_("Flip to Right") :
5200 region_alignment == RGN_ALIGN_RIGHT ? IFACE_("Flip to Left") :
5201 region_alignment == RGN_ALIGN_BOTTOM ? IFACE_("Flip to Top") :
5202 IFACE_("Flip to Bottom");
5203
5204 /* default is WM_OP_INVOKE_REGION_WIN, which we don't want here. */
5206
5207 layout->op("SCREEN_OT_region_flip", but_flip_str, ICON_NONE);
5208}
5209
5210static void ed_screens_statusbar_menu_create(uiLayout *layout, void * /*arg*/)
5211{
5212 PointerRNA ptr = RNA_pointer_create_discrete(nullptr, &RNA_PreferencesView, &U);
5213 layout->prop(&ptr, "show_statusbar_stats", UI_ITEM_NONE, IFACE_("Scene Statistics"), ICON_NONE);
5214 layout->prop(
5215 &ptr, "show_statusbar_scene_duration", UI_ITEM_NONE, IFACE_("Scene Duration"), ICON_NONE);
5216 layout->prop(&ptr, "show_statusbar_memory", UI_ITEM_NONE, IFACE_("System Memory"), ICON_NONE);
5218 layout->prop(&ptr, "show_statusbar_vram", UI_ITEM_NONE, IFACE_("Video Memory"), ICON_NONE);
5219 }
5220 layout->prop(
5221 &ptr, "show_extensions_updates", UI_ITEM_NONE, IFACE_("Extensions Updates"), ICON_NONE);
5222 layout->prop(&ptr, "show_statusbar_version", UI_ITEM_NONE, IFACE_("Blender Version"), ICON_NONE);
5223}
5224
5226 wmOperator * /*op*/,
5227 const wmEvent * /*event*/)
5228{
5229 const ScrArea *area = CTX_wm_area(C);
5230 const ARegion *region = CTX_wm_region(C);
5231
5232 if (area && area->spacetype == SPACE_STATUSBAR) {
5233 uiPopupMenu *pup = UI_popup_menu_begin(C, IFACE_("Status Bar"), ICON_NONE);
5234 uiLayout *layout = UI_popup_menu_layout(pup);
5235 ed_screens_statusbar_menu_create(layout, nullptr);
5236 UI_popup_menu_end(C, pup);
5237 }
5238 else if (region) {
5240 uiPopupMenu *pup = UI_popup_menu_begin(C, IFACE_("Header"), ICON_NONE);
5241 uiLayout *layout = UI_popup_menu_layout(pup);
5242 ED_screens_header_tools_menu_create(C, layout, nullptr);
5243 UI_popup_menu_end(C, pup);
5244 }
5245 else if (region->regiontype == RGN_TYPE_FOOTER) {
5246 uiPopupMenu *pup = UI_popup_menu_begin(C, IFACE_("Footer"), ICON_NONE);
5247 uiLayout *layout = UI_popup_menu_layout(pup);
5248 ED_screens_footer_tools_menu_create(C, layout, nullptr);
5249 UI_popup_menu_end(C, pup);
5250 }
5251 else if (region->regiontype == RGN_TYPE_NAV_BAR) {
5252 uiPopupMenu *pup = UI_popup_menu_begin(C, IFACE_("Navigation Bar"), ICON_NONE);
5253 uiLayout *layout = UI_popup_menu_layout(pup);
5254
5255 /* We need WM_OP_INVOKE_DEFAULT in case menu item is over another area. */
5257 layout->op("SCREEN_OT_region_toggle", IFACE_("Hide"), ICON_NONE);
5258
5259 ED_screens_region_flip_menu_create(C, layout, nullptr);
5260 const ScrArea *area = CTX_wm_area(C);
5261 if (area && area->spacetype == SPACE_PROPERTIES) {
5262 layout->menu_fn(IFACE_("Visible Tabs"), ICON_NONE, ED_buttons_visible_tabs_menu, nullptr);
5263 }
5264 UI_popup_menu_end(C, pup);
5265 }
5266 }
5267
5268 return OPERATOR_INTERFACE;
5269}
5270
5272{
5273 /* identifiers */
5274 ot->name = "Region";
5275 ot->description = "Display region context menu";
5276 ot->idname = "SCREEN_OT_region_context_menu";
5277
5278 /* API callbacks. */
5280}
5281
5283
5284/* -------------------------------------------------------------------- */
5289
5290static bool match_region_with_redraws(const ScrArea *area,
5291 eRegion_Type regiontype,
5292 eScreen_Redraws_Flag redraws,
5293 bool from_anim_edit)
5294{
5295 const eSpace_Type spacetype = eSpace_Type(area->spacetype);
5296 if (regiontype == RGN_TYPE_WINDOW) {
5297
5298 switch (spacetype) {
5299 case SPACE_VIEW3D:
5300 if ((redraws & TIME_ALL_3D_WIN) || from_anim_edit) {
5301 return true;
5302 }
5303 break;
5304 case SPACE_GRAPH:
5305 case SPACE_NLA:
5306 if ((redraws & TIME_ALL_ANIM_WIN) || from_anim_edit) {
5307 return true;
5308 }
5309 break;
5310 case SPACE_ACTION:
5311 /* if only 1 window or 3d windows, we do timeline too
5312 * NOTE: Now we do action editor in all these cases, since timeline is here. */
5313 if ((redraws & (TIME_ALL_ANIM_WIN | TIME_REGION | TIME_ALL_3D_WIN)) || from_anim_edit) {
5314 return true;
5315 }
5316 break;
5317 case SPACE_PROPERTIES:
5318 if (redraws & TIME_ALL_BUTS_WIN) {
5319 return true;
5320 }
5321 break;
5322 case SPACE_SEQ:
5323 if ((redraws & (TIME_SEQ | TIME_ALL_ANIM_WIN)) || from_anim_edit) {
5324 return true;
5325 }
5326 break;
5327 case SPACE_NODE:
5328 if (redraws & TIME_NODES) {
5329 return true;
5330 }
5331 break;
5332 case SPACE_IMAGE:
5333 if ((redraws & TIME_ALL_IMAGE_WIN) || from_anim_edit) {
5334 return true;
5335 }
5336 break;
5337 case SPACE_CLIP:
5338 if ((redraws & TIME_CLIPS) || from_anim_edit) {
5339 return true;
5340 }
5341 break;
5342 case SPACE_SPREADSHEET:
5343 if (redraws & TIME_SPREADSHEETS) {
5344 return true;
5345 }
5346 break;
5347 default:
5348 break;
5349 }
5350 }
5351 else if (regiontype == RGN_TYPE_UI) {
5352 if (spacetype == SPACE_CLIP) {
5353 /* Track Preview button is on Properties Editor in SpaceClip,
5354 * and it's very common case when users want it be refreshing
5355 * during playback, so asking people to enable special option
5356 * for this is a bit tricky, so add exception here for refreshing
5357 * Properties Editor for SpaceClip always */
5358 return true;
5359 }
5360
5361 if (redraws & TIME_ALL_BUTS_WIN) {
5362 return true;
5363 }
5364 }
5365 else if (regiontype == RGN_TYPE_HEADER) {
5366 if (spacetype == SPACE_ACTION) {
5367 /* The timeline shows the current frame in the header. Other headers
5368 * don't need to be updated. */
5369 SpaceAction *saction = (SpaceAction *)area->spacedata.first;
5370 return saction->mode == SACTCONT_TIMELINE;
5371 }
5372 }
5373 else if (regiontype == RGN_TYPE_PREVIEW) {
5374 switch (spacetype) {
5375 case SPACE_SEQ:
5376 if (redraws & (TIME_SEQ | TIME_ALL_ANIM_WIN)) {
5377 return true;
5378 }
5379 break;
5380 case SPACE_CLIP:
5381 return true;
5382 default:
5383 break;
5384 }
5385 }
5386 else if (regiontype == RGN_TYPE_TOOLS) {
5387 switch (spacetype) {
5388 case SPACE_SPREADSHEET:
5389 if (redraws & TIME_SPREADSHEETS) {
5390 return true;
5391 }
5392 break;
5393 default:
5394 break;
5395 }
5396 }
5397
5398 return false;
5399}
5400
5402 bContext *C, ScrArea *area, ARegion *region, const Scene *scene, eScreen_Redraws_Flag redraws)
5403{
5404 /* Do follow time here if editor type supports it */
5405 if ((redraws & TIME_FOLLOW) &&
5407 eRegion_Type(region->regiontype)))
5408 {
5409 float w = BLI_rctf_size_x(&region->v2d.cur);
5410 if (scene->r.cfra < region->v2d.cur.xmin) {
5411 region->v2d.cur.xmax = scene->r.cfra;
5412 region->v2d.cur.xmin = region->v2d.cur.xmax - w;
5413 ED_region_tag_redraw(region);
5414 return;
5415 }
5416 if (scene->r.cfra > region->v2d.cur.xmax) {
5417 region->v2d.cur.xmin = scene->r.cfra;
5418 region->v2d.cur.xmax = region->v2d.cur.xmin + w;
5419 ED_region_tag_redraw(region);
5420 return;
5421 }
5422 }
5423
5424 /* No need to do a full redraw as the current frame indicator is only updated.
5425 * We do need to redraw when this area is in full screen as no other areas
5426 * will be tagged for redrawing. */
5427 if (region->regiontype == RGN_TYPE_WINDOW && !area->full) {
5428 if (ELEM(area->spacetype, SPACE_NLA, SPACE_ACTION)) {
5429 return;
5430 }
5431
5432 /* Drivers Editor needs a full redraw on playback for graph_draw_driver_debug().
5433 * This will make it slower than regular graph editor during playback, but drawing this in
5434 * graph_main_region_draw_overlay() is not feasible because it requires animation filtering
5435 * which has significant overhead which needs to be avoided in the overlay which is redrawn on
5436 * every UI interaction. */
5437 if (area->spacetype == SPACE_GRAPH) {
5438 const SpaceGraph *sipo = static_cast<const SpaceGraph *>(area->spacedata.first);
5439 if (sipo->mode != SIPO_MODE_DRIVERS) {
5440 return;
5441 }
5442 bAnimContext ac;
5443 if (ANIM_animdata_get_context(C, &ac) == false) {
5444 return;
5445 }
5446 if (ac.datatype != ANIMCONT_DRIVERS) {
5447 return;
5448 }
5449 }
5450
5451 if (area->spacetype == SPACE_SEQ) {
5453 return;
5454 }
5455 }
5456 }
5457 ED_region_tag_redraw(region);
5458}
5459
5460// #define PROFILE_AUDIO_SYNC
5461
5463 wmOperator * /*op*/,
5464 const wmEvent *event)
5465{
5466 bScreen *screen = CTX_wm_screen(C);
5467 wmTimer *wt = screen->animtimer;
5468
5469 if (!(wt && wt == event->customdata)) {
5470 return OPERATOR_PASS_THROUGH;
5471 }
5472
5473 wmWindow *win = CTX_wm_window(C);
5474
5475#ifdef PROFILE_AUDIO_SYNC
5476 static int old_frame = 0;
5477 int newfra_int;
5478#endif
5479
5480 Main *bmain = CTX_data_main(C);
5481 Scene *scene = CTX_data_scene(C);
5482 ViewLayer *view_layer = WM_window_get_active_view_layer(win);
5483 Depsgraph *depsgraph = BKE_scene_get_depsgraph(scene, view_layer);
5484 Scene *scene_eval = (depsgraph != nullptr) ? DEG_get_evaluated_scene(depsgraph) : nullptr;
5485 ScreenAnimData *sad = static_cast<ScreenAnimData *>(wt->customdata);
5487 int sync;
5488 double time;
5489
5490 /* sync, don't sync, or follow scene setting */
5491 if (sad->flag & ANIMPLAY_FLAG_SYNC) {
5492 sync = 1;
5493 }
5494 else if (sad->flag & ANIMPLAY_FLAG_NO_SYNC) {
5495 sync = 0;
5496 }
5497 else {
5498 sync = (scene->flag & SCE_FRAME_DROP);
5499 }
5500
5501 if (scene_eval == nullptr) {
5502 /* Happens when undo/redo system is used during playback, nothing meaningful we can do here. */
5503 }
5504 else if (scene_eval->id.recalc & ID_RECALC_FRAME_CHANGE) {
5505 /* Ignore seek here, the audio will be updated to the scene frame after jump during next
5506 * dependency graph update. */
5507 }
5508 else if ((scene->audio.flag & AUDIO_SYNC) && (sad->flag & ANIMPLAY_FLAG_REVERSE) == false &&
5509 isfinite(time = BKE_sound_sync_scene(scene_eval)))
5510 {
5511 scene->r.cfra = round(time * FPS);
5512
5513#ifdef PROFILE_AUDIO_SYNC
5514 newfra_int = scene->r.cfra;
5515 if (newfra_int < old_frame) {
5516 printf("back -%d jump detected, frame %d!\n", old_frame - newfra_int, old_frame);
5517 }
5518 else if (newfra_int > old_frame + 1) {
5519 printf("forward +%d jump detected, frame %d!\n", newfra_int - old_frame, old_frame);
5520 }
5521 fflush(stdout);
5522 old_frame = newfra_int;
5523#endif
5524 }
5525 else {
5526 if (sync) {
5527 /* Try to keep the playback in realtime by dropping frames. */
5528
5529 /* How much time (in frames) has passed since the last frame was drawn? */
5530 double delta_frames = wt->time_delta * FPS;
5531
5532 /* Add the remaining fraction from the last time step. */
5533 delta_frames += sad->lagging_frame_count;
5534
5535 if (delta_frames < 1.0) {
5536 /* We can render faster than the scene frame rate. However skipping or delaying frames
5537 * here seems to in practice lead to jittery playback so just step forward a minimum of
5538 * one frame. (Even though this can lead to too fast playback, the jitteriness is more
5539 * annoying)
5540 */
5541 delta_frames = 1.0f;
5542 sad->lagging_frame_count = 0;
5543 }
5544 else {
5545 /* Extract the delta frame fractions that will be skipped when converting to int. */
5546 sad->lagging_frame_count = delta_frames - int(delta_frames);
5547 }
5548
5549 const int step = delta_frames;
5550
5551 /* skip frames */
5552 if (sad->flag & ANIMPLAY_FLAG_REVERSE) {
5553 scene->r.cfra -= step;
5554 }
5555 else {
5556 scene->r.cfra += step;
5557 }
5558 }
5559 else {
5560 /* one frame +/- */
5561 if (sad->flag & ANIMPLAY_FLAG_REVERSE) {
5562 scene->r.cfra--;
5563 }
5564 else {
5565 scene->r.cfra++;
5566 }
5567 }
5568 }
5569
5570 /* reset 'jumped' flag before checking if we need to jump... */
5572
5573 if (sad->flag & ANIMPLAY_FLAG_REVERSE) {
5574 /* jump back to end? */
5575 if (PRVRANGEON) {
5576 if (scene->r.cfra < scene->r.psfra) {
5577 scene->r.cfra = scene->r.pefra;
5578 sad->flag |= ANIMPLAY_FLAG_JUMPED;
5579 }
5580 }
5581 else {
5582 if (scene->r.cfra < scene->r.sfra) {
5583 scene->r.cfra = scene->r.efra;
5584 sad->flag |= ANIMPLAY_FLAG_JUMPED;
5585 }
5586 }
5587 }
5588 else {
5589 /* jump back to start? */
5590 if (PRVRANGEON) {
5591 if (scene->r.cfra > scene->r.pefra) {
5592 scene->r.cfra = scene->r.psfra;
5593 sad->flag |= ANIMPLAY_FLAG_JUMPED;
5594 }
5595 }
5596 else {
5597 if (scene->r.cfra > scene->r.efra) {
5598 scene->r.cfra = scene->r.sfra;
5599 sad->flag |= ANIMPLAY_FLAG_JUMPED;
5600 }
5601 }
5602 }
5603
5604 /* next frame overridden by user action (pressed jump to first/last frame) */
5606 scene->r.cfra = sad->nextfra;
5608 sad->flag |= ANIMPLAY_FLAG_JUMPED;
5609 }
5610
5611 if (sad->flag & ANIMPLAY_FLAG_JUMPED) {
5613#ifdef PROFILE_AUDIO_SYNC
5614 old_frame = scene->r.cfra;
5615#endif
5616 }
5617
5618 /* Since we follow draw-flags, we can't send notifier but tag regions ourselves. */
5619 if (depsgraph != nullptr) {
5621 }
5622
5623 LISTBASE_FOREACH (wmWindow *, window, &wm->windows) {
5624 bScreen *win_screen = WM_window_get_active_screen(window);
5625
5626 LISTBASE_FOREACH (ScrArea *, area, &win_screen->areabase) {
5627 LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
5628 bool redraw = false;
5629 if (region == sad->region) {
5630 redraw = true;
5631 }
5632 else if (match_region_with_redraws(area,
5633 eRegion_Type(region->regiontype),
5635 sad->from_anim_edit))
5636 {
5637 redraw = true;
5638 }
5639
5640 if (redraw) {
5642 C, area, region, scene, eScreen_Redraws_Flag(sad->redraws));
5643 /* Doesn't trigger a full redraw of the screen but makes sure at least overlay drawing
5644 * (#ARegionType.draw_overlay()) is triggered, which is how the current-frame is drawn.
5645 */
5646 win_screen->do_draw = true;
5647 }
5648 }
5649 }
5650 }
5651
5652 if (U.uiflag & USER_SHOW_FPS) {
5653 /* Update frame rate info too.
5654 * NOTE: this may not be accurate enough, since we might need this after modifiers/etc.
5655 * have been calculated instead of just before updates have been done? */
5656 ED_scene_fps_average_accumulate(scene, U.playback_fps_samples, wt->time_last);
5657 }
5658
5659 /* Recalculate the time-step for the timer now that we've finished calculating this,
5660 * since the frames-per-second value may have been changed.
5661 */
5662 /* TODO: this may make evaluation a bit slower if the value doesn't change...
5663 * any way to avoid this? */
5664 wt->time_step = (1.0 / FPS);
5665
5666 return OPERATOR_FINISHED;
5667}
5668
5670{
5671 /* identifiers */
5672 ot->name = "Animation Step";
5673 ot->description = "Step through animation by position";
5674 ot->idname = "SCREEN_OT_animation_step";
5675
5676 /* API callbacks. */
5678
5680}
5681
5683
5684/* -------------------------------------------------------------------- */
5689
5691{
5692 /* If sound was playing back when we changed any sound settings, we need to make sure that
5693 * we reinitialize the playback state properly. Audaspace pauses playback on re-initializing
5694 * the playback device, so we need to make sure we reinitialize the playback state on our
5695 * end as well. (Otherwise the sound device might be in a weird state and crashes Blender). */
5697 wmWindow *timer_win = nullptr;
5698 const bool is_playing = screen != nullptr;
5699 bool playback_sync = false;
5700 int play_direction = 0;
5701
5702 if (is_playing) {
5703 ScreenAnimData *sad = static_cast<ScreenAnimData *>(screen->animtimer->customdata);
5704 timer_win = screen->animtimer->win;
5705 /* -1 means play backwards. */
5706 play_direction = (sad->flag & ANIMPLAY_FLAG_REVERSE) ? -1 : 1;
5707 playback_sync = sad->flag & ANIMPLAY_FLAG_SYNC;
5708 /* Stop playback. */
5710 }
5711 Main *bmain = CTX_data_main(C);
5712 /* Re-initialize the audio device. */
5713 BKE_sound_init(bmain);
5714 if (is_playing) {
5715 /* We need to set the context window to the window that was playing back previously.
5716 * Otherwise we will attach the new playback timer to an other window.
5717 */
5718 wmWindow *win = CTX_wm_window(C);
5719 CTX_wm_window_set(C, timer_win);
5720 ED_screen_animation_play(C, playback_sync, play_direction);
5721 CTX_wm_window_set(C, win);
5722 }
5723}
5724
5726{
5727 LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
5728 bScreen *screen = WM_window_get_active_screen(win);
5729
5730 if (screen->animtimer || screen->scrubbing) {
5731 return screen;
5732 }
5733 }
5734
5735 return nullptr;
5736}
5737
5739{
5740 LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
5741 bScreen *screen = WM_window_get_active_screen(win);
5742
5743 if (screen->animtimer) {
5744 return screen;
5745 }
5746 }
5747
5748 return nullptr;
5749}
5750
5752{
5753 Main *bmain = CTX_data_main(C);
5755 wmTimer *wt = screen->animtimer;
5756 Scene *scene = CTX_data_scene(C);
5759
5760 ScreenAnimData *sad = static_cast<ScreenAnimData *>(wt->customdata);
5761 /* Only stop sound playback, when playing forward, since there is no sound for reverse
5762 * playback. */
5763 if ((sad->flag & ANIMPLAY_FLAG_REVERSE) == 0) {
5764 BKE_sound_stop_scene(scene_eval);
5765 }
5766
5767 ED_screen_animation_timer(C, 0, 0, 0);
5770
5771 /* Triggers redraw of sequencer preview so that it does not show to fps anymore after stopping
5772 * playback. */
5776}
5777
5778static wmOperatorStatus start_playback(bContext *C, int sync, int mode)
5779{
5780 Main *bmain = CTX_data_main(C);
5781 bScreen *screen = CTX_wm_screen(C);
5782 Scene *scene = CTX_data_scene(C);
5785
5787
5788 /* Only play sound when playing forward. Reverse sound playback is not implemented. */
5789 if (mode == 1) {
5790 BKE_sound_play_scene(scene_eval);
5791 }
5792
5793 ED_screen_animation_timer(C, screen->redraws_flag, sync, mode);
5795
5796 if (screen->animtimer) {
5797 wmTimer *wt = screen->animtimer;
5798 ScreenAnimData *sad = static_cast<ScreenAnimData *>(wt->customdata);
5799
5800 sad->region = CTX_wm_region(C);
5801 }
5802
5803 return OPERATOR_FINISHED;
5804}
5805
5807{
5810 return OPERATOR_FINISHED;
5811 }
5812
5813 return start_playback(C, sync, mode);
5814}
5815
5817{
5818 int mode = RNA_boolean_get(op->ptr, "reverse") ? -1 : 1;
5819 int sync = -1;
5820
5821 if (RNA_struct_property_is_set(op->ptr, "sync")) {
5822 sync = RNA_boolean_get(op->ptr, "sync");
5823 }
5824
5825 return ED_screen_animation_play(C, sync, mode);
5826}
5827
5829{
5830 PropertyRNA *prop;
5831
5832 /* identifiers */
5833 ot->name = "Play Animation";
5834 ot->description = "Play animation";
5835 ot->idname = "SCREEN_OT_animation_play";
5836
5837 /* API callbacks. */
5839
5841
5842 prop = RNA_def_boolean(
5843 ot->srna, "reverse", false, "Play in Reverse", "Animation is played backwards");
5845 prop = RNA_def_boolean(ot->srna, "sync", false, "Sync", "Drop frames to maintain framerate");
5847}
5848
5850
5851/* -------------------------------------------------------------------- */
5854
5856{
5858
5859 if (screen) {
5860 bool restore_start_frame = RNA_boolean_get(op->ptr, "restore_frame") && screen->animtimer;
5861 int frame;
5862 if (restore_start_frame) {
5863 ScreenAnimData *sad = static_cast<ScreenAnimData *>(screen->animtimer->customdata);
5864 frame = sad->sfra;
5865 }
5866
5867 /* Stop playback */
5869 if (restore_start_frame) {
5870 Scene *scene = CTX_data_scene(C);
5871 /* reset current frame and just send a notifier to deal with the rest */
5872 scene->r.cfra = frame;
5874 }
5875 }
5876
5877 return OPERATOR_PASS_THROUGH;
5878}
5879
5881{
5882 /* identifiers */
5883 ot->name = "Cancel Animation";
5884 ot->description = "Cancel animation, returning to the original frame";
5885 ot->idname = "SCREEN_OT_animation_cancel";
5886
5887 /* API callbacks. */
5889
5891
5892 RNA_def_boolean(ot->srna,
5893 "restore_frame",
5894 true,
5895 "Restore Frame",
5896 "Restore the frame when animation was initialized");
5897}
5898
5900
5901/* -------------------------------------------------------------------- */
5904
5905/* operator state vars used: (added by default WM callbacks)
5906 * xmin, ymin
5907 * xmax, ymax
5908 *
5909 * customdata: the wmGesture pointer
5910 *
5911 * callbacks:
5912 *
5913 * exec() has to be filled in by user
5914 *
5915 * invoke() default WM function
5916 * adds modal handler
5917 *
5918 * modal() default WM function
5919 * accept modal events while doing it, calls exec(), handles ESC and border drawing
5920 *
5921 * poll() has to be filled in by user for context
5922 */
5923#if 0
5925{
5926 int event_type = RNA_int_get(op->ptr, "event_type");
5927
5928 if (event_type == LEFTMOUSE) {
5929 printf("box select do select\n");
5930 }
5931 else if (event_type == RIGHTMOUSE) {
5932 printf("box select deselect\n");
5933 }
5934 else {
5935 printf("box select do something\n");
5936 }
5937
5938 return 1;
5939}
5940
5941static void SCREEN_OT_box_select(wmOperatorType *ot)
5942{
5943 /* identifiers */
5944 ot->name = "Box Select";
5945 ot->idname = "SCREEN_OT_box_select";
5946
5947 /* API callbacks. */
5948 ot->exec = box_select_exec;
5949 ot->invoke = WM_gesture_box_invoke;
5950 ot->modal = WM_gesture_box_modal;
5951 ot->cancel = WM_gesture_box_cancel;
5952
5953 ot->poll = ED_operator_areaactive;
5954
5955 /* rna */
5956 RNA_def_int(ot->srna, "event_type", 0, INT_MIN, INT_MAX, "Event Type", "", INT_MIN, INT_MAX);
5958}
5959#endif
5960
5962
5963/* -------------------------------------------------------------------- */
5968
5970{
5971 bScreen *screen = CTX_wm_screen(C);
5972 ScrArea *area = nullptr;
5973
5974 /* search current screen for 'fullscreen' areas */
5975 LISTBASE_FOREACH (ScrArea *, area_iter, &screen->areabase) {
5976 if (area_iter->full) {
5977 area = area_iter;
5978 break;
5979 }
5980 }
5981 if (!area) {
5982 BKE_report(op->reports, RPT_ERROR, "No fullscreen areas were found");
5983 return OPERATOR_CANCELLED;
5984 }
5985
5987
5988 return OPERATOR_FINISHED;
5989}
5990
5992{
5993 /* identifiers */
5994 ot->name = "Back to Previous Screen";
5995 ot->description = "Revert back to the original screen layout, before fullscreen area overlay";
5996 ot->idname = "SCREEN_OT_back_to_previous";
5997
5998 /* API callbacks. */
5999 ot->exec = fullscreen_back_exec;
6001}
6002
6004
6005/* -------------------------------------------------------------------- */
6008
6010{
6011 wmWindow *win_cur = CTX_wm_window(C);
6012 /* Use eventstate, not event from _invoke, so this can be called through exec(). */
6013 const wmEvent *event = win_cur->eventstate;
6014 int sizex = (500 + UI_NAVIGATION_REGION_WIDTH) * UI_SCALE_FAC;
6015 int sizey = 520 * UI_SCALE_FAC;
6016
6017 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "section");
6018 if (prop && RNA_property_is_set(op->ptr, prop)) {
6019 /* Set active section via RNA, so it can fail properly. */
6020
6021 PointerRNA pref_ptr = RNA_pointer_create_discrete(nullptr, &RNA_Preferences, &U);
6022 PropertyRNA *active_section_prop = RNA_struct_find_property(&pref_ptr, "active_section");
6023
6024 RNA_property_enum_set(&pref_ptr, active_section_prop, RNA_property_enum_get(op->ptr, prop));
6025 RNA_property_update(C, &pref_ptr, active_section_prop);
6026 }
6027
6028 const rcti window_rect = {
6029 /*xmin*/ event->xy[0],
6030 /*xmax*/ event->xy[0] + sizex,
6031 /*ymin*/ event->xy[1],
6032 /*ymax*/ event->xy[1] + sizey,
6033 };
6034
6035 /* changes context! */
6036 if (WM_window_open(C,
6037 nullptr,
6038 &window_rect,
6040 false,
6041 false,
6042 true,
6044 nullptr,
6045 nullptr) != nullptr)
6046 {
6047 /* The header only contains the editor switcher and looks empty.
6048 * So hiding in the temp window makes sense. */
6049 ScrArea *area = CTX_wm_area(C);
6051
6052 region->flag |= RGN_FLAG_HIDDEN;
6054
6055 return OPERATOR_FINISHED;
6056 }
6057 BKE_report(op->reports, RPT_ERROR, "Failed to open window!");
6058 return OPERATOR_CANCELLED;
6059}
6060
6062 wmOperatorType * /*ot*/,
6063 PointerRNA *ptr)
6064{
6065 PropertyRNA *prop = RNA_struct_find_property(ptr, "section");
6066 if (RNA_property_is_set(ptr, prop)) {
6067 int section = RNA_property_enum_get(ptr, prop);
6068 const char *section_name;
6069 if (RNA_property_enum_name_gettexted(C, ptr, prop, section, &section_name)) {
6070 return fmt::format(fmt::runtime(TIP_("Show {} preferences")), section_name);
6071 }
6072 }
6073 /* Fall back to default. */
6074 return "";
6075}
6076
6078{
6079 PropertyRNA *prop;
6080
6081 /* identifiers */
6082 ot->name = "Open Preferences...";
6083 ot->description = "Edit user preferences and system settings";
6084 ot->idname = "SCREEN_OT_userpref_show";
6085
6086 /* API callbacks. */
6087 ot->exec = userpref_show_exec;
6088 ot->poll = ED_operator_screenactive_nobackground; /* Not in background as this opens a window. */
6089 ot->get_description = userpref_show_get_description;
6090
6091 prop = RNA_def_enum(ot->srna,
6092 "section",
6094 0,
6095 "",
6096 "Section to activate in the Preferences");
6098}
6099
6101
6102/* -------------------------------------------------------------------- */
6105
6107{
6108 wmWindow *win_cur = CTX_wm_window(C);
6109 /* Use eventstate, not event from _invoke, so this can be called through exec(). */
6110 const wmEvent *event = win_cur->eventstate;
6111
6112 int sizex = 900 * UI_SCALE_FAC;
6113 int sizey = 580 * UI_SCALE_FAC;
6114
6115 /* Get active property to show driver for
6116 * - Need to grab it first, or else this info disappears
6117 * after we've created the window
6118 */
6119 int index;
6121 PropertyRNA *prop;
6122 uiBut *but = UI_context_active_but_prop_get(C, &ptr, &prop, &index);
6123
6124 const rcti window_rect = {
6125 /*xmin*/ event->xy[0],
6126 /*xmax*/ event->xy[0] + sizex,
6127 /*ymin*/ event->xy[1],
6128 /*ymax*/ event->xy[1] + sizey,
6129 };
6130
6131 /* changes context! */
6132 if (WM_window_open(C,
6133 IFACE_("Blender Drivers Editor"),
6134 &window_rect,
6136 false,
6137 false,
6138 true,
6140 nullptr,
6141 nullptr) != nullptr)
6142 {
6144
6145 /* activate driver F-Curve for the property under the cursor */
6146 if (but) {
6147 bool driven, special;
6149 C, &ptr, prop, index, nullptr, nullptr, &driven, &special);
6150
6151 if (fcu) {
6152 /* Isolate this F-Curve... */
6153 bAnimContext ac;
6154 if (ANIM_animdata_get_context(C, &ac)) {
6158 ac.data,
6161 fcu,
6163 }
6164 else {
6165 /* Just blindly isolate...
6166 * This isn't the best, and shouldn't happen, but may be enough. */
6168 }
6169 }
6170 }
6171
6172 return OPERATOR_FINISHED;
6173 }
6174 BKE_report(op->reports, RPT_ERROR, "Failed to open window!");
6175 return OPERATOR_CANCELLED;
6176}
6177
6179{
6180 /* identifiers */
6181 ot->name = "Show Drivers Editor";
6182 ot->description = "Show drivers editor in a separate window";
6183 ot->idname = "SCREEN_OT_drivers_editor_show";
6184
6185 /* API callbacks. */
6187 ot->poll = ED_operator_screenactive_nobackground; /* Not in background as this opens a window. */
6188}
6189
6191
6192/* -------------------------------------------------------------------- */
6195
6197{
6198 wmWindow *win_cur = CTX_wm_window(C);
6199 /* Use eventstate, not event from _invoke, so this can be called through exec(). */
6200 const wmEvent *event = win_cur->eventstate;
6201 const int shift_y = 480;
6202 const int mx = event->xy[0];
6203 const int my = event->xy[1] + shift_y;
6204 int sizex = 900 * UI_SCALE_FAC;
6205 int sizey = 580 * UI_SCALE_FAC;
6206
6207 const rcti window_rect = {
6208 /*xmin*/ mx,
6209 /*xmax*/ mx + sizex,
6210 /*ymin*/ my,
6211 /*ymax*/ my + sizey,
6212 };
6213
6214 /* changes context! */
6215 if (WM_window_open(C,
6216 IFACE_("Blender Info Log"),
6217 &window_rect,
6218 SPACE_INFO,
6219 false,
6220 false,
6221 true,
6223 nullptr,
6224 nullptr) != nullptr)
6225 {
6226 return OPERATOR_FINISHED;
6227 }
6228 BKE_report(op->reports, RPT_ERROR, "Failed to open window!");
6229 return OPERATOR_CANCELLED;
6230}
6231
6233{
6234 /* identifiers */
6235 ot->name = "Show Info Log";
6236 ot->description = "Show info log in a separate window";
6237 ot->idname = "SCREEN_OT_info_log_show";
6238
6239 /* API callbacks. */
6240 ot->exec = info_log_show_exec;
6242}
6243
6245
6246/* -------------------------------------------------------------------- */
6249
6251{
6252 Main *bmain = CTX_data_main(C);
6253 wmWindow *win = CTX_wm_window(C);
6256
6257 WorkSpaceLayout *layout_new = ED_workspace_layout_duplicate(bmain, workspace, layout_old, win);
6258
6260
6261 return OPERATOR_FINISHED;
6262}
6263
6265{
6266 /* identifiers */
6267 ot->name = "New Screen";
6268 ot->description = "Add a new screen";
6269 ot->idname = "SCREEN_OT_new";
6270
6271 /* API callbacks. */
6272 ot->exec = screen_new_exec;
6273 ot->poll = WM_operator_winactive;
6274}
6275
6277
6278/* -------------------------------------------------------------------- */
6281
6283{
6284 bScreen *screen = CTX_wm_screen(C);
6285 WorkSpace *workspace = CTX_wm_workspace(C);
6286 WorkSpaceLayout *layout = BKE_workspace_layout_find(workspace, screen);
6287
6289
6290 return OPERATOR_FINISHED;
6291}
6292
6294{
6295 /* identifiers */
6296 ot->name = "Delete Screen";
6297 ot->description = "Delete active screen";
6298 ot->idname = "SCREEN_OT_delete";
6299
6300 /* API callbacks. */
6301 ot->exec = screen_delete_exec;
6302}
6303
6305
6306/* -------------------------------------------------------------------- */
6314
6317 ARegion *region, *child_region; /* other region */
6319};
6320
6321#define TIMEOUT 0.1f
6322#define TIMESTEP (1.0f / 60.0f)
6323
6325{
6326 /* check parent too */
6327 if (region->runtime->regiontimer == nullptr &&
6328 (region->alignment & (RGN_SPLIT_PREV | RGN_ALIGN_HIDE_WITH_PREV)) && region->prev)
6329 {
6330 region = region->prev;
6331 }
6332
6333 if (region->runtime->regiontimer) {
6334 RegionAlphaInfo *rgi = static_cast<RegionAlphaInfo *>(
6335 region->runtime->regiontimer->customdata);
6336 float alpha;
6337
6338 alpha = float(region->runtime->regiontimer->time_duration) / TIMEOUT;
6339 /* makes sure the blend out works 100% - without area redraws */
6340 if (rgi->hidden) {
6341 alpha = 0.9f - TIMESTEP - alpha;
6342 }
6343
6344 CLAMP(alpha, 0.0f, 1.0f);
6345 return alpha;
6346 }
6347 return 1.0f;
6348}
6349
6350/* assumes region has running region-blend timer */
6351static void region_blend_end(bContext *C, ARegion *region, const bool is_running)
6352{
6353 RegionAlphaInfo *rgi = static_cast<RegionAlphaInfo *>(region->runtime->regiontimer->customdata);
6354
6355 /* always send redraw */
6356 ED_region_tag_redraw(region);
6357 if (rgi->child_region) {
6359 }
6360
6361 /* if running timer was hiding, the flag toggle went wrong */
6362 if (is_running) {
6363 if (rgi->hidden) {
6364 rgi->region->flag &= ~RGN_FLAG_HIDDEN;
6365 }
6366 }
6367 else {
6368 if (rgi->hidden) {
6369 rgi->region->flag |= rgi->hidden;
6371 }
6372 /* area decoration needs redraw in end */
6374 }
6375 WM_event_timer_remove(CTX_wm_manager(C), nullptr, region->runtime->regiontimer); /* frees rgi */
6376 region->runtime->regiontimer = nullptr;
6377}
6379{
6381 wmWindow *win = CTX_wm_window(C);
6382
6383 /* end running timer */
6384 if (region->runtime->regiontimer) {
6385
6386 region_blend_end(C, region, true);
6387 }
6388 RegionAlphaInfo *rgi = MEM_callocN<RegionAlphaInfo>("RegionAlphaInfo");
6389
6390 rgi->hidden = region->flag & RGN_FLAG_HIDDEN;
6391 rgi->area = area;
6392 rgi->region = region;
6393 region->flag &= ~RGN_FLAG_HIDDEN;
6394
6395 /* blend in, reinitialize regions because it got unhidden */
6396 if (rgi->hidden == 0) {
6397 ED_area_init(C, win, area);
6398 }
6399 else {
6400 ED_region_visibility_change_update_ex(C, area, region, true, false);
6401 }
6402
6403 if (region->next) {
6405 rgi->child_region = region->next;
6406 }
6407 }
6408
6409 /* new timer */
6410 region->runtime->regiontimer = WM_event_timer_add(wm, win, TIMERREGION, TIMESTEP);
6411 region->runtime->regiontimer->customdata = rgi;
6412}
6413
6414/* timer runs in win->handlers, so it cannot use context to find area/region */
6416{
6417 wmTimer *timer = static_cast<wmTimer *>(event->customdata);
6418
6419 /* event type is TIMERREGION, but we better check */
6420 if (event->type != TIMERREGION || timer == nullptr) {
6421 return OPERATOR_PASS_THROUGH;
6422 }
6423
6424 RegionAlphaInfo *rgi = static_cast<RegionAlphaInfo *>(timer->customdata);
6425
6426 /* always send redraws */
6428 if (rgi->child_region) {
6430 }
6431
6432 /* end timer? */
6433 if (rgi->region->runtime->regiontimer->time_duration > double(TIMEOUT)) {
6434 region_blend_end(C, rgi->region, false);
6436 }
6437
6439}
6440
6442{
6443 /* identifiers */
6444 ot->name = "Region Alpha";
6445 ot->idname = "SCREEN_OT_region_blend";
6446 ot->description = "Blend in and out overlapping region";
6447
6448 /* API callbacks. */
6449 ot->invoke = region_blend_invoke;
6450
6451 /* flags */
6452 ot->flag = OPTYPE_INTERNAL;
6453
6454 /* properties */
6455}
6456
6458
6459/* -------------------------------------------------------------------- */
6462
6464{
6465 ScrArea *area = CTX_wm_area(C);
6466 return (area && !ELEM(area->spacetype, SPACE_TOPBAR, SPACE_STATUSBAR));
6467}
6468
6470{
6471 const int space_type = RNA_enum_get(op->ptr, "space_type");
6472
6473 ScrArea *area = CTX_wm_area(C);
6475 PropertyRNA *prop_type = RNA_struct_find_property(&ptr, "type");
6476 PropertyRNA *prop_ui_type = RNA_struct_find_property(&ptr, "ui_type");
6477
6478 if (area->spacetype != space_type) {
6479 /* Set the type. */
6480 RNA_property_enum_set(&ptr, prop_type, space_type);
6481 /* Specify that we want last-used if there are subtypes. */
6482 area->butspacetype_subtype = -1;
6483 RNA_property_update(C, &ptr, prop_type);
6484 }
6485 else {
6486 /* Types match, cycle the subtype. */
6487 const int space_type_ui = RNA_property_enum_get(&ptr, prop_ui_type);
6488 const EnumPropertyItem *item;
6489 int item_len;
6490 bool free;
6491 RNA_property_enum_items(C, &ptr, prop_ui_type, &item, &item_len, &free);
6492 int index = RNA_enum_from_value(item, space_type_ui);
6493 for (int i = 1; i < item_len; i++) {
6494 const EnumPropertyItem *item_test = &item[(index + i) % item_len];
6495 if ((item_test->value >> 16) == space_type) {
6496 RNA_property_enum_set(&ptr, prop_ui_type, item_test->value);
6497 RNA_property_update(C, &ptr, prop_ui_type);
6498 break;
6499 }
6500 }
6501 if (free) {
6502 MEM_freeN(item);
6503 }
6504 }
6505
6506 return OPERATOR_FINISHED;
6507}
6508
6510{
6511 /* identifiers */
6512 ot->name = "Cycle Space Type Set";
6513 ot->description = "Set the space type or cycle subtype";
6514 ot->idname = "SCREEN_OT_space_type_set_or_cycle";
6515
6516 /* API callbacks. */
6519
6520 ot->flag = 0;
6521
6522 RNA_def_enum(ot->srna, "space_type", rna_enum_space_type_items, SPACE_EMPTY, "Type", "");
6523}
6524
6526
6527/* -------------------------------------------------------------------- */
6530
6532 {SPACE_CONTEXT_CYCLE_PREV, "PREV", 0, "Previous", ""},
6533 {SPACE_CONTEXT_CYCLE_NEXT, "NEXT", 0, "Next", ""},
6534 {0, nullptr, 0, nullptr, nullptr},
6535};
6536
6538{
6539 ScrArea *area = CTX_wm_area(C);
6540 /* area might be nullptr if called out of window bounds */
6541 return (area && ELEM(area->spacetype, SPACE_PROPERTIES, SPACE_USERPREF));
6542}
6543
6549 const ScrArea *area,
6550 PointerRNA *r_ptr,
6551 PropertyRNA **r_prop)
6552{
6553 const char *propname;
6554
6555 switch (area->spacetype) {
6556 case SPACE_PROPERTIES:
6558 &screen->id, &RNA_SpaceProperties, area->spacedata.first);
6559 propname = "context";
6560 break;
6561 case SPACE_USERPREF:
6562 *r_ptr = RNA_pointer_create_discrete(nullptr, &RNA_Preferences, &U);
6563 propname = "active_section";
6564 break;
6565 default:
6566 BLI_assert(0);
6567 propname = "";
6568 }
6569
6570 *r_prop = RNA_struct_find_property(r_ptr, propname);
6571}
6572
6574 wmOperator *op,
6575 const wmEvent * /*event*/)
6576{
6577 const eScreenCycle direction = eScreenCycle(RNA_enum_get(op->ptr, "direction"));
6578
6580 PropertyRNA *prop;
6582 const int old_context = RNA_property_enum_get(&ptr, prop);
6583 const int new_context = RNA_property_enum_step(
6584 C, &ptr, prop, old_context, direction == SPACE_CONTEXT_CYCLE_PREV ? -1 : 1);
6585 RNA_property_enum_set(&ptr, prop, new_context);
6586 RNA_property_update(C, &ptr, prop);
6587
6588 return OPERATOR_FINISHED;
6589}
6590
6592{
6593 /* identifiers */
6594 ot->name = "Cycle Space Context";
6595 ot->description = "Cycle through the editor context by activating the next/previous one";
6596 ot->idname = "SCREEN_OT_space_context_cycle";
6597
6598 /* API callbacks. */
6601
6602 ot->flag = 0;
6603
6604 RNA_def_enum(ot->srna,
6605 "direction",
6608 "Direction",
6609 "Direction to cycle through");
6610}
6611
6613
6614/* -------------------------------------------------------------------- */
6617
6619 wmOperator *op,
6620 const wmEvent * /*event*/)
6621{
6622 wmWindow *win = CTX_wm_window(C);
6623 if (WM_window_is_temp_screen(win)) {
6624 return OPERATOR_CANCELLED;
6625 }
6626
6627 Main *bmain = CTX_data_main(C);
6628 const eScreenCycle direction = eScreenCycle(RNA_enum_get(op->ptr, "direction"));
6629 WorkSpace *workspace_src = WM_window_get_active_workspace(win);
6630
6631 Vector<ID *> ordered = BKE_id_ordered_list(&bmain->workspaces);
6632 if (ordered.size() == 1) {
6633 return OPERATOR_CANCELLED;
6634 }
6635
6636 const int index = ordered.first_index_of(&workspace_src->id);
6637
6638 WorkSpace *workspace_dst = nullptr;
6639 switch (direction) {
6641 workspace_dst = reinterpret_cast<WorkSpace *>(index == 0 ? ordered.last() :
6642 ordered[index - 1]);
6643 break;
6645 workspace_dst = reinterpret_cast<WorkSpace *>(
6646 index == ordered.index_range().last() ? ordered.first() : ordered[index + 1]);
6647 break;
6648 }
6649
6650 win->workspace_hook->temp_workspace_store = workspace_dst;
6652 win->workspace_hook->temp_workspace_store = nullptr;
6653
6654 return OPERATOR_FINISHED;
6655}
6656
6658{
6659 /* identifiers */
6660 ot->name = "Cycle Workspace";
6661 ot->description = "Cycle through workspaces";
6662 ot->idname = "SCREEN_OT_workspace_cycle";
6663
6664 /* API callbacks. */
6667
6668 ot->flag = 0;
6669
6670 RNA_def_enum(ot->srna,
6671 "direction",
6674 "Direction",
6675 "Direction to cycle through");
6676}
6677
6679
6680/* -------------------------------------------------------------------- */
6683
6685{
6686 /* Generic UI stuff. */
6691
6692 /* Screen tools. */
6719
6720 /* Frame changes. */
6725
6729
6730 /* New/delete. */
6733}
6734
6736
6737/* -------------------------------------------------------------------- */
6740
6741static void keymap_modal_set(wmKeyConfig *keyconf)
6742{
6743 static const EnumPropertyItem modal_items[] = {
6744 {KM_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""},
6745 {KM_MODAL_APPLY, "APPLY", 0, "Apply", ""},
6746 {KM_MODAL_SNAP_ON, "SNAP", 0, "Snap On", ""},
6747 {KM_MODAL_SNAP_OFF, "SNAP_OFF", 0, "Snap Off", ""},
6748 {0, nullptr, 0, nullptr, nullptr},
6749 };
6750
6751 /* Standard Modal keymap ------------------------------------------------ */
6752 wmKeyMap *keymap = WM_modalkeymap_ensure(keyconf, "Standard Modal Map", modal_items);
6753
6754 WM_modalkeymap_assign(keymap, "SCREEN_OT_area_move");
6755}
6756
6757static bool blend_file_drop_poll(bContext * /*C*/, wmDrag *drag, const wmEvent * /*event*/)
6758{
6759 if (drag->type == WM_DRAG_PATH) {
6762 return true;
6763 }
6764 }
6765 return false;
6766}
6767
6768static void blend_file_drop_copy(bContext * /*C*/, wmDrag *drag, wmDropBox *drop)
6769{
6770 /* copy drag path to properties */
6771 RNA_string_set(drop->ptr, "filepath", WM_drag_get_single_path(drag));
6772}
6773
6775{
6776 /* Screen Editing ------------------------------------------------ */
6777 WM_keymap_ensure(keyconf, "Screen Editing", SPACE_EMPTY, RGN_TYPE_WINDOW);
6778
6779 /* Screen General ------------------------------------------------ */
6780 WM_keymap_ensure(keyconf, "Screen", SPACE_EMPTY, RGN_TYPE_WINDOW);
6781
6782 /* Anim Playback ------------------------------------------------ */
6783 WM_keymap_ensure(keyconf, "Frames", SPACE_EMPTY, RGN_TYPE_WINDOW);
6784
6785 /* dropbox for entire window */
6788 lb, "WM_OT_drop_blend_file", blend_file_drop_poll, blend_file_drop_copy, nullptr, nullptr);
6789 WM_dropbox_add(lb, "UI_OT_drop_color", UI_drop_color_poll, UI_drop_color_copy, nullptr, nullptr);
6790
6791 keymap_modal_set(keyconf);
6792}
6793
@ BKE_CB_EVT_ANIMATION_PLAYBACK_PRE
@ BKE_CB_EVT_ANIMATION_PLAYBACK_POST
void BKE_callback_exec_id_depsgraph(Main *bmain, ID *id, Depsgraph *depsgraph, eCbEvent evt)
Definition callbacks.cc:52
WorkSpace * CTX_wm_workspace(const bContext *C)
SpaceImage * CTX_wm_space_image(const bContext *C)
bScreen * CTX_wm_screen(const bContext *C)
Mask * CTX_data_edit_mask(const bContext *C)
SpaceFile * CTX_wm_space_file(const bContext *C)
PointerRNA CTX_data_pointer_get_type(const bContext *C, const char *member, StructRNA *type)
SpaceNode * CTX_wm_space_node(const bContext *C)
void CTX_wm_operator_poll_msg_set(bContext *C, const char *msg)
Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
ScrArea * CTX_wm_area(const bContext *C)
wmWindow * CTX_wm_window(const bContext *C)
Object * CTX_data_active_object(const bContext *C)
SpaceLink * CTX_wm_space_data(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Object * CTX_data_edit_object(const bContext *C)
void CTX_wm_window_set(bContext *C, wmWindow *win)
Main * CTX_data_main(const bContext *C)
void CTX_wm_area_set(bContext *C, ScrArea *area)
void CTX_wm_region_set(bContext *C, ARegion *region)
RegionView3D * CTX_wm_region_view3d(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
BMEditMesh * BKE_editmesh_from_object(Object *ob)
Return the BMEditMesh for a given object.
Definition editmesh.cc:61
FCurve * BKE_fcurve_find_by_rna_context_ui(bContext *C, const PointerRNA *ptr, PropertyRNA *prop, int rnaindex, AnimData **r_animdata, bAction **r_action, bool *r_driven, bool *r_special)
@ G_TRANSFORM_WM
void BKE_icon_changed(int icon_id)
Definition icons.cc:204
bool BKE_id_is_editable(const Main *bmain, const ID *id)
Definition lib_id.cc:2503
blender::Vector< ID * > BKE_id_ordered_list(const ListBase *lb)
Definition lib_id.cc:2556
struct MaskLayer * BKE_mask_layer_active(struct Mask *mask)
General operations, lookup, etc. for blender objects.
bool BKE_object_pose_context_check(const Object *ob)
Object * BKE_object_pose_armature_get(Object *ob)
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:126
void BKE_scene_frame_set(Scene *scene, float frame)
Definition scene.cc:2386
float BKE_scene_frame_get(const Scene *scene)
Definition scene.cc:2381
Depsgraph * BKE_scene_get_depsgraph(const Scene *scene, const ViewLayer *view_layer)
Definition scene.cc:3414
void BKE_screen_remove_unused_scrverts(bScreen *screen)
Definition screen.cc:804
void BKE_screen_remove_unused_scredges(bScreen *screen)
Definition screen.cc:759
void BKE_screen_remove_double_scrverts(bScreen *screen)
Definition screen.cc:691
void BKE_spacedata_freelist(ListBase *lb)
Definition screen.cc:299
ScrEdge * BKE_screen_find_edge(const bScreen *screen, ScrVert *v1, ScrVert *v2)
Definition screen.cc:670
ScrArea ScrArea * BKE_screen_find_area_xy(const bScreen *screen, int spacetype, const int xy[2]) ATTR_NONNULL(1
void BKE_screen_remove_double_scredges(bScreen *screen)
Definition screen.cc:743
ARegion * BKE_area_find_region_type(const ScrArea *area, int region_type)
Definition screen.cc:840
ARegion * BKE_area_region_copy(const SpaceType *st, const ARegion *region)
Definition screen.cc:344
double BKE_sound_sync_scene(struct Scene *scene)
void BKE_sound_init(struct Main *bmain)
void BKE_sound_stop_scene(struct Scene *scene)
void BKE_sound_play_scene(struct Scene *scene)
WorkSpaceLayout * BKE_workspace_layout_find(const WorkSpace *workspace, const bScreen *screen) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT
Definition workspace.cc:427
bScreen * BKE_workspace_active_screen_get(const WorkSpaceInstanceHook *hook) GETTER_ATTRS
Definition workspace.cc:612
WorkSpace * BKE_workspace_active_get(WorkSpaceInstanceHook *hook) GETTER_ATTRS
Definition workspace.cc:561
WorkSpaceLayout * BKE_workspace_active_layout_get(const WorkSpaceInstanceHook *hook) GETTER_ATTRS
Definition workspace.cc:585
#define BLI_assert(a)
Definition BLI_assert.h:46
void BLI_kdtree_nd_ free(KDTree *tree)
int BLI_findindex(const ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:586
void * BLI_findlink(const ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:534
#define LISTBASE_FOREACH(type, var, list)
BLI_INLINE bool BLI_listbase_is_empty(const ListBase *lb)
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
void BLI_remlink(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:131
int BLI_listbase_count(const ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:524
void void BLI_INLINE bool BLI_listbase_is_single(const ListBase *lb)
MINLINE int round_fl_to_int(float a)
MINLINE int min_ii(int a, int b)
MINLINE int square_i(int a)
MINLINE int max_ii(int a, int b)
void copy_qt_qt(float q[4], const float a[4])
MINLINE void copy_v2_v2_int(int r[2], const int a[2])
MINLINE int len_manhattan_v2v2_int(const int a[2], const int b[2]) ATTR_WARN_UNUSED_RESULT
bool BLI_rcti_isect_pt_v(const struct rcti *rect, const int xy[2])
int BLI_rcti_length_x(const rcti *rect, int x)
Definition rct.cc:149
void BLI_rcti_translate(struct rcti *rect, int x, int y)
Definition rct.cc:566
int BLI_rcti_length_y(const rcti *rect, int y)
Definition rct.cc:160
void BLI_rcti_init(struct rcti *rect, int xmin, int xmax, int ymin, int ymax)
Definition rct.cc:414
BLI_INLINE int BLI_rcti_size_x(const struct rcti *rct)
Definition BLI_rect.h:194
BLI_INLINE float BLI_rctf_size_x(const struct rctf *rct)
Definition BLI_rect.h:202
#define CLAMP(a, b, c)
#define ARRAY_SIZE(arr)
#define ELEM(...)
#define IS_EQF(a, b)
#define IN_RANGE_INCL(a, b, c)
#define TIP_(msgid)
#define CTX_IFACE_(context, msgid)
#define BLT_I18NCONTEXT_ID_SCREEN
#define IFACE_(msgid)
void DEG_id_tag_update(ID *id, unsigned int flags)
Scene * DEG_get_evaluated_scene(const Depsgraph *graph)
@ ID_RECALC_FRAME_CHANGE
Definition DNA_ID.h:1033
@ ADS_FILTER_ONLYSEL
@ SACTCONT_TIMELINE
@ FCURVE_ACTIVE
@ FCURVE_SELECTED
@ CU_3D
#define OB_MODE_ALL_WEIGHT_PAINT
@ OB_MODE_EDIT
@ OB_MODE_OBJECT
Object is a sort of wrapper for general info.
@ OB_HIDE_VIEWPORT
@ OB_LATTICE
@ OB_MBALL
@ OB_SURF
@ OB_FONT
@ OB_GREASE_PENCIL
@ OB_ARMATURE
@ OB_MESH
@ OB_CURVES_LEGACY
#define PSFRA
@ SCE_FRAME_DROP
@ SCE_KEYS_NO_SELONLY
@ AUDIO_SYNC
#define FPS
#define PRVRANGEON
#define PEFRA
#define HEADERY
#define RGN_ALIGN_ENUM_FROM_MASK(align)
@ SCREENFULL
@ SCREENMAXIMIZED
@ SCREENNORMAL
#define AREAMAP_FROM_SCREEN(screen)
#define AREAMINX
eRegion_Type
@ RGN_TYPE_TOOL_HEADER
@ RGN_TYPE_UI
@ RGN_TYPE_WINDOW
@ RGN_TYPE_PREVIEW
@ RGN_TYPE_NAV_BAR
@ RGN_TYPE_FOOTER
@ RGN_TYPE_HEADER
@ RGN_TYPE_TOOLS
eScreen_Redraws_Flag
@ TIME_SEQ
@ TIME_ALL_IMAGE_WIN
@ TIME_ALL_BUTS_WIN
@ TIME_FOLLOW
@ TIME_REGION
@ TIME_ALL_3D_WIN
@ TIME_SPREADSHEETS
@ TIME_CLIPS
@ TIME_NODES
@ TIME_ALL_ANIM_WIN
@ RGN_FLAG_RESIZE_RESPECT_BUTTON_SECTIONS
@ RGN_FLAG_HIDDEN
@ RGN_FLAG_NO_USER_RESIZE
@ RGN_FLAG_TOO_SMALL
@ RGN_FLAG_HIDDEN_BY_USER
@ AREA_FLAG_ACTIONZONES_UPDATE
@ HEADER_NO_PULLDOWN
#define AREAGRID
@ RGN_SPLIT_SCALE_PREV
@ RGN_ALIGN_HIDE_WITH_PREV
@ RGN_ALIGN_BOTTOM
@ RGN_ALIGN_LEFT
@ RGN_ALIGN_TOP
@ RGN_ALIGN_RIGHT
@ RGN_SPLIT_PREV
@ RGN_ALIGN_NONE
@ RGN_ALIGN_QSPLIT
eFileSel_File_Types
@ FILE_TYPE_BLENDER
@ FILE_TYPE_BLENDER_BACKUP
eSpace_Type
@ SPACE_CLIP
@ SPACE_ACTION
@ SPACE_CONSOLE
@ SPACE_OUTLINER
@ SPACE_STATUSBAR
@ SPACE_TOPBAR
@ SPACE_NODE
@ SPACE_SPREADSHEET
@ SPACE_USERPREF
@ SPACE_FILE
@ SPACE_PROPERTIES
@ SPACE_NLA
@ SPACE_SEQ
@ SPACE_EMPTY
@ SPACE_IMAGE
@ SPACE_GRAPH
@ SPACE_VIEW3D
@ SPACE_INFO
@ SIPO_MODE_DRIVERS
#define SPACE_TYPE_ANY
#define UI_SCALE_FAC
#define FRAMENUMBER_MIN_CLAMP(cfra)
@ USER_SHOW_FPS
@ V2D_SCROLL_LEFT
@ V2D_SCROLL_HORIZONTAL
@ V2D_SCROLL_TOP
@ V2D_SCROLL_RIGHT
@ V2D_SCROLL_BOTTOM
@ V2D_SCROLL_VERTICAL
@ V2D_IS_INIT
@ V2D_KEEPTOT_STRICT
@ RV3D_LOCK_ROTATION
@ RV3D_BOXCLIP
@ RV3D_VIEWLOCK_INIT
@ RV3D_CAMOB
@ RV3D_PERSP
@ RV3D_ORTHO
@ RV3D_VIEW_AXIS_ROLL_0
@ RV3D_GPULIGHT_UPDATE
@ RV3D_VIEW_CAMERA
@ RV3D_VIEW_USER
@ OPERATOR_CANCELLED
@ OPERATOR_INTERFACE
@ OPERATOR_FINISHED
@ OPERATOR_RUNNING_MODAL
@ OPERATOR_PASS_THROUGH
@ ACHANNEL_SETFLAG_CLEAR
@ ANIMTYPE_FCURVE
eAnimCont_Types
@ ANIMCONT_DRIVERS
eAnimFilter_Flags
@ ANIMFILTER_DATA_VISIBLE
@ ANIMFILTER_NODUPLIS
void ED_drivers_editor_init(bContext *C, ScrArea *area)
void ED_buttons_visible_tabs_menu(bContext *C, uiLayout *layout, void *)
bool ED_fileselect_is_file_browser(const SpaceFile *sfile)
Definition filesel.cc:465
bool ED_fileselect_is_asset_browser(const SpaceFile *sfile)
Definition filesel.cc:470
bool ED_space_image_show_uvedit(const SpaceImage *sima, Object *obedit)
Mesh * ED_mesh_context(bContext *C)
void ED_scene_fps_average_accumulate(Scene *scene, short fps_samples, double ltime) ATTR_NONNULL(1)
Definition scene_fps.cc:93
bool void ED_scene_fps_average_clear(Scene *scene) ATTR_NONNULL(1)
Definition scene_fps.cc:72
void ED_area_tag_redraw(ScrArea *area)
Definition area.cc:714
void ED_screen_global_areas_sync(wmWindow *win)
void ED_area_init(bContext *C, const wmWindow *win, ScrArea *area)
Definition area.cc:2144
bool ED_workspace_layout_cycle(WorkSpace *workspace, short direction, bContext *C) ATTR_NONNULL()
bool ED_operator_screen_mainwinactive(bContext *C)
int ED_area_max_regionsize(const ScrArea *area, const ARegion *scale_region, const AZEdge edge)
Definition area.cc:783
void ED_region_toggle_hidden(bContext *C, ARegion *region)
Definition area.cc:2377
int ED_area_global_min_size_y(const ScrArea *area)
Definition area.cc:3790
ScrArea * ED_area_find_under_cursor(const bContext *C, int spacetype, const int event_xy[2])
Definition area.cc:3806
#define ED_screen_verts_iter(win, screen, vert_name)
Definition ED_screen.hh:291
#define ED_screen_areas_iter(win, screen, area_name)
Definition ED_screen.hh:288
bool ED_operator_screenactive(bContext *C)
void ED_area_tag_redraw_no_rebuild(ScrArea *area)
Definition area.cc:723
int ED_area_global_max_size_y(const ScrArea *area)
Definition area.cc:3795
bool ED_area_is_global(const ScrArea *area)
Definition area.cc:3801
void ED_area_swapspace(bContext *C, ScrArea *sa1, ScrArea *sa2)
Definition area.cc:2667
void ED_region_tag_redraw_no_rebuild(ARegion *region)
Definition area.cc:659
int ED_area_headersize()
Definition area.cc:3774
void ED_screen_animation_timer(bContext *C, int redraws, int sync, int enable)
bool ED_operator_screenactive_nobackground(bContext *C)
bool ED_operator_region_view3d_active(bContext *C)
void ED_region_visibility_change_update(bContext *C, ScrArea *area, ARegion *region)
Definition area.cc:2355
bool ED_operator_areaactive(bContext *C)
ScrArea * ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *area, short state)
void ED_workspace_status_text(bContext *C, const char *str)
Definition area.cc:1040
void ED_update_for_newframe(Main *bmain, Depsgraph *depsgraph)
WorkSpaceLayout * ED_workspace_layout_duplicate(Main *bmain, WorkSpace *workspace, const WorkSpaceLayout *layout_old, wmWindow *win) ATTR_NONNULL()
void ED_region_visibility_change_update_ex(bContext *C, ScrArea *area, ARegion *region, bool is_hidden, bool do_init)
Definition area.cc:2336
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:639
void ED_region_remove(bContext *C, ScrArea *area, ARegion *region)
void ED_screen_full_prevspace(bContext *C, ScrArea *area)
eScreenCycle
Definition ED_screen.hh:754
@ SPACE_CONTEXT_CYCLE_NEXT
Definition ED_screen.hh:756
@ SPACE_CONTEXT_CYCLE_PREV
Definition ED_screen.hh:755
@ AZ_SCROLL_HOR
@ AZ_SCROLL_VERT
@ ANIMPLAY_FLAG_JUMPED
@ ANIMPLAY_FLAG_NO_SYNC
@ ANIMPLAY_FLAG_REVERSE
@ ANIMPLAY_FLAG_SYNC
@ ANIMPLAY_FLAG_USE_NEXT_FRAME
@ AZONE_REGION
@ AZONE_FULLSCREEN
@ AZONE_REGION_SCROLL
@ AZONE_AREA
@ AE_LEFT_TO_TOPRIGHT
@ AE_RIGHT_TO_TOPLEFT
@ AE_BOTTOM_TO_TOPLEFT
@ AE_TOP_TO_BOTTOMRIGHT
bool ED_view3d_context_user_region(bContext *C, View3D **r_v3d, ARegion **r_region)
bool ED_view3d_lock(RegionView3D *rv3d)
char ED_view3d_lock_view_from_index(int index)
void ED_view3d_lastview_store(RegionView3D *rv3d)
void ED_view3d_quadview_update(ScrArea *area, ARegion *region, bool do_clip)
static AppView * view
bool GPU_mem_stats_supported()
Read Guarded memory(de)allocation.
@ PROP_SKIP_SAVE
Definition RNA_types.hh:330
@ PROP_HIDDEN
Definition RNA_types.hh:324
#define C
Definition RandGen.cpp:29
#define UI_REGION_OVERLAP_MARGIN
#define UI_UNIT_Y
#define UI_AZONESPOTW
#define UI_NAVIGATION_REGION_WIDTH
bool UI_drop_color_poll(bContext *C, wmDrag *drag, const wmEvent *event)
void UI_popup_menu_end(bContext *C, uiPopupMenu *pup)
uiPopupMenu * UI_popup_menu_begin(bContext *C, const char *title, int icon) ATTR_NONNULL()
uiBut * UI_context_active_but_prop_get(const bContext *C, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index)
uiLayout * UI_popup_menu_layout(uiPopupMenu *pup)
void UI_drop_color_copy(bContext *C, wmDrag *drag, wmDropBox *drop)
bool UI_region_button_sections_is_inside_x(const ARegion *region, const int mval_x)
#define UI_UNIT_X
void uiLayoutSetActive(uiLayout *layout, bool active)
#define UI_ITEM_NONE
void uiLayoutSetOperatorContext(uiLayout *layout, wmOperatorCallContext opcontext)
void UI_view2d_curRect_validate(View2D *v2d)
Definition view2d.cc:828
char UI_view2d_mouse_in_scrollers_ex(const ARegion *region, const View2D *v2d, const int xy[2], int *r_scroll) ATTR_NONNULL(1
float UI_view2d_view_to_region_y(const View2D *v2d, float y)
Definition view2d.cc:1695
#define V2D_SCROLL_HIDE_WIDTH
Definition UI_view2d.hh:63
float UI_view2d_view_to_region_x(const View2D *v2d, float x)
Definition view2d.cc:1690
#define V2D_SCROLL_HIDE_HEIGHT
Definition UI_view2d.hh:64
@ WIN_ALIGN_LOCATION_CENTER
Definition WM_api.hh:362
@ WIN_ALIGN_ABSOLUTE
Definition WM_api.hh:361
#define ND_SPACE_SEQUENCER
Definition WM_types.hh:532
#define NC_WINDOW
Definition WM_types.hh:372
@ KM_CTRL
Definition WM_types.hh:276
@ KM_ALT
Definition WM_types.hh:277
eWM_EventFlag
Definition WM_types.hh:668
@ KM_NOTHING
Definition WM_types.hh:307
@ KM_PRESS
Definition WM_types.hh:308
@ KM_RELEASE
Definition WM_types.hh:309
#define NC_SCREEN
Definition WM_types.hh:374
#define NC_SCENE
Definition WM_types.hh:375
#define NA_EDITED
Definition WM_types.hh:581
#define ND_FRAME
Definition WM_types.hh:431
#define ND_TRANSFORM
Definition WM_types.hh:453
@ WM_OP_INVOKE_DEFAULT
Definition WM_types.hh:238
@ WM_OP_EXEC_DEFAULT
Definition WM_types.hh:245
#define ND_WORKSPACE_SET
Definition WM_types.hh:425
@ WM_DRAG_PATH
Definition WM_types.hh:1205
#define ND_LAYOUTBROWSE
Definition WM_types.hh:419
#define ND_SPACE_SPREADSHEET
Definition WM_types.hh:538
#define ND_LAYOUTDELETE
Definition WM_types.hh:420
@ OPTYPE_INTERNAL
Definition WM_types.hh:202
@ OPTYPE_BLOCKING
Definition WM_types.hh:184
@ OPTYPE_UNDO_GROUPED
Definition WM_types.hh:207
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define NC_SPACE
Definition WM_types.hh:389
void ANIM_anim_channels_select_set(bAnimContext *ac, eAnimChannels_SetFlag sel)
void ANIM_set_active_channel(bAnimContext *ac, void *data, eAnimCont_Types datatype, eAnimFilter_Flags filter, void *channel_data, eAnim_ChannelType channel_type)
void ANIM_animdata_freelist(ListBase *anim_data)
Definition anim_deps.cc:463
bool ANIM_nla_mapping_allowed(const bAnimListElem *ale)
Definition anim_draw.cc:212
bool ANIM_animdata_get_context(const bContext *C, bAnimContext *ac)
void region_toggle_hidden(bContext *C, ARegion *region, const bool do_fade)
Definition area.cc:2362
void ED_area_data_copy(ScrArea *area_dst, ScrArea *area_src, const bool do_free)
Definition area.cc:2382
#define U
ATTR_WARN_UNUSED_RESULT const BMVert * v2
BPy_StructRNA * depsgraph
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
bool closest(btVector3 &v)
void item_bool(std::string text, bool inverted, int icon1, int icon2=0)
Definition area.cc:995
void item(std::string text, int icon1, int icon2=0)
Definition area.cc:979
int64_t size() const
const T & last(const int64_t n=0) const
IndexRange index_range() const
int64_t first_index_of(const T &value) const
const T & first() const
static float is_left(const float p0[2], const float p1[2], const float p2[2])
uint pos
uint col
#define round
#define abs
#define filter
VecBase< float, D > step(VecOp< float, D >, VecOp< float, D >) RET
#define printf(...)
#define ID_IS_EDITABLE(_id)
#define ID_IS_OVERRIDE_LIBRARY(_id)
int count
void mask_to_keylist(bDopeSheet *, MaskLayer *masklay, AnimKeylist *keylist)
const ActKeyColumn * ED_keylist_find_prev(const AnimKeylist *keylist, const float cfra)
void fcurve_to_keylist(AnimData *adt, FCurve *fcu, AnimKeylist *keylist, const int saction_flag, blender::float2 range, const bool use_nla_remapping)
void scene_to_keylist(bDopeSheet *ads, Scene *sce, AnimKeylist *keylist, const int saction_flag, blender::float2 range)
void ob_to_keylist(bDopeSheet *ads, Object *ob, AnimKeylist *keylist, const int saction_flag, blender::float2 range)
void grease_pencil_data_block_to_keylist(AnimData *adt, const GreasePencil *grease_pencil, AnimKeylist *keylist, const int saction_flag, const bool active_layer_only)
void ED_keylist_prepare_for_direct_access(AnimKeylist *keylist)
void summary_to_keylist(bAnimContext *ac, AnimKeylist *keylist, const int saction_flag, blender::float2 range)
AnimKeylist * ED_keylist_create()
void ED_keylist_free(AnimKeylist *keylist)
const ActKeyColumn * ED_keylist_find_next(const AnimKeylist *keylist, const float cfra)
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
static wmOperatorStatus box_select_exec(bContext *C, wmOperator *op)
ccl_device_inline float2 fabs(const float2 a)
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
static ulong * next
static ulong state[N]
#define G(x, y, z)
ListBase get_editable_fcurves(bAnimContext &ac)
Object * context_active_object(const bContext *C)
bool has_playback_animation(const Scene *scene)
VecBase< int32_t, 2 > int2
Object * ED_pose_object_from_context(bContext *C)
Definition pose_edit.cc:58
void RNA_int_set_array(PointerRNA *ptr, const char *name, const int *values)
void RNA_int_get_array(PointerRNA *ptr, const char *name, int *values)
void RNA_string_set(PointerRNA *ptr, const char *name, const char *value)
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
void RNA_boolean_set(PointerRNA *ptr, const char *name, bool value)
bool RNA_property_is_set(PointerRNA *ptr, PropertyRNA *prop)
void RNA_int_set(PointerRNA *ptr, const char *name, int value)
void RNA_property_enum_set(PointerRNA *ptr, PropertyRNA *prop, int value)
int RNA_property_enum_step(const bContext *C, PointerRNA *ptr, PropertyRNA *prop, int from_value, int step)
void RNA_property_update(bContext *C, PointerRNA *ptr, PropertyRNA *prop)
bool RNA_property_enum_name_gettexted(bContext *C, PointerRNA *ptr, PropertyRNA *prop, const int value, const char **r_name)
int RNA_int_get(PointerRNA *ptr, const char *name)
float RNA_float_get(PointerRNA *ptr, const char *name)
void RNA_float_set(PointerRNA *ptr, const char *name, float value)
int RNA_property_enum_get(PointerRNA *ptr, PropertyRNA *prop)
void RNA_property_float_set(PointerRNA *ptr, PropertyRNA *prop, float value)
void RNA_property_int_get_array(PointerRNA *ptr, PropertyRNA *prop, int *values)
int RNA_enum_from_value(const EnumPropertyItem *item, const int value)
bool RNA_struct_property_is_set(PointerRNA *ptr, const char *identifier)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
void RNA_property_enum_items(bContext *C, PointerRNA *ptr, PropertyRNA *prop, const EnumPropertyItem **r_item, int *r_totitem, bool *r_free)
void RNA_enum_set(PointerRNA *ptr, const char *name, int value)
PointerRNA RNA_pointer_create_discrete(ID *id, StructRNA *type, void *data)
int RNA_enum_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_float(StructOrFunctionRNA *cont_, const char *identifier, const float default_value, const float hardmin, const float hardmax, const char *ui_name, const char *ui_description, const float softmin, const float softmax)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_int_vector(StructOrFunctionRNA *cont_, const char *identifier, const int len, const int *default_value, const int hardmin, const int hardmax, const char *ui_name, const char *ui_description, const int softmin, const int softmax)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
PropertyRNA * RNA_def_int(StructOrFunctionRNA *cont_, const char *identifier, const int default_value, const int hardmin, const int hardmax, const char *ui_name, const char *ui_description, const int softmin, const int softmax)
const EnumPropertyItem rna_enum_region_type_items[]
Definition rna_screen.cc:21
const EnumPropertyItem rna_enum_space_type_items[]
Definition rna_space.cc:72
const EnumPropertyItem rna_enum_preference_section_items[]
void screen_draw_region_scale_highlight(ARegion *region)
void screen_draw_move_highlight(const wmWindow *win, bScreen *screen, eScreenAxis dir_axis)
void screen_draw_split_preview(ScrArea *area, const eScreenAxis dir_axis, const float factor)
void screen_draw_join_highlight(const wmWindow *win, ScrArea *sa1, ScrArea *sa2, eScreenDir dir)
void screen_draw_dock_preview(const wmWindow *win, ScrArea *source, ScrArea *target, AreaDockTarget dock_target, float factor, int x, int y)
void area_getoffsets(ScrArea *sa_a, ScrArea *sa_b, const eScreenDir dir, int *r_offset1, int *r_offset2)
eScreenDir area_getorientation(ScrArea *sa_a, ScrArea *sa_b)
bool screen_area_close(bContext *C, ReportList *reports, bScreen *screen, ScrArea *area)
ScrArea * area_split(const wmWindow *win, bScreen *screen, ScrArea *area, const eScreenAxis dir_axis, const float fac, const bool merge)
int screen_area_join(bContext *C, ReportList *reports, bScreen *screen, ScrArea *sa1, ScrArea *sa2)
ScrEdge * screen_geom_find_active_scredge(const wmWindow *win, const bScreen *screen, const int mx, const int my)
void screen_geom_select_connected_edge(const wmWindow *win, ScrEdge *edge)
bool screen_geom_edge_is_horizontal(ScrEdge *se)
ScrEdge * screen_geom_area_map_find_active_scredge(const ScrAreaMap *area_map, const rcti *bounds_rect, const int mx, const int my, int safety)
int screen_geom_area_height(const ScrArea *area)
void SCREEN_OT_screenshot(wmOperatorType *ot)
eScreenAxis
@ SCREEN_AXIS_V
@ SCREEN_AXIS_H
#define AZONEFADEIN
void SCREEN_OT_screenshot_area(wmOperatorType *ot)
#define SCREEN_DIR_IS_VERTICAL(dir)
eScreenDir
@ SCREEN_DIR_W
@ SCREEN_DIR_N
@ SCREEN_DIR_E
@ SCREEN_DIR_S
@ SCREEN_DIR_NONE
#define AZONEFADEOUT
AreaDockTarget
static bool blend_file_drop_poll(bContext *, wmDrag *drag, const wmEvent *)
static wmOperatorStatus screen_delete_exec(bContext *C, wmOperator *)
static wmOperatorStatus space_context_cycle_invoke(bContext *C, wmOperator *op, const wmEvent *)
static void area_join_dock_cb(const wmWindow *win, void *userdata)
static void screen_animation_region_tag_redraw(bContext *C, ScrArea *area, ARegion *region, const Scene *scene, eScreen_Redraws_Flag redraws)
static float area_split_factor(bContext *C, sAreaJoinData *jd, const wmEvent *event)
static void view3d_localview_update_rv3d(RegionView3D *rv3d)
bool ED_operator_spreadsheet_active(bContext *C)
bool ED_operator_region_outliner_active(bContext *C)
static void stop_playback(bContext *C)
static void SCREEN_OT_area_options(wmOperatorType *ot)
bool ED_operator_editarmature(bContext *C)
static void area_dupli_fn(bScreen *, ScrArea *area, void *user_data)
static void screen_area_menu_items(ScrArea *area, uiLayout *layout)
static wmOperatorStatus area_split_modal(bContext *C, wmOperator *op, const wmEvent *event)
static bool ed_operator_posemode_exclusive_ex(bContext *C, Object *obact)
static wmOperatorStatus space_type_set_or_cycle_exec(bContext *C, wmOperator *op)
static void area_swap_cancel(bContext *C, wmOperator *op)
static void actionzone_exit(wmOperator *op)
static void SCREEN_OT_region_context_menu(wmOperatorType *ot)
static bool area_split_allowed(const ScrArea *area, const eScreenAxis dir_axis)
static void keymap_modal_set(wmKeyConfig *keyconf)
static void keylist_from_dopesheet(bContext &C, AnimKeylist &keylist)
static wmOperatorStatus area_split_exec(bContext *C, wmOperator *op)
static void SCREEN_OT_region_scale(wmOperatorType *ot)
#define KM_MODAL_APPLY
Definition screen_ops.cc:90
static wmOperatorStatus area_swap_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void fullscreen_click_rcti_init(rcti *rect, const short, const short, const short x2, const short y2)
bool ED_operator_uvmap(bContext *C)
static wmOperatorStatus userpref_show_exec(bContext *C, wmOperator *op)
bool ED_operator_graphedit_active(bContext *C)
wmOperatorStatus ED_screen_animation_play(bContext *C, int sync, int mode)
bool ED_operator_action_active(bContext *C)
bool ED_operator_sequencer_active_editable(bContext *C)
bool ED_operator_animview_active(bContext *C)
static wmOperatorStatus repeat_history_exec(bContext *C, wmOperator *op)
bool ED_operator_node_editable(bContext *C)
static void SCREEN_OT_info_log_show(wmOperatorType *ot)
bool ED_operator_screen_mainwinactive(bContext *C)
static wmOperatorStatus area_join_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static bool area_split_menu_init(bContext *C, wmOperator *op)
static wmOperatorStatus header_toggle_menus_exec(bContext *C, wmOperator *)
AZone * ED_area_actionzone_find_xy(ScrArea *area, const int xy[2])
static void area_move_set_limits(wmWindow *win, bScreen *screen, const eScreenAxis dir_axis, int *bigger, int *smaller, bool *use_bigger_smaller_snap)
static void region_scale_validate_size(RegionMoveData *rmd)
void ED_reset_audio_device(bContext *C)
static wmOperatorStatus space_workspace_cycle_invoke(bContext *C, wmOperator *op, const wmEvent *)
static wmOperatorStatus drivers_editor_show_exec(bContext *C, wmOperator *op)
static bool keyframe_jump_poll(bContext *C)
static bool space_context_cycle_poll(bContext *C)
static void keylist_from_graph_editor(bContext &C, AnimKeylist &keylist)
static void keylist_fallback_for_keyframe_jump(bContext &C, AnimKeylist &keylist)
static bool area_split_apply(bContext *C, wmOperator *op)
bool ED_operator_console_active(bContext *C)
static void context_cycle_prop_get(bScreen *screen, const ScrArea *area, PointerRNA *r_ptr, PropertyRNA **r_prop)
float ED_region_blend_alpha(ARegion *region)
static void SCREEN_OT_animation_play(wmOperatorType *ot)
static bool area_join_init(bContext *C, wmOperator *op, ScrArea *sa1, ScrArea *sa2)
static wmOperatorStatus fullscreen_back_exec(bContext *C, wmOperator *op)
static void SCREEN_OT_workspace_cycle(wmOperatorType *ot)
bool ED_operator_object_active_only(bContext *C)
static wmOperatorStatus start_playback(bContext *C, int sync, int mode)
#define KM_MODAL_CANCEL
Definition screen_ops.cc:89
static void area_move_exit(bContext *C, wmOperator *op)
bool ED_operator_info_active(bContext *C)
static void area_join_dock_cb_window(sAreaJoinData *jd, wmOperator *op)
static wmOperatorStatus area_join_exec(bContext *C, wmOperator *op)
void ED_keymap_screen(wmKeyConfig *keyconf)
static bool operator_screenactive_norender(bContext *C)
static wmOperatorStatus area_dupli_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static wmOperatorStatus screen_context_menu_invoke(bContext *C, wmOperator *, const wmEvent *)
static int area_snap_calc_location(const bScreen *screen, const enum AreaMoveSnapType snap_type, const int delta, const int origval, const eScreenAxis dir_axis, const int bigger, const int smaller)
static wmOperatorStatus area_swap_modal(bContext *C, wmOperator *op, const wmEvent *event)
static void SCREEN_OT_userpref_show(wmOperatorType *ot)
bool ED_operator_editable_mesh(bContext *C)
#define TIMEOUT
static void SCREEN_OT_actionzone(wmOperatorType *ot)
static void actionzone_apply(bContext *C, wmOperator *op, int type)
void ED_areas_do_frame_follow(bContext *C, bool center_view)
bool ED_operator_scene(bContext *C)
bool ED_operator_object_active_local_editable(bContext *C)
bool ED_operator_screenactive(bContext *C)
#define KM_MODAL_SNAP_ON
Definition screen_ops.cc:91
bool ED_operator_regionactive(bContext *C)
Definition screen_ops.cc:98
static void area_actionzone_get_rect(AZone *az, rcti *r_rect)
static bool actionzone_area_poll(bContext *C)
bool ED_operator_editsurfcurve_region_view3d(bContext *C)
static wmOperatorStatus screen_animation_step_invoke(bContext *C, wmOperator *, const wmEvent *event)
static float area_docking_snap(const float pos, const wmEvent *event)
static wmOperatorStatus info_log_show_exec(bContext *C, wmOperator *op)
static void region_scale_toggle_hidden(bContext *C, RegionMoveData *rmd)
static wmOperatorStatus frame_offset_exec(bContext *C, wmOperator *op)
static bool match_region_with_redraws(const ScrArea *area, eRegion_Type regiontype, eScreen_Redraws_Flag redraws, bool from_anim_edit)
static wmOperatorStatus screen_maximize_area_exec(bContext *C, wmOperator *op)
static void SCREEN_OT_back_to_previous(wmOperatorType *ot)
static void SCREEN_OT_keyframe_jump(wmOperatorType *ot)
static bool space_type_set_or_cycle_poll(bContext *C)
static bool ed_spacetype_test(bContext *C, int type)
static int area_join_cursor(sAreaJoinData *jd, const wmEvent *event)
bool ED_operator_object_active(bContext *C)
bool ED_operator_editsurfcurve(bContext *C)
static void SCREEN_OT_frame_jump(wmOperatorType *ot)
static wmOperatorStatus region_blend_invoke(bContext *C, wmOperator *, const wmEvent *event)
static void SCREEN_OT_area_close(wmOperatorType *ot)
static void SCREEN_OT_redo_last(wmOperatorType *ot)
AreaMoveSnapType
@ SNAP_FRACTION_AND_ADJACENT
@ SNAP_BIGGER_SMALLER_ONLY
@ SNAP_NONE
@ SNAP_AREAGRID
static void actionzone_cancel(bContext *, wmOperator *op)
static bool area_swap_init(wmOperator *op, const wmEvent *event)
static void area_move_apply(bContext *C, wmOperator *op)
#define TIMESTEP
bool ED_operator_editcurve_3d(bContext *C)
bool ED_operator_objectmode(bContext *C)
static wmOperatorStatus area_split_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static bool area_split_init(bContext *C, wmOperator *op)
bool ED_operator_sequencer_active(bContext *C)
static void SCREEN_OT_animation_step(wmOperatorType *ot)
static void SCREEN_OT_header_toggle_menus(wmOperatorType *ot)
bScreen * ED_screen_animation_no_scrub(const wmWindowManager *wm)
bool ED_operator_scene_editable(bContext *C)
bool ED_operator_asset_browsing_active(bContext *C)
static void SCREEN_OT_delete(wmOperatorType *ot)
bool ED_operator_objectmode_with_view3d_poll_msg(bContext *C)
static AZone * area_actionzone_refresh_xy(ScrArea *area, const int xy[2], const bool test_only)
bool ED_operator_image_active(bContext *C)
static void SCREEN_OT_space_type_set_or_cycle(wmOperatorType *ot)
static wmOperatorStatus frame_jump_exec(bContext *C, wmOperator *op)
void ED_operatortypes_screen()
bool ED_operator_view3d_active(bContext *C)
void ED_screens_region_flip_menu_create(bContext *C, uiLayout *layout, void *)
static const EnumPropertyItem prop_direction_items[]
static wmOperatorStatus spacedata_cleanup_exec(bContext *C, wmOperator *op)
bool ED_operator_node_active(bContext *C)
static wmOperatorStatus region_scale_modal(bContext *C, wmOperator *op, const wmEvent *event)
static void SCREEN_OT_screen_full_area(wmOperatorType *ot)
static wmOperatorStatus redo_last_invoke(bContext *C, wmOperator *, const wmEvent *)
static bool azone_clipped_rect_calc(const AZone *az, rcti *r_rect_clip)
static bool repeat_history_poll(bContext *C)
bool ED_operator_posemode_local(bContext *C)
static void screen_modal_action_end()
static void SCREEN_OT_frame_offset(wmOperatorType *ot)
static wmOperatorStatus repeat_last_exec(bContext *C, wmOperator *)
static void area_docking_apply(bContext *C, wmOperator *op)
static ScrArea * screen_actionzone_area(bScreen *screen, const AZone *az)
static void area_move_draw_cb(const wmWindow *win, void *userdata)
bool ED_operator_nla_active(bContext *C)
static AZone * screen_actionzone_find_xy(bScreen *screen, const int xy[2])
bool ED_operator_object_active_local_editable_ex(bContext *C, const Object *ob)
bool ED_operator_screenactive_nobackground(bContext *C)
bool ED_operator_uvedit_space_image(bContext *C)
static void region_scale_exit(wmOperator *op)
static void SCREEN_OT_space_context_cycle(wmOperatorType *ot)
bool ED_operator_region_view3d_active(bContext *C)
bool ED_operator_object_active_editable(bContext *C)
static wmOperatorStatus screen_new_exec(bContext *C, wmOperator *)
static void area_join_update_data(bContext *C, sAreaJoinData *jd, const wmEvent *event)
bool ED_operator_posemode_exclusive(bContext *C)
static wmOperatorStatus region_flip_exec(bContext *C, wmOperator *)
bScreen * ED_screen_animation_playing(const wmWindowManager *wm)
static bool region_toggle_poll(bContext *C)
static ScrEdge * area_findsharededge(bScreen *screen, ScrArea *area, ScrArea *sb)
static void region_scale_draw_cb(const wmWindow *, void *userdata)
static wmOperatorStatus repeat_history_invoke(bContext *C, wmOperator *op, const wmEvent *)
static wmOperatorStatus area_join_modal(bContext *C, wmOperator *op, const wmEvent *event)
bool ED_operator_editcurve(bContext *C)
static void area_split_exit(bContext *C, wmOperator *op)
static bool area_close_poll(bContext *C)
static void SCREEN_OT_area_swap(wmOperatorType *ot)
static void area_swap_exit(bContext *C, wmOperator *op)
static wmOperatorStatus region_quadview_exec(bContext *C, wmOperator *op)
bool ED_operator_areaactive(bContext *C)
bool ED_operator_editmesh_region_view3d(bContext *C)
bool ED_operator_file_browsing_active(bContext *C)
static void SCREEN_OT_area_split(wmOperatorType *ot)
void ED_region_visibility_change_update_animated(bContext *C, ScrArea *area, ARegion *region)
AZone * ED_area_azones_update(ScrArea *area, const int xy[2])
bool ED_operator_object_active_editable_mesh(bContext *C)
static wmOperatorStatus screen_set_exec(bContext *C, wmOperator *op)
static void SCREEN_OT_spacedata_cleanup(wmOperatorType *ot)
static wmOperatorStatus actionzone_modal(bContext *C, wmOperator *op, const wmEvent *event)
bool ED_operator_posemode_context(bContext *C)
static void SCREEN_OT_repeat_history(wmOperatorType *ot)
bool ED_operator_posemode(bContext *C)
bool ED_operator_editmball(bContext *C)
static void SCREEN_OT_new(wmOperatorType *ot)
static void SCREEN_OT_animation_cancel(wmOperatorType *ot)
static wmOperatorStatus screen_animation_play_exec(bContext *C, wmOperator *op)
void ED_screens_footer_tools_menu_create(bContext *C, uiLayout *layout, void *)
bool ED_operator_camera_poll(bContext *C)
static void SCREEN_OT_area_dupli(wmOperatorType *ot)
#define KM_MODAL_SNAP_OFF
Definition screen_ops.cc:92
bool ED_operator_file_active(bContext *C)
static void area_split_preview_update_cursor(bContext *C, wmOperator *op)
static bool screen_animation_region_supports_time_follow(eSpace_Type spacetype, eRegion_Type regiontype)
static void SCREEN_OT_region_quadview(wmOperatorType *ot)
static void SCREEN_OT_screen_set(wmOperatorType *ot)
static AreaDockTarget area_docking_target(sAreaJoinData *jd, const wmEvent *event)
bool ED_operator_editmesh_view3d(bContext *C)
static wmOperatorStatus area_move_modal(bContext *C, wmOperator *op, const wmEvent *event)
static void area_split_cancel(bContext *C, wmOperator *op)
static bool screen_active_editable(bContext *C)
bool ED_operator_object_active_local_editable_posemode_exclusive(bContext *C)
static void area_move_apply_do(bContext *C, int delta, const int origval, const eScreenAxis dir_axis, const int bigger, const int smaller, const enum AreaMoveSnapType snap_type)
static wmOperatorStatus actionzone_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void region_scale_cancel(bContext *, wmOperator *op)
bool ED_operator_editsurf(bContext *C)
static void SCREEN_OT_repeat_last(wmOperatorType *ot)
static wmOperatorStatus area_move_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static bool area_join_apply(bContext *C, wmOperator *op)
static void region_blend_end(bContext *C, ARegion *region, const bool is_running)
static void SCREEN_OT_drivers_editor_show(wmOperatorType *ot)
bool ED_operator_object_active_editable_ex(bContext *C, const Object *ob)
static void SCREEN_OT_area_move(wmOperatorType *ot)
static void screen_modal_action_begin()
static void SCREEN_OT_marker_jump(wmOperatorType *ot)
bool ED_operator_editlattice(bContext *C)
bool ED_operator_outliner_active_no_editobject(bContext *C)
static bool region_flip_poll(bContext *C)
void ED_screens_header_tools_menu_create(bContext *C, uiLayout *layout, void *)
static wmOperatorStatus region_scale_invoke(bContext *C, wmOperator *op, const wmEvent *event)
bool ED_operator_editmesh(bContext *C)
static void ed_screens_statusbar_menu_create(uiLayout *layout, void *)
static bool ed_object_hidden(const Object *ob)
static wmOperatorStatus area_move_exec(bContext *C, wmOperator *op)
static const EnumPropertyItem space_context_cycle_direction[]
bool ED_operator_uvedit(bContext *C)
static wmOperatorStatus marker_jump_exec(bContext *C, wmOperator *op)
static void area_join_cancel(bContext *C, wmOperator *op)
bool ED_operator_outliner_active(bContext *C)
bool ED_operator_object_active_editable_font(bContext *C)
static wmOperatorStatus region_toggle_exec(bContext *C, wmOperator *op)
static void region_quadview_init_rv3d(ScrArea *area, ARegion *region, const char viewlock, const char view, const char persp)
static void SCREEN_OT_region_flip(wmOperatorType *ot)
static wmOperatorStatus screen_area_options_invoke(bContext *C, wmOperator *op, const wmEvent *event)
bool ED_operator_region_gizmo_active(bContext *C)
static void area_join_exit(bContext *C, wmOperator *op)
static bool area_move_init(bContext *C, wmOperator *op)
static void area_move_cancel(bContext *C, wmOperator *op)
static void blend_file_drop_copy(bContext *, wmDrag *drag, wmDropBox *drop)
static wmOperatorStatus screen_animation_cancel_exec(bContext *C, wmOperator *op)
bool ED_operator_objectmode_poll_msg(bContext *C)
static bool screen_maximize_area_poll(bContext *C)
static void SCREEN_OT_region_blend(wmOperatorType *ot)
bool ED_operator_buttons_active(bContext *C)
static void area_join_draw_cb(const wmWindow *win, void *userdata)
static ScrEdge * screen_area_edge_from_cursor(const bContext *C, const int cursor[2], ScrArea **r_sa1, ScrArea **r_sa2)
static wmOperatorStatus area_swap_exec(bContext *C, wmOperator *op)
static wmOperatorStatus keyframe_jump_exec(bContext *C, wmOperator *op)
static void SCREEN_OT_region_toggle(wmOperatorType *ot)
static wmOperatorStatus area_close_exec(bContext *C, wmOperator *op)
static bool area_dupli_open(bContext *C, ScrArea *area, const blender::int2 position)
static void SCREEN_OT_area_join(wmOperatorType *ot)
bool ED_operator_editfont(bContext *C)
static void area_split_draw_cb(const wmWindow *, void *userdata)
static std::string userpref_show_get_description(bContext *C, wmOperatorType *, PointerRNA *ptr)
static bool is_split_edge(const int alignment, const AZEdge edge)
#define FLT_MAX
Definition stdcycles.h:14
void * regiondata
struct ARegion * prev
ARegionRuntimeHandle * runtime
struct ARegion * next
float alpha
AZEdge edge
ARegion * region
AZone * next
AZScrollDirection direction
ActKeyColumn * prev
ActKeyColumn * next
int totface
EditNurb * editnurb
BezTriple * bezt
Definition DNA_ID.h:404
unsigned int recalc
Definition DNA_ID.h:427
int icon_id
Definition DNA_ID.h:426
void * last
void * first
ListBase screens
Definition BKE_main.hh:261
ListBase workspaces
Definition BKE_main.hh:284
short visibility_flag
void * data
Definition RNA_types.hh:53
ARegion * child_region
wmWindow * win
ARegion * region
struct RegionView3D * localvd
struct RenderData r
ListBase markers
struct AudioData audio
ListBase areabase
ScrVert * v2
ListBase actionzones
ScrVert * v3
ListBase spacedata
short butspacetype_subtype
struct SpaceType * type
bScreen * full
ScrVert * v1
ListBase regionbase
ScrGlobalAreaData * global
ScrVert * v4
ScrVert * v1
ScrVert * v2
struct bNodeTree * edittree
char alpha_vert
short keeptot
struct Object * camera
Wrapper for bScreen.
eAnimCont_Types datatype
ScrArea * area
short redraws_flag
ListBase vertbase
struct wmTimer * animtimer
ListBase areabase
struct ARegion * active_region
float xmax
float xmin
float ymax
float ymin
int ymin
int ymax
int xmin
int xmax
eScreenDir gesture_dir
wmWindow * draw_dock_win
ScrArea * sa1
eScreenAxis split_dir
wmWindow * win1
AreaDockTarget dock_target
void * draw_dock_callback
wmWindow * win2
eScreenDir dir
void * draw_callback
ScrArea * sa2
eScreenAxis dir_axis
AreaMoveSnapType snap_type
void * draw_callback
bScreen * screen
ScrEdge * nedge
ScrArea * narea
ScrArea * sarea
ScrArea * sa1
ScrArea * sa2
PointerRNA op(wmOperatorType *ot, std::optional< blender::StringRef > name, int icon, wmOperatorCallContext context, eUI_Item_Flag flag)
uiLayout & column(bool align)
void separator(float factor=1.0f, LayoutSeparatorType type=LayoutSeparatorType::Auto)
void menu_fn(blender::StringRefNull name, int icon, uiMenuCreateFunc func, void *arg)
void prop(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRef > name_opt, int icon, std::optional< blender::StringRef > placeholder=std::nullopt)
short y
short x
eWM_DragDataType type
Definition WM_types.hh:1327
PointerRNA * ptr
Definition WM_types.hh:1415
wmEventModifierFlag modifier
Definition WM_types.hh:771
wmEventType type
Definition WM_types.hh:754
short val
Definition WM_types.hh:756
int xy[2]
Definition WM_types.hh:758
void * customdata
Definition WM_types.hh:804
struct ReportList * reports
struct wmOperator * prev
struct wmOperatorType * type
struct PointerRNA * ptr
double time_delta
Definition WM_types.hh:967
void * customdata
Definition WM_types.hh:962
wmWindow * win
Definition WM_types.hh:953
double time_step
Definition WM_types.hh:956
double time_last
Definition WM_types.hh:970
struct wmEvent * eventstate
ScrAreaMap global_areas
struct WorkSpaceInstanceHook * workspace_hook
i
Definition text_draw.cc:230
wmTimer * timer
void WM_operator_free_all_after(wmWindowManager *wm, wmOperator *op)
Definition wm.cc:310
void WM_cursor_modal_set(wmWindow *win, int val)
void WM_cursor_set(wmWindow *win, int curs)
void WM_cursor_modal_restore(wmWindow *win)
@ WM_CURSOR_DEFAULT
Definition wm_cursors.hh:15
@ WM_CURSOR_HAND_CLOSED
Definition wm_cursors.hh:23
@ WM_CURSOR_H_SPLIT
Definition wm_cursors.hh:41
@ WM_CURSOR_S_ARROW
Definition wm_cursors.hh:48
@ WM_CURSOR_Y_MOVE
Definition wm_cursors.hh:40
@ WM_CURSOR_PICK_AREA
Definition wm_cursors.hh:62
@ WM_CURSOR_MOVE
Definition wm_cursors.hh:21
@ WM_CURSOR_E_ARROW
Definition wm_cursors.hh:49
@ WM_CURSOR_EDIT
Definition wm_cursors.hh:19
@ WM_CURSOR_N_ARROW
Definition wm_cursors.hh:47
@ WM_CURSOR_STOP
Definition wm_cursors.hh:18
@ WM_CURSOR_V_SPLIT
Definition wm_cursors.hh:42
@ WM_CURSOR_SWAP_AREA
Definition wm_cursors.hh:38
@ WM_CURSOR_X_MOVE
Definition wm_cursors.hh:39
@ WM_CURSOR_W_ARROW
Definition wm_cursors.hh:50
wmDropBox * WM_dropbox_add(ListBase *lb, const char *idname, bool(*poll)(bContext *C, wmDrag *drag, const wmEvent *event), void(*copy)(bContext *C, wmDrag *drag, wmDropBox *drop), void(*cancel)(Main *bmain, wmDrag *drag, wmDropBox *drop), WMDropboxTooltipFunc tooltip)
int WM_drag_get_path_file_type(const wmDrag *drag)
const char * WM_drag_get_single_path(const wmDrag *drag)
ListBase * WM_dropboxmap_find(const char *idname, int spaceid, int regionid)
int xy[2]
Definition wm_draw.cc:174
void * WM_draw_cb_activate(wmWindow *win, void(*draw)(const wmWindow *win, void *customdata), void *customdata)
Definition wm_draw.cc:615
void WM_draw_cb_exit(wmWindow *win, void *handle)
Definition wm_draw.cc:628
int WM_event_drag_threshold(const wmEvent *event)
bool WM_operator_repeat_check(const bContext *, wmOperator *op)
wmOperatorStatus WM_operator_repeat_last(bContext *C, wmOperator *op)
void WM_main_add_notifier(uint type, void *reference)
wmEventHandler_Op * WM_event_add_modal_handler(bContext *C, wmOperator *op)
void WM_event_modal_handler_region_replace(wmWindow *win, const ARegion *old_region, ARegion *new_region)
wmOperatorStatus WM_operator_repeat(bContext *C, wmOperator *op)
wmEvent * WM_event_add(wmWindow *win, const wmEvent *event_to_add)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
void wm_event_init_from_window(wmWindow *win, wmEvent *event)
void WM_event_add_mousemove(wmWindow *win)
@ RIGHTMOUSE
@ EVT_MODAL_MAP
@ EVT_RIGHTCTRLKEY
@ EVT_ACTIONZONE_FULLSCREEN
@ EVT_ACTIONZONE_REGION
@ EVT_TABKEY
@ EVT_LEFTCTRLKEY
@ EVT_ACTIONZONE_AREA
@ MOUSEMOVE
@ LEFTMOUSE
@ MIDDLEMOUSE
@ EVT_ESCKEY
@ WINDEACTIVATE
@ TIMERREGION
PointerRNA * ptr
Definition wm_files.cc:4227
wmOperatorType * ot
Definition wm_files.cc:4226
void WM_gesture_box_cancel(bContext *C, wmOperator *op)
wmOperatorStatus WM_gesture_box_modal(bContext *C, wmOperator *op, const wmEvent *event)
wmOperatorStatus WM_gesture_box_invoke(bContext *C, wmOperator *op, const wmEvent *event)
wmKeyMap * WM_modalkeymap_ensure(wmKeyConfig *keyconf, const char *idname, const EnumPropertyItem *items)
Definition wm_keymap.cc:929
void WM_modalkeymap_assign(wmKeyMap *km, const char *opname)
wmKeyMap * WM_keymap_ensure(wmKeyConfig *keyconf, const char *idname, int spaceid, int regionid)
Definition wm_keymap.cc:893
void WM_operator_properties_border(wmOperatorType *ot)
std::string WM_operatortype_name(wmOperatorType *ot, PointerRNA *properties)
void WM_operatortype_append(void(*opfunc)(wmOperatorType *))
wmOperatorStatus WM_operator_redo_popup(bContext *C, wmOperator *op)
bool WM_operator_winactive(bContext *C)
wmOperator * WM_operator_last_redo(const bContext *C)
void WM_window_native_pixel_coords(const wmWindow *win, int *x, int *y)
bool wm_cursor_position_get(wmWindow *win, int *r_x, int *r_y)
void wm_window_close(bContext *C, wmWindowManager *wm, wmWindow *win)
Definition wm_window.cc:433
wmWindow * WM_window_open(bContext *C, const char *title, const rcti *rect_unscaled, int space_type, bool toplevel, bool dialog, bool temp, eWindowAlignment alignment, void(*area_setup_fn)(bScreen *screen, ScrArea *area, void *user_data), void *area_setup_user_data)
wmTimer * WM_event_timer_add(wmWindowManager *wm, wmWindow *win, const wmEventType event_type, const double time_step)
bool WM_window_is_temp_screen(const wmWindow *win)
void WM_window_rect_calc(const wmWindow *win, rcti *r_rect)
wmWindow * WM_window_find_by_area(wmWindowManager *wm, const ScrArea *area)
void WM_window_screen_rect_calc(const wmWindow *win, rcti *r_rect)
ViewLayer * WM_window_get_active_view_layer(const wmWindow *win)
void WM_event_timer_remove(wmWindowManager *wm, wmWindow *, wmTimer *timer)
void WM_window_title(wmWindowManager *wm, wmWindow *win, const char *title)
Definition wm_window.cc:489
WorkSpace * WM_window_get_active_workspace(const wmWindow *win)
bScreen * WM_window_get_active_screen(const wmWindow *win)