UI Experiment: Screen Layout Operations #118115

Open
Harley Acheson wants to merge 1 commits from Harley/blender:AreaLayout into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
2 changed files with 373 additions and 0 deletions

View File

@ -615,6 +615,43 @@ class TOPBAR_MT_edit(Menu):
text="Preferences...", icon='PREFERENCES')
class TOPBAR_MT_window_layout_reduce(Menu):
bl_label = "Reduce Areas To"
def draw(self, _context):
layout = self.layout
layout.operator("screen.area_reduce", text="One Area").number = 1
layout.operator("screen.area_reduce", text="Two Areas").number = 2
layout.operator("screen.area_reduce", text="Three Areas").number = 3
layout.operator("screen.area_reduce", text="Four Areas").number = 4
layout.operator("screen.area_reduce", text="Five Areas").number = 5
layout.operator("screen.area_reduce", text="Six Areas").number = 6
layout.operator("screen.area_reduce", text="Seven Areas").number = 7
class TOPBAR_MT_window_layout(Menu):
bl_label = "Layout"
def draw(self, _context):
layout = self.layout
layout.operator("screen.area_layout", text="1 Area Only").layout='1'
layout.separator()
layout.operator("screen.area_layout", text="2 Areas - Horizontal Columns").layout='2H'
layout.operator("screen.area_layout", text="2 Areas - Vertical Rows").layout='2V'
layout.separator()
layout.operator("screen.area_layout", text="3 Areas - Horizontal Columns").layout='3H'
layout.operator("screen.area_layout", text="3 Areas - 1 + 2").layout='312'
layout.operator("screen.area_layout", text="3 Areas - 2 + 1").layout='321'
layout.separator()
layout.operator("screen.area_layout", text="4 Areas - 2 + 2").layout='422'
layout.operator("screen.area_layout", text="4 Areas - 1 + 1 + 2").layout='4112'
layout.separator()
layout.operator("screen.area_layout", text="5 Areas - 3 column split sides").layout='5212'
layout.operator("screen.area_layout", text="6 Areas - 3 columns").layout='6222'
class TOPBAR_MT_window(Menu):
bl_label = "Window"
@ -627,6 +664,10 @@ class TOPBAR_MT_window(Menu):
layout.operator("wm.window_new")
layout.operator("wm.window_new_main")
layout.separator()
layout.menu("TOPBAR_MT_window_layout")
layout.operator("screen.area_reduce", text="Close Smallest Area").number = -1
layout.menu("TOPBAR_MT_window_layout_reduce")
layout.separator()
layout.operator("wm.window_fullscreen_toggle", icon='FULLSCREEN_ENTER')
@ -913,6 +954,8 @@ classes = (
TOPBAR_MT_file_previews,
TOPBAR_MT_edit,
TOPBAR_MT_render,
TOPBAR_MT_window_layout_reduce,
TOPBAR_MT_window_layout,
TOPBAR_MT_window,
TOPBAR_MT_help,
TOPBAR_PT_tool_fallback,

View File

@ -3823,6 +3823,334 @@ static void SCREEN_OT_area_options(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
/** \name Screen Area Layout Operator
* \{ */
/* Sort areas by position, l2R and T2B, needed to keep resized areas in place. */
static int screen_area_position_cmp(const void *area_a_ptr, const void *area_b_ptr)
{
const ScrArea *area_a = static_cast<const ScrArea *>(area_a_ptr);
const ScrArea *area_b = static_cast<const ScrArea *>(area_b_ptr);
const int center_a_x = BLI_rcti_cent_x(&area_a->totrct);
const int center_b_x = BLI_rcti_cent_x(&area_b->totrct);
if (center_a_x > center_b_x) {
return 1;
}
if (center_a_x < center_b_x) {
return -1;
}
const int center_a_y = BLI_rcti_cent_y(&area_a->totrct);
const int center_b_y = BLI_rcti_cent_y(&area_b->totrct);
if (center_a_y < center_b_y) {
return 1;
}
if (center_a_y > center_b_y) {
return -1;
}
return 0;
}
/* Sort areas by size, needed so we can cull smaller areas first. */
static int screen_area_size_cmp(const void *area_a_ptr, const void *area_b_ptr)
{
const ScrArea *area_a = static_cast<const ScrArea *>(area_a_ptr);
const ScrArea *area_b = static_cast<const ScrArea *>(area_b_ptr);
const int size_a = BLI_rcti_size_x(&area_a->totrct) * BLI_rcti_size_y(&area_a->totrct);
const int size_b = BLI_rcti_size_x(&area_b->totrct) * BLI_rcti_size_y(&area_b->totrct);
if (size_a < size_b) {
return 1;
}
if (size_a > size_b) {
return -1;
}
return 0;
}
static const EnumPropertyItem prop_area_layout_items[] = {
{10, "1", 0, "1 Area", "One Area"},
{20, "2H", 0, "2 Areas - Columns", "Two Areas arranged horizontally"},
{21, "2V", 0, "2 Areas - Rows", "Two Areas arranged vertically"},
{30, "3H", 0, "3 Areas - Columns", "Three Areas arranged horizontally"},
{31, "312", 0, "3 Areas - 1 + 2", "Three Areas with two columns, right split"},
{32, "321", 0, "3 Areas - 2 + 1", "Three Areas with two columns, left split"},
{41, "422", 0, "4 Areas - 2 + 2", "Four Areas with two columns of two rows"},
{42, "4112", 0, "4 Areas - 1 + 1 + 2", "Four Areas with three columns, right split"},
{51, "5212", 0, "5 Areas", "Five areas in three columns, split sides"},
{61, "6222", 0, "6 Areas", "Six areas in three columns"},
{0, nullptr, 0, nullptr, nullptr},
};
static int screen_area_layout_exec(bContext *C, wmOperator *op)
{
wmWindowManager *wm = CTX_wm_manager(C);
wmWindow *win = CTX_wm_window(C);
bScreen *screen = CTX_wm_screen(C);
if (!win || !screen) {
return OPERATOR_CANCELLED;
};
int area_count_current = BLI_listbase_count(&screen->areabase);
int layout_index = RNA_enum_get(op->ptr, "layout");
if (layout_index == 0) {
return OPERATOR_CANCELLED;
}
int area_count_target = (layout_index / 10);
if (area_count_current > area_count_target) {
/* Sort the areas by size. */
ScrAreaMap *area_map = AREAMAP_FROM_SCREEN(screen);
BLI_listbase_sort(&area_map->areabase, screen_area_size_cmp);
/* Delete the smallest until we get to our target. */
LISTBASE_FOREACH_BACKWARD_MUTABLE (ScrArea *, area, &screen->areabase) {
screen_area_close(C, screen, area);
if (BLI_listbase_count(&screen->areabase) <= area_count_target) {
break;
}
}
}
area_count_current = BLI_listbase_count(&screen->areabase);
if (area_count_current != area_count_target) {
return OPERATOR_CANCELLED;
}
if (area_count_current == 1) {
CTX_wm_window_set(C, nullptr);
WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, nullptr);
return OPERATOR_FINISHED;
}
rcti win_rect;
WM_window_screen_rect_calc(win, &win_rect);
float width = float(BLI_rcti_size_x(&win_rect));
float height = float(BLI_rcti_size_y(&win_rect));
std::vector<rctf> points;
switch (layout_index) {
case 20:
/* Golden Ratio, left biased. */
points = {{0.0f, 0.618033f, 0.0f, 1.0f}, {0.618033f, 1.0f, 0.0f, 1.0f}};
break;
case 21:
/* Golden Ratio, top biased. */
points = {{0.0f, 1.0f, 0.0f, 0.381967f}, {0.0f, 1.0f, 0.381967f, 1.0f}};
break;
case 30:
/* 5, 8, 3. */
points = {{0.3125f, 0.8125f, 0.0f, 1.0f},
{0.0f, 0.3125f, 0.0f, 1.0f},
{0.8125f, 1.0f, 0.0f, 1.0f}};
break;
case 31:
/* Golden ratios. */
points = {{0.0f, 0.618033f, 0.0f, 1.0f},
{0.618033f, 1.0f, 0.381967f, 1.0f},
{0.618033f, 1.0f, 0.0f, 0.381967f}};
break;
case 32:
/* Golden ratios. */
points = {{0.0f, 0.618033f, 0.381967f, 1.0f},
{0.0f, 1.0f, 0.0f, 0.381967f},
{0.618033f, 1.0f, 0.381967f, 1.0f}};
break;
case 40:
/* 8, 5, 3, 2 */
points = {{0.0f, 0.444444f, 0.0f, 1.0f},
{0.444444f, 0.722222f, 0.0f, 1.0f},
{0.722222f, 0.888888f, 0.0f, 1.0f},
{0.888888f, 1.0f, 0.0f, 1.0f}};
break;
case 41:
/* Golden ratios. */
points = {{0.0f, 0.618033f, 0.381967f, 1.0f},
{0.0f, 0.618033f, 0.0f, 0.381967f},
{0.618033f, 1.0f, 0.0f, 0.618033f},
{0.618033f, 1.0f, 0.618033f, 1.0f}};
break;
case 42:
/* 5, 8, 3. */
points = {{0.0f, 0.3125f, 0.0f, 1.0f},
{0.3125f, 0.8125f, 0.0f, 1.0f},
{0.8125f, 1.0f, 0.0f, 0.381967f},
{0.8125f, 1.0f, 0.381967f, 1.0f}};
break;
case 51:
/* 8, 5, 3. */
points = {{0.3125f, 0.8125f, 0.0f, 1.0f},
{0.0f, 0.3125f, 0.381967f, 1.0f},
{0.0f, 0.3125f, 0.0f, 0.381967f},
{0.8125f, 1.0f, 0.381967f, 1.0f},
{0.8125f, 1.0f, 0.0f, 0.381967f}};
break;
case 61:
/* 8, 5, 3. */
points = {{0.3125f, 0.8125f, 0.381967f, 1.0f},
{0.3125f, 0.8125f, 0.0f, 0.381967f},
{0.0f, 0.3125f, 0.0f, 0.618033f},
{0.0f, 0.3125f, 0.618033f, 1.0f},
{0.8125f, 1.0f, 0.0f, 0.618033f},
{0.8125f, 1.0f, 0.618033f, 1.0f}};
break;
}
ScrAreaMap *area_map = AREAMAP_FROM_SCREEN(screen);
BLI_listbase_sort(&area_map->areabase, screen_area_position_cmp);
BLI_freelistN(&area_map->vertbase);
BLI_freelistN(&area_map->edgebase);
int i = 0;
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
area->v1 = screen_geom_vertex_add_ex(area_map,
win_rect.xmin + lroundf(points[i].xmin * width),
win_rect.ymin + lroundf(points[i].ymin * height));
area->v2 = screen_geom_vertex_add_ex(area_map,
win_rect.xmin + lroundf(points[i].xmin * width),
win_rect.ymin + lroundf(points[i].ymax * height));
area->v3 = screen_geom_vertex_add_ex(area_map,
win_rect.xmin + lroundf(points[i].xmax * width),
win_rect.ymin + lroundf(points[i].ymax * height));
area->v4 = screen_geom_vertex_add_ex(area_map,
win_rect.xmin + lroundf(points[i].xmax * width),
win_rect.ymin + lroundf(points[i].ymin * height));
screen_geom_edge_add_ex(area_map, area->v1, area->v2);
screen_geom_edge_add_ex(area_map, area->v2, area->v3);
screen_geom_edge_add_ex(area_map, area->v3, area->v4);
screen_geom_edge_add_ex(area_map, area->v4, area->v1);
ED_area_update_region_sizes(wm, win, area);
ED_area_tag_redraw(area);
i++;
}
BKE_screen_remove_double_scredges(screen);
BKE_screen_remove_double_scrverts(screen);
BKE_screen_remove_unused_scredges(screen);
BKE_screen_remove_unused_scrverts(screen);
CTX_wm_window_set(C, nullptr);
WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, nullptr);
/* Update preview thumbnail. */
BKE_icon_changed(screen->id.icon_id);
return OPERATOR_FINISHED;
}
bool screen_area_layout_poll(bContext *C)
{
wmWindow *win = CTX_wm_window(C);
bScreen *screen = CTX_wm_screen(C);
if (!win || !screen) {
return false;
};
/* There must be more than area. */
return BLI_listbase_count_at_most(&screen->areabase, 2) > 1;
}
static void SCREEN_OT_area_layout(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Area Layout";
ot->description = "Arrange Screen Area Layout";
ot->idname = "SCREEN_OT_area_layout";
/* api callbacks */
ot->exec = screen_area_layout_exec;
ot->poll = screen_area_layout_poll;
RNA_def_enum(ot->srna, "layout", prop_area_layout_items, 0, "Layout", "");
}
static int screen_area_reduce_exec(bContext *C, wmOperator *op)
{
bScreen *screen = CTX_wm_screen(C);
int area_count_current = BLI_listbase_count(&screen->areabase);
int area_count_target = RNA_int_get(op->ptr, "number");
if (area_count_target < 0) {
area_count_target = area_count_current + area_count_target;
}
if (area_count_target == area_count_current) {
return OPERATOR_FINISHED;
}
if (area_count_target < 1) {
return OPERATOR_CANCELLED;
}
/* Sort the areas by size. */
ScrAreaMap *area_map = AREAMAP_FROM_SCREEN(screen);
BLI_listbase_sort(&area_map->areabase, screen_area_size_cmp);
/* Delete the smallest until we get to our target. */
LISTBASE_FOREACH_BACKWARD_MUTABLE (ScrArea *, area, &screen->areabase) {
screen_area_close(C, screen, area);
if (BLI_listbase_count(&screen->areabase) <= area_count_target) {
break;
}
}
/* Ensure the event loop doesn't attempt to continue handling events.
*
* This causes execution from the Python console fail to return to the prompt as it should.
* This glitch could be solved in the event loop handling as other operators may also
* destructively manipulate windowing data. */
CTX_wm_window_set(C, nullptr);
WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, nullptr);
return OPERATOR_FINISHED;
}
bool screen_area_reduce_poll(bContext *C)
{
wmWindow *win = CTX_wm_window(C);
bScreen *screen = CTX_wm_screen(C);
if (!win || !screen) {
return false;
};
/* There must be more than area. */
return BLI_listbase_count_at_most(&screen->areabase, 2) > 1;
}
static void SCREEN_OT_area_reduce(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Area Reduce";
ot->description = "Reduce the number of screen areas";
ot->idname = "SCREEN_OT_area_reduce";
/* api callbacks */
ot->exec = screen_area_reduce_exec;
ot->poll = screen_area_reduce_poll;
RNA_def_int(ot->srna,
"number",
-1,
INT_MIN,
INT_MAX,
"Area Reduction",
"Number of Areas to remain (if positive) or remove (if negative)",
INT_MIN,
INT_MAX);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Space Data Cleanup Operator
* \{ */
@ -5873,6 +6201,8 @@ void ED_operatortypes_screen()
WM_operatortype_append(SCREEN_OT_area_options);
WM_operatortype_append(SCREEN_OT_area_dupli);
WM_operatortype_append(SCREEN_OT_area_swap);
WM_operatortype_append(SCREEN_OT_area_layout);
WM_operatortype_append(SCREEN_OT_area_reduce);
WM_operatortype_append(SCREEN_OT_region_quadview);
WM_operatortype_append(SCREEN_OT_region_scale);
WM_operatortype_append(SCREEN_OT_region_toggle);