UI: Screen Area Docking Experimental Feature
Improvements to Area maintenance, adding the ability to move and dock areas to any location, including between multiple windows. Allows transitioning between splitting, joining, moving, and docking without early commit. Improved visual feedback. Design Doc #124915. Added as experiment feature. Pull Request: https://projects.blender.org/blender/blender/pulls/123414
This commit is contained in:
committed by
Harley Acheson
parent
7104813d31
commit
e802fe1433
@@ -2854,6 +2854,7 @@ class USERPREF_PT_experimental_new_features(ExperimentalPanel, Panel):
|
||||
({"property": "use_new_volume_nodes"}, ("blender/blender/issues/103248", "#103248")),
|
||||
({"property": "use_new_file_import_nodes"}, ("blender/blender/issues/122846", "#122846")),
|
||||
({"property": "use_shader_node_previews"}, ("blender/blender/issues/110353", "#110353")),
|
||||
({"property": "use_docking"}, ("blender/blender/issues/124915", "#124915")),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
#include "GPU_platform.hh"
|
||||
#include "GPU_state.hh"
|
||||
|
||||
#include "BKE_screen.hh"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_math_vector.hh"
|
||||
#include "BLI_rect.h"
|
||||
@@ -22,6 +24,9 @@
|
||||
#include "UI_interface.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
#include "RNA_enum_types.hh"
|
||||
|
||||
#include "screen_intern.hh"
|
||||
|
||||
#define CORNER_RESOLUTION 3
|
||||
@@ -214,10 +219,63 @@ void ED_screen_draw_edges(wmWindow *win)
|
||||
}
|
||||
}
|
||||
|
||||
void screen_draw_join_highlight(ScrArea *sa1, ScrArea *sa2)
|
||||
static void screen_draw_area_icon(
|
||||
rctf *rect, int icon, uchar *color, float *bg_color = nullptr, float *outline = nullptr)
|
||||
{
|
||||
const eScreenDir dir = area_getorientation(sa1, sa2);
|
||||
if (dir == SCREEN_DIR_NONE) {
|
||||
if (!U.experimental.use_docking) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (BLI_rctf_size_x(rect) < UI_SCALE_FAC * 75.0f || BLI_rctf_size_y(rect) < UI_SCALE_FAC * 60.0f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const float center_x = BLI_rctf_cent_x(rect);
|
||||
const float center_y = BLI_rctf_cent_y(rect);
|
||||
|
||||
if (bg_color) {
|
||||
const float bg_width = UI_SCALE_FAC * 50.0f;
|
||||
const float bg_height = UI_SCALE_FAC * 40.0f;
|
||||
rctf rect = {center_x - (bg_width / 2.0f),
|
||||
center_x + bg_width - (bg_width / 2.0f),
|
||||
center_y - (bg_height / 2.0f),
|
||||
center_y + bg_height - (bg_height / 2.0f)};
|
||||
UI_draw_roundbox_4fv_ex(
|
||||
&rect, bg_color, nullptr, 1.0f, outline ? outline : nullptr, U.pixelsize, 6 * U.pixelsize);
|
||||
}
|
||||
|
||||
const float icon_size = 32.0f * UI_SCALE_FAC;
|
||||
UI_icon_draw_ex(center_x - (icon_size / 2.0f),
|
||||
center_y - (icon_size / 2.0f),
|
||||
icon,
|
||||
16.0f / icon_size,
|
||||
float(color[3]) / 255.0f,
|
||||
0.0f,
|
||||
color,
|
||||
false,
|
||||
UI_NO_ICON_OVERLAY_TEXT);
|
||||
}
|
||||
|
||||
static void screen_draw_area_closed(int xmin, int xmax, int ymin, int ymax)
|
||||
{
|
||||
/* Darken the area. */
|
||||
rctf rect = {float(xmin), float(xmax), float(ymin), float(ymax)};
|
||||
float darken[4] = {0.0f, 0.0f, 0.0f, 0.7f};
|
||||
UI_draw_roundbox_corner_set(UI_CNR_ALL);
|
||||
UI_draw_roundbox_4fv_ex(&rect, darken, nullptr, 1.0f, nullptr, U.pixelsize, 6 * U.pixelsize);
|
||||
|
||||
/* Show "X" icon in the middle if there is space. */
|
||||
uchar color[4] = {255, 255, 255, 128};
|
||||
screen_draw_area_icon(&rect, ICON_CANCEL, color);
|
||||
}
|
||||
|
||||
void screen_draw_join_highlight(ScrArea *sa1, ScrArea *sa2, eScreenDir dir)
|
||||
{
|
||||
if (dir == SCREEN_DIR_NONE || !sa2) {
|
||||
/* Darken source if docking. Done here because it might be a different window. */
|
||||
screen_draw_area_closed(
|
||||
sa1->totrct.xmin, sa1->totrct.xmax, sa1->totrct.ymin, sa1->totrct.ymax);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -233,120 +291,208 @@ void screen_draw_join_highlight(ScrArea *sa1, ScrArea *sa2)
|
||||
combined.ymax = vertical ? std::max(sa1->totrct.ymax, sa2->totrct.ymax) :
|
||||
std::min(sa1->totrct.ymax, sa2->totrct.ymax);
|
||||
|
||||
uint pos_id = GPU_vertformat_attr_add(
|
||||
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
|
||||
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
|
||||
GPU_blend(GPU_BLEND_ALPHA);
|
||||
|
||||
/* Highlight source (sa1) within combined area. */
|
||||
immUniformColor4fv(blender::float4{1.0f, 1.0f, 1.0f, 0.10f});
|
||||
immRectf(pos_id,
|
||||
std::max(float(sa1->totrct.xmin), combined.xmin),
|
||||
std::max(float(sa1->totrct.ymin), combined.ymin),
|
||||
std::min(float(sa1->totrct.xmax), combined.xmax),
|
||||
std::min(float(sa1->totrct.ymax), combined.ymax));
|
||||
|
||||
/* Highlight destination (sa2) within combined area. */
|
||||
immUniformColor4fv(blender::float4{0.0f, 0.0f, 0.0f, 0.25f});
|
||||
immRectf(pos_id,
|
||||
std::max(float(sa2->totrct.xmin), combined.xmin),
|
||||
std::max(float(sa2->totrct.ymin), combined.ymin),
|
||||
std::min(float(sa2->totrct.xmax), combined.xmax),
|
||||
std::min(float(sa2->totrct.ymax), combined.ymax));
|
||||
|
||||
int offset1;
|
||||
int offset2;
|
||||
area_getoffsets(sa1, sa2, dir, &offset1, &offset2);
|
||||
if (offset1 < 0 || offset2 > 0) {
|
||||
/* Show partial areas that will be closed. */
|
||||
immUniformColor4fv(blender::float4{0.0f, 0.0f, 0.0f, 0.8f});
|
||||
if (vertical) {
|
||||
if (sa1->totrct.xmin < combined.xmin) {
|
||||
immRectf(pos_id, sa1->totrct.xmin, sa1->totrct.ymin, combined.xmin, sa1->totrct.ymax);
|
||||
screen_draw_area_closed(
|
||||
sa1->totrct.xmin, combined.xmin, sa1->totrct.ymin, sa1->totrct.ymax);
|
||||
}
|
||||
if (sa2->totrct.xmin < combined.xmin) {
|
||||
immRectf(pos_id, sa2->totrct.xmin, sa2->totrct.ymin, combined.xmin, sa2->totrct.ymax);
|
||||
screen_draw_area_closed(
|
||||
sa2->totrct.xmin, combined.xmin, sa2->totrct.ymin, sa2->totrct.ymax);
|
||||
}
|
||||
if (sa1->totrct.xmax > combined.xmax) {
|
||||
immRectf(pos_id, combined.xmax, sa1->totrct.ymin, sa1->totrct.xmax, sa1->totrct.ymax);
|
||||
screen_draw_area_closed(
|
||||
combined.xmax, sa1->totrct.xmax, sa1->totrct.ymin, sa1->totrct.ymax);
|
||||
}
|
||||
if (sa2->totrct.xmax > combined.xmax) {
|
||||
immRectf(pos_id, combined.xmax, sa2->totrct.ymin, sa2->totrct.xmax, sa2->totrct.ymax);
|
||||
screen_draw_area_closed(
|
||||
combined.xmax, sa2->totrct.xmax, sa2->totrct.ymin, sa2->totrct.ymax);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (sa1->totrct.ymin < combined.ymin) {
|
||||
immRectf(pos_id, sa1->totrct.xmin, combined.ymin, sa1->totrct.xmax, sa1->totrct.ymin);
|
||||
screen_draw_area_closed(
|
||||
sa1->totrct.xmin, sa1->totrct.xmax, sa1->totrct.ymin, combined.ymin);
|
||||
}
|
||||
if (sa2->totrct.ymin < combined.ymin) {
|
||||
immRectf(pos_id, sa2->totrct.xmin, combined.ymin, sa2->totrct.xmax, sa2->totrct.ymin);
|
||||
screen_draw_area_closed(
|
||||
sa2->totrct.xmin, sa2->totrct.xmax, sa2->totrct.ymin, combined.ymin);
|
||||
}
|
||||
if (sa1->totrct.ymax > combined.ymax) {
|
||||
immRectf(pos_id, sa1->totrct.xmin, sa1->totrct.ymax, sa1->totrct.xmax, combined.ymax);
|
||||
screen_draw_area_closed(
|
||||
sa1->totrct.xmin, sa1->totrct.xmax, combined.ymax, sa1->totrct.ymax);
|
||||
}
|
||||
if (sa2->totrct.ymax > combined.ymax) {
|
||||
immRectf(pos_id, sa2->totrct.xmin, sa2->totrct.ymax, sa2->totrct.xmax, combined.ymax);
|
||||
screen_draw_area_closed(
|
||||
sa2->totrct.xmin, sa2->totrct.xmax, combined.ymax, sa2->totrct.ymax);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
immUnbindProgram();
|
||||
GPU_blend(GPU_BLEND_NONE);
|
||||
|
||||
/* Outline the combined area. */
|
||||
UI_draw_roundbox_corner_set(UI_CNR_ALL);
|
||||
UI_draw_roundbox_4fv(&combined, false, 7 * U.pixelsize, blender::float4{1.0f, 1.0f, 1.0f, 0.8f});
|
||||
|
||||
if (!U.experimental.use_docking) {
|
||||
float inner1[4] = {1.0f, 1.0f, 1.0f, 0.10f};
|
||||
rctf source = {std::max(float(sa1->totrct.xmin), combined.xmin),
|
||||
std::min(float(sa1->totrct.xmax), combined.xmax),
|
||||
std::max(float(sa1->totrct.ymin), combined.ymin),
|
||||
std::min(float(sa1->totrct.ymax), combined.ymax)};
|
||||
UI_draw_roundbox_4fv_ex(&source, inner1, nullptr, 1.0f, nullptr, 1.0f, 0.0f);
|
||||
|
||||
float inner2[4] = {0.0f, 0.0f, 0.0f, 0.25f};
|
||||
rctf dest = {std::max(float(sa2->totrct.xmin), combined.xmin),
|
||||
std::min(float(sa2->totrct.xmax), combined.xmax),
|
||||
std::max(float(sa2->totrct.ymin), combined.ymin),
|
||||
std::min(float(sa2->totrct.ymax), combined.ymax)};
|
||||
UI_draw_roundbox_4fv_ex(&dest, inner2, nullptr, 1.0f, nullptr, 0.0f, 0.0f);
|
||||
|
||||
float outline[4] = {1.0f, 1.0f, 1.0f, 0.8f};
|
||||
UI_draw_roundbox_4fv_ex(
|
||||
&combined, nullptr, nullptr, 1.0f, outline, U.pixelsize, 6 * U.pixelsize);
|
||||
return;
|
||||
}
|
||||
|
||||
float outline[4] = {1.0f, 1.0f, 1.0f, 0.4f};
|
||||
float inner[4] = {1.0f, 1.0f, 1.0f, 0.10f};
|
||||
UI_draw_roundbox_4fv_ex(&combined, inner, nullptr, 1.0f, outline, U.pixelsize, 6 * U.pixelsize);
|
||||
|
||||
/* Icon in center of intersection of combined and sa2 - the subsumed part. */
|
||||
rctf sa2tot;
|
||||
BLI_rctf_rcti_copy(&sa2tot, &sa2->totrct);
|
||||
rctf sa2new;
|
||||
BLI_rctf_isect(&combined, &sa2tot, &sa2new);
|
||||
|
||||
uchar icon_color[4] = {255, 255, 255, 255};
|
||||
float bg_color[4] = {0.0f, 0.0f, 0.0f, 0.4f};
|
||||
float outline_color[4] = {1.0f, 1.0f, 1.0f, 0.4f};
|
||||
screen_draw_area_icon(&sa2new, ED_area_icon(sa1), icon_color, bg_color, outline_color);
|
||||
}
|
||||
|
||||
void screen_draw_dock_preview(const struct wmWindow * /* win */,
|
||||
ScrArea *source,
|
||||
ScrArea *target,
|
||||
AreaDockTarget dock_target)
|
||||
{
|
||||
if (dock_target == AreaDockTarget::None) {
|
||||
return;
|
||||
}
|
||||
|
||||
float outline[4] = {1.0f, 1.0f, 1.0f, 0.4f};
|
||||
float inner[4] = {1.0f, 1.0f, 1.0f, 0.1f};
|
||||
float bg_color[4] = {0.0f, 0.0f, 0.0f, 0.4f};
|
||||
uchar icon_color[4] = {255, 255, 255, 255};
|
||||
float border[4];
|
||||
UI_GetThemeColor4fv(TH_EDITOR_OUTLINE, border);
|
||||
UI_draw_roundbox_corner_set(UI_CNR_ALL);
|
||||
float half_line_width = 2.0f * U.pixelsize;
|
||||
|
||||
rctf dest;
|
||||
rctf remainder;
|
||||
BLI_rctf_rcti_copy(&dest, &target->totrct);
|
||||
BLI_rctf_rcti_copy(&remainder, &target->totrct);
|
||||
|
||||
float split;
|
||||
|
||||
if (dock_target == AreaDockTarget::Right) {
|
||||
split = std::min(dest.xmin + target->winx * 0.501f, dest.xmax - AREAMINX * UI_SCALE_FAC);
|
||||
dest.xmin = split + half_line_width;
|
||||
remainder.xmax = split - half_line_width;
|
||||
}
|
||||
else if (dock_target == AreaDockTarget::Left) {
|
||||
split = std::max(dest.xmax - target->winx * 0.501f, dest.xmin + AREAMINX * UI_SCALE_FAC);
|
||||
dest.xmax = split - half_line_width;
|
||||
remainder.xmin = split + half_line_width;
|
||||
}
|
||||
else if (dock_target == AreaDockTarget::Top) {
|
||||
split = std::min(dest.ymin + target->winy * 0.501f, dest.ymax - HEADERY * UI_SCALE_FAC);
|
||||
dest.ymin = split + half_line_width;
|
||||
remainder.ymax = split - half_line_width;
|
||||
}
|
||||
else if (dock_target == AreaDockTarget::Bottom) {
|
||||
split = std::max(dest.ymax - target->winy * 0.501f, dest.ymin + HEADERY * UI_SCALE_FAC);
|
||||
dest.ymax = split - half_line_width;
|
||||
remainder.ymin = split + half_line_width;
|
||||
}
|
||||
|
||||
if (dock_target == AreaDockTarget::Center) {
|
||||
UI_draw_roundbox_4fv_ex(&dest, inner, nullptr, 1.0f, outline, U.pixelsize, 6 * U.pixelsize);
|
||||
screen_draw_area_icon(&dest, ED_area_icon(source), icon_color, bg_color, outline);
|
||||
}
|
||||
else {
|
||||
UI_draw_roundbox_4fv_ex(&dest, inner, nullptr, 1.0f, outline, U.pixelsize, 6 * U.pixelsize);
|
||||
screen_draw_area_icon(&dest, ED_area_icon(source), icon_color, bg_color, outline);
|
||||
|
||||
bg_color[3] = 0.3f;
|
||||
icon_color[3] = 128;
|
||||
screen_draw_area_icon(&remainder, ED_area_icon(target), icon_color, bg_color, nullptr);
|
||||
|
||||
/* Darken the split position itself. */
|
||||
if (ELEM(dock_target, AreaDockTarget::Right, AreaDockTarget::Left)) {
|
||||
dest.xmin = split - half_line_width;
|
||||
dest.xmax = split + half_line_width;
|
||||
}
|
||||
else {
|
||||
dest.ymin = split - half_line_width;
|
||||
dest.ymax = split + half_line_width;
|
||||
}
|
||||
UI_draw_roundbox_4fv(&dest, true, 0.0f, border);
|
||||
}
|
||||
}
|
||||
|
||||
void screen_draw_split_preview(ScrArea *area, const eScreenAxis dir_axis, const float fac)
|
||||
{
|
||||
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
|
||||
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
|
||||
float outline[4] = {1.0f, 1.0f, 1.0f, 0.4f};
|
||||
float inner[4] = {1.0f, 1.0f, 1.0f, 0.10f};
|
||||
float border[4];
|
||||
UI_GetThemeColor4fv(TH_EDITOR_OUTLINE, border);
|
||||
UI_draw_roundbox_corner_set(UI_CNR_ALL);
|
||||
|
||||
/* Split-point. */
|
||||
GPU_blend(GPU_BLEND_ALPHA);
|
||||
rctf rect;
|
||||
BLI_rctf_rcti_copy(&rect, &area->totrct);
|
||||
|
||||
immUniformColor4ub(255, 255, 255, 100);
|
||||
if (fac < 0.0001 || fac > 0.9999) {
|
||||
/* Highlight the entire area. */
|
||||
UI_draw_roundbox_4fv_ex(&rect, inner, nullptr, 1.0f, outline, U.pixelsize, 7 * U.pixelsize);
|
||||
return;
|
||||
}
|
||||
|
||||
immBegin(GPU_PRIM_LINES, 2);
|
||||
float x = (1 - fac) * rect.xmin + fac * rect.xmax;
|
||||
float y = (1 - fac) * rect.ymin + fac * rect.ymax;
|
||||
x = std::clamp(x, rect.xmin + (AREAMINX * UI_SCALE_FAC), rect.xmax - (AREAMINX * UI_SCALE_FAC));
|
||||
y = std::clamp(y, rect.ymin + (HEADERY * UI_SCALE_FAC), rect.ymax - (HEADERY * UI_SCALE_FAC));
|
||||
float half_line_width = 2.0f * U.pixelsize;
|
||||
|
||||
/* Outlined rectangle to left/above split position. */
|
||||
rect.xmax = (dir_axis == SCREEN_AXIS_V) ? x - half_line_width : rect.xmax;
|
||||
rect.ymax = (dir_axis == SCREEN_AXIS_H) ? y - half_line_width : rect.ymax;
|
||||
|
||||
UI_draw_roundbox_4fv_ex(&rect, inner, nullptr, 1.0f, outline, U.pixelsize, 7 * U.pixelsize);
|
||||
|
||||
/* Outlined rectangle to right/below split position. */
|
||||
if (dir_axis == SCREEN_AXIS_H) {
|
||||
const float y = (1 - fac) * area->totrct.ymin + fac * area->totrct.ymax;
|
||||
|
||||
immVertex2f(pos, area->totrct.xmin, y);
|
||||
immVertex2f(pos, area->totrct.xmax, y);
|
||||
|
||||
immEnd();
|
||||
|
||||
immUniformColor4ub(0, 0, 0, 100);
|
||||
|
||||
immBegin(GPU_PRIM_LINES, 2);
|
||||
|
||||
immVertex2f(pos, area->totrct.xmin, y + 1);
|
||||
immVertex2f(pos, area->totrct.xmax, y + 1);
|
||||
|
||||
immEnd();
|
||||
rect.ymin = y + half_line_width;
|
||||
rect.ymax = area->totrct.ymax;
|
||||
}
|
||||
else {
|
||||
BLI_assert(dir_axis == SCREEN_AXIS_V);
|
||||
const float x = (1 - fac) * area->totrct.xmin + fac * area->totrct.xmax;
|
||||
|
||||
immVertex2f(pos, x, area->totrct.ymin);
|
||||
immVertex2f(pos, x, area->totrct.ymax);
|
||||
|
||||
immEnd();
|
||||
|
||||
immUniformColor4ub(0, 0, 0, 100);
|
||||
|
||||
immBegin(GPU_PRIM_LINES, 2);
|
||||
|
||||
immVertex2f(pos, x + 1, area->totrct.ymin);
|
||||
immVertex2f(pos, x + 1, area->totrct.ymax);
|
||||
|
||||
immEnd();
|
||||
rect.xmin = x + half_line_width;
|
||||
rect.xmax = area->totrct.xmax;
|
||||
}
|
||||
UI_draw_roundbox_4fv_ex(&rect, inner, nullptr, 1.0f, outline, U.pixelsize, 7 * U.pixelsize);
|
||||
|
||||
GPU_blend(GPU_BLEND_NONE);
|
||||
|
||||
immUnbindProgram();
|
||||
/* Darken the split position itself. */
|
||||
if (dir_axis == SCREEN_AXIS_H) {
|
||||
rect.ymin = y - half_line_width;
|
||||
rect.ymax = y + half_line_width;
|
||||
}
|
||||
else {
|
||||
rect.xmin = x - half_line_width;
|
||||
rect.xmax = x + half_line_width;
|
||||
}
|
||||
UI_draw_roundbox_4fv(&rect, true, 0.0f, border);
|
||||
}
|
||||
|
||||
@@ -47,6 +47,15 @@ enum eScreenAxis {
|
||||
SCREEN_AXIS_V = 'v',
|
||||
};
|
||||
|
||||
enum class AreaDockTarget {
|
||||
None,
|
||||
Right, /* Right diagonal quadrant of area. */
|
||||
Left, /* Left diagonal quadrant of area. */
|
||||
Top, /* Top diagonal quadrant of area. */
|
||||
Bottom, /* Bottom diagonal quadrant of area. */
|
||||
Center, /* Middle portion of area. */
|
||||
};
|
||||
|
||||
#define AZONESPOTW UI_HEADER_OFFSET /* width of corner #AZone - max */
|
||||
#define AZONESPOTH (0.6f * U.widget_unit) /* height of corner #AZone */
|
||||
#define AZONEFADEIN (5.0f * U.widget_unit) /* when #AZone is totally visible */
|
||||
@@ -79,7 +88,11 @@ void region_toggle_hidden(bContext *C, ARegion *region, bool do_fade);
|
||||
* \param sa1: Area from which the resultant originates.
|
||||
* \param sa2: Target area that will be replaced.
|
||||
*/
|
||||
void screen_draw_join_highlight(ScrArea *sa1, ScrArea *sa2);
|
||||
void screen_draw_join_highlight(ScrArea *sa1, ScrArea *sa2, eScreenDir dir);
|
||||
void screen_draw_dock_preview(const wmWindow *win,
|
||||
ScrArea *source,
|
||||
ScrArea *target,
|
||||
AreaDockTarget dock_target);
|
||||
void screen_draw_split_preview(ScrArea *area, eScreenAxis dir_axis, float fac);
|
||||
|
||||
/* `screen_edit.cc` */
|
||||
|
||||
@@ -75,6 +75,8 @@
|
||||
|
||||
#include "GPU_capabilities.hh"
|
||||
|
||||
#include "wm_window.hh"
|
||||
|
||||
#include "screen_intern.hh" /* own module include */
|
||||
|
||||
using blender::Vector;
|
||||
@@ -1103,7 +1105,11 @@ static int actionzone_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
BLI_assert(ELEM(sad->az->type, AZONE_AREA, AZONE_REGION_SCROLL));
|
||||
if (U.experimental.use_docking && sad->az->type == AZONE_AREA) {
|
||||
actionzone_apply(C, op, sad->az->type);
|
||||
actionzone_exit(op);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
/* add modal handler */
|
||||
G.moving |= G_TRANSFORM_WM;
|
||||
@@ -1457,24 +1463,10 @@ static void area_dupli_fn(bScreen * /*screen*/, ScrArea *area, void *user_data)
|
||||
};
|
||||
|
||||
/* operator callback */
|
||||
static int area_dupli_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
static bool area_dupli_open(bContext *C, ScrArea *area, const blender::int2 position)
|
||||
{
|
||||
ScrArea *area = CTX_wm_area(C);
|
||||
|
||||
if (event && event->customdata) {
|
||||
sActionzoneData *sad = static_cast<sActionzoneData *>(event->customdata);
|
||||
if (sad == nullptr) {
|
||||
return OPERATOR_PASS_THROUGH;
|
||||
}
|
||||
area = sad->sa1;
|
||||
}
|
||||
|
||||
const rcti window_rect = {
|
||||
/*xmin*/ area->totrct.xmin,
|
||||
/*xmax*/ area->totrct.xmin + area->winx,
|
||||
/*ymin*/ area->totrct.ymin,
|
||||
/*ymax*/ area->totrct.ymin + area->winy,
|
||||
};
|
||||
position.x, position.x + area->winx, position.y, position.y + area->winy};
|
||||
|
||||
/* Create new window. No need to set space_type since it will be copied over. */
|
||||
wmWindow *newwin = WM_window_open(C,
|
||||
@@ -1488,6 +1480,21 @@ static int area_dupli_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
/* Initialize area from callback. */
|
||||
area_dupli_fn,
|
||||
(void *)area);
|
||||
return (newwin != nullptr);
|
||||
}
|
||||
|
||||
static int area_dupli_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
ScrArea *area = CTX_wm_area(C);
|
||||
if (event && event->customdata) {
|
||||
sActionzoneData *sad = static_cast<sActionzoneData *>(event->customdata);
|
||||
if (sad == nullptr) {
|
||||
return OPERATOR_PASS_THROUGH;
|
||||
}
|
||||
area = sad->sa1;
|
||||
}
|
||||
|
||||
bool newwin = area_dupli_open(C, area, blender::int2(area->totrct.xmin, area->totrct.ymin));
|
||||
|
||||
if (newwin) {
|
||||
/* screen, areas init */
|
||||
@@ -3514,19 +3521,64 @@ static void SCREEN_OT_screen_full_area(wmOperatorType *ot)
|
||||
*/
|
||||
|
||||
struct sAreaJoinData {
|
||||
ScrArea *sa1; /* Potential source area (kept). */
|
||||
ScrArea *sa2; /* Potential target area (removed or reduced). */
|
||||
eScreenDir dir; /* Direction of potential join. */
|
||||
void *draw_callback; /* call #screen_draw_join_highlight */
|
||||
ScrArea *sa1; /* Potential source area (kept). */
|
||||
ScrArea *sa2; /* Potential target area (removed or reduced). */
|
||||
eScreenDir dir; /* Direction of potential join. */
|
||||
eScreenAxis split_dir; /* Direction of split within the source area. */
|
||||
AreaDockTarget dock_target; /* Position within target we are pointing to. */
|
||||
int x, y; /* Starting mouse position. */
|
||||
float split_fac; /* Split factor in split_dir direction. */
|
||||
wmWindow *win1; /* Window of source area. */
|
||||
wmWindow *win2; /* Window of the target area. */
|
||||
wmWindow *draw_dock_win; /* Window getting docking highlight. */
|
||||
bool close_win; /* Close the source window when done. */
|
||||
void *draw_callback; /* call #screen_draw_join_highlight */
|
||||
void *draw_dock_callback; /* call #screen_draw_dock_highlight, overlay on draw_dock_win. */
|
||||
};
|
||||
|
||||
static void area_join_draw_cb(const wmWindow * /*win*/, void *userdata)
|
||||
{
|
||||
const wmOperator *op = static_cast<const wmOperator *>(userdata);
|
||||
|
||||
sAreaJoinData *sd = static_cast<sAreaJoinData *>(op->customdata);
|
||||
if (sd->sa1 && sd->sa2 && (sd->dir != SCREEN_DIR_NONE)) {
|
||||
screen_draw_join_highlight(sd->sa1, sd->sa2);
|
||||
if (!sd || !sd->sa1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (sd->sa1 == sd->sa2) {
|
||||
screen_draw_split_preview(sd->sa1, sd->split_dir, sd->split_fac);
|
||||
}
|
||||
else {
|
||||
screen_draw_join_highlight(sd->sa1, sd->sa2, sd->dir);
|
||||
}
|
||||
}
|
||||
|
||||
static void area_join_dock_cb(const struct wmWindow *win, void *userdata)
|
||||
{
|
||||
const wmOperator *op = static_cast<wmOperator *>(userdata);
|
||||
sAreaJoinData *jd = static_cast<sAreaJoinData *>(op->customdata);
|
||||
if (!jd || !jd->sa2 || jd->dir != SCREEN_DIR_NONE || jd->sa1 == jd->sa2) {
|
||||
return;
|
||||
}
|
||||
screen_draw_dock_preview(win, jd->sa1, jd->sa2, jd->dock_target);
|
||||
}
|
||||
|
||||
static void area_join_dock_cb_window(sAreaJoinData *jd, wmOperator *op)
|
||||
{
|
||||
if (jd->sa2 && jd->win2 != jd->draw_dock_win) {
|
||||
/* Change of highlight window. */
|
||||
if (jd->draw_dock_callback) {
|
||||
WM_draw_cb_exit(jd->draw_dock_win, jd->draw_dock_callback);
|
||||
/* Refresh the entire window. */
|
||||
ED_screen_areas_iter (
|
||||
jd->draw_dock_win, WM_window_get_active_screen(jd->draw_dock_win), area)
|
||||
{
|
||||
ED_area_tag_redraw(area);
|
||||
}
|
||||
}
|
||||
if (jd->win2) {
|
||||
jd->draw_dock_win = jd->win2;
|
||||
jd->draw_dock_callback = WM_draw_cb_activate(jd->draw_dock_win, area_join_dock_cb, op);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3534,27 +3586,27 @@ static void area_join_draw_cb(const wmWindow * /*win*/, void *userdata)
|
||||
/* return false: init failed */
|
||||
static bool area_join_init(bContext *C, wmOperator *op, ScrArea *sa1, ScrArea *sa2)
|
||||
{
|
||||
if (sa1 == nullptr || sa2 == nullptr) {
|
||||
if (sa1 == nullptr && sa2 == nullptr) {
|
||||
/* Get areas from cursor location if not specified. */
|
||||
int cursor[2];
|
||||
RNA_int_get_array(op->ptr, "cursor", cursor);
|
||||
screen_area_edge_from_cursor(C, cursor, &sa1, &sa2);
|
||||
RNA_int_get_array(op->ptr, "source_xy", cursor);
|
||||
sa1 = BKE_screen_find_area_xy(CTX_wm_screen(C), SPACE_TYPE_ANY, cursor);
|
||||
RNA_int_get_array(op->ptr, "target_xy", cursor);
|
||||
sa2 = BKE_screen_find_area_xy(CTX_wm_screen(C), SPACE_TYPE_ANY, cursor);
|
||||
}
|
||||
if (sa1 == nullptr || sa2 == nullptr) {
|
||||
if (sa1 == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
sAreaJoinData *jd = static_cast<sAreaJoinData *>(
|
||||
MEM_callocN(sizeof(sAreaJoinData), "op_area_join"));
|
||||
|
||||
jd->sa1 = sa1;
|
||||
jd->sa2 = sa2;
|
||||
jd->dir = SCREEN_DIR_NONE;
|
||||
jd->dir = area_getorientation(sa1, sa2);
|
||||
jd->win1 = WM_window_find_by_area(CTX_wm_manager(C), sa1);
|
||||
jd->win2 = WM_window_find_by_area(CTX_wm_manager(C), sa2);
|
||||
|
||||
op->customdata = jd;
|
||||
|
||||
jd->draw_callback = WM_draw_cb_activate(CTX_wm_window(C), area_join_draw_cb, op);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3591,7 +3643,10 @@ static void area_join_exit(bContext *C, wmOperator *op)
|
||||
|
||||
if (jd) {
|
||||
if (jd->draw_callback) {
|
||||
WM_draw_cb_exit(CTX_wm_window(C), jd->draw_callback);
|
||||
WM_draw_cb_exit(jd->win1, jd->draw_callback);
|
||||
}
|
||||
if (jd->draw_dock_callback) {
|
||||
WM_draw_cb_exit(jd->draw_dock_win, jd->draw_dock_callback);
|
||||
}
|
||||
|
||||
MEM_freeN(jd);
|
||||
@@ -3602,6 +3657,8 @@ static void area_join_exit(bContext *C, wmOperator *op)
|
||||
BKE_screen_remove_double_scredges(CTX_wm_screen(C));
|
||||
BKE_screen_remove_unused_scredges(CTX_wm_screen(C));
|
||||
BKE_screen_remove_unused_scrverts(CTX_wm_screen(C));
|
||||
|
||||
G.moving &= ~G_TRANSFORM_WM;
|
||||
}
|
||||
|
||||
static int area_join_exec(bContext *C, wmOperator *op)
|
||||
@@ -3610,8 +3667,15 @@ static int area_join_exec(bContext *C, wmOperator *op)
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
|
||||
|
||||
if (jd->sa2 == nullptr || area_getorientation(jd->sa1, jd->sa2) == SCREEN_DIR_NONE) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
area_join_apply(C, op);
|
||||
area_join_exit(C, op);
|
||||
WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, nullptr);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
@@ -3622,99 +3686,354 @@ static int area_join_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
if (event->type == EVT_ACTIONZONE_AREA) {
|
||||
sActionzoneData *sad = static_cast<sActionzoneData *>(event->customdata);
|
||||
|
||||
if (sad == nullptr || sad->modifier > 0) {
|
||||
if (sad == nullptr || sad->modifier > 0 || sad->sa1 == nullptr) {
|
||||
return OPERATOR_PASS_THROUGH;
|
||||
}
|
||||
|
||||
/* verify *sad itself */
|
||||
if (sad->sa1 == nullptr || sad->sa2 == nullptr) {
|
||||
return OPERATOR_PASS_THROUGH;
|
||||
}
|
||||
|
||||
/* is this our *sad? if areas equal it should be passed on */
|
||||
if (sad->sa1 == sad->sa2) {
|
||||
return OPERATOR_PASS_THROUGH;
|
||||
}
|
||||
if (!area_join_init(C, op, sad->sa1, sad->sa2)) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
|
||||
jd->x = sad->x;
|
||||
jd->y = sad->y;
|
||||
jd->draw_callback = WM_draw_cb_activate(CTX_wm_window(C), area_join_draw_cb, op);
|
||||
|
||||
WM_event_add_modal_handler(C, op);
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
|
||||
/* add temp handler */
|
||||
WM_event_add_modal_handler(C, op);
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
/* Apply the docking of the area. */
|
||||
void static area_docking_apply(bContext *C, wmOperator *op)
|
||||
{
|
||||
sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
|
||||
|
||||
int offset1;
|
||||
int offset2;
|
||||
area_getoffsets(jd->sa1, jd->sa2, area_getorientation(jd->sa1, jd->sa2), &offset1, &offset2);
|
||||
|
||||
/* Check before making changes. */
|
||||
bool aligned_neighbors = (offset1 == 0 && offset2 == 0);
|
||||
bool same_area = (jd->sa1 == jd->sa2);
|
||||
|
||||
if (!(jd->dock_target == AreaDockTarget::Center)) {
|
||||
eScreenAxis dir = (ELEM(jd->dock_target, AreaDockTarget::Left, AreaDockTarget::Right)) ?
|
||||
SCREEN_AXIS_V :
|
||||
SCREEN_AXIS_H;
|
||||
float fac = ELEM(jd->dock_target, AreaDockTarget::Left, AreaDockTarget::Bottom) ? 0.49999f :
|
||||
0.50001f;
|
||||
ScrArea *newa = area_split(
|
||||
jd->win2, WM_window_get_active_screen(jd->win2), jd->sa2, dir, fac, true);
|
||||
jd->sa2 = newa;
|
||||
}
|
||||
|
||||
if (same_area) {
|
||||
WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!aligned_neighbors || !screen_area_join(C, CTX_wm_screen(C), jd->sa1, jd->sa2)) {
|
||||
ED_area_swapspace(C, jd->sa2, jd->sa1);
|
||||
if (BLI_listbase_is_single(&WM_window_get_active_screen(jd->win1)->areabase) &&
|
||||
BLI_listbase_is_empty(&jd->win1->global_areas.areabase))
|
||||
{
|
||||
jd->close_win = true;
|
||||
}
|
||||
else {
|
||||
screen_area_close(C, CTX_wm_screen(C), jd->sa1);
|
||||
}
|
||||
}
|
||||
|
||||
if (jd && jd->sa2 == CTX_wm_area(C)) {
|
||||
CTX_wm_area_set(C, nullptr);
|
||||
CTX_wm_region_set(C, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
static int area_join_cursor(sAreaJoinData *jd, const wmEvent *event)
|
||||
{
|
||||
if (!jd->sa2 && jd->dock_target == AreaDockTarget::None && U.experimental.use_docking) {
|
||||
/* Mouse outside window, so can open new window. */
|
||||
if (event->xy[0] < 0 || event->xy[0] > jd->win1->sizex || event->xy[1] < 1 ||
|
||||
event->xy[1] > jd->win1->sizey)
|
||||
{
|
||||
return WM_CURSOR_PICK_AREA;
|
||||
}
|
||||
return WM_CURSOR_STOP;
|
||||
}
|
||||
|
||||
if (jd->sa1 && jd->sa1 == jd->sa2 && U.experimental.use_docking) {
|
||||
if (jd->split_fac >= 0.0001f) {
|
||||
/* Mouse inside source area, so allow splitting. */
|
||||
return (jd->split_dir == SCREEN_AXIS_V) ? WM_CURSOR_V_SPLIT : WM_CURSOR_H_SPLIT;
|
||||
}
|
||||
return WM_CURSOR_EDIT;
|
||||
}
|
||||
|
||||
if (jd->dock_target == AreaDockTarget::None) {
|
||||
if (jd->dir == SCREEN_DIR_N) {
|
||||
return WM_CURSOR_N_ARROW;
|
||||
}
|
||||
if (jd->dir == SCREEN_DIR_S) {
|
||||
return WM_CURSOR_S_ARROW;
|
||||
}
|
||||
if (jd->dir == SCREEN_DIR_W) {
|
||||
return WM_CURSOR_W_ARROW;
|
||||
}
|
||||
if (jd->dir == SCREEN_DIR_E) {
|
||||
return WM_CURSOR_E_ARROW;
|
||||
}
|
||||
}
|
||||
|
||||
if (U.experimental.use_docking &&
|
||||
(jd->dir != SCREEN_DIR_NONE || jd->dock_target != AreaDockTarget::None))
|
||||
{
|
||||
return WM_CURSOR_PICK_AREA;
|
||||
}
|
||||
|
||||
return U.experimental.use_docking ? WM_CURSOR_PICK_AREA : WM_CURSOR_STOP;
|
||||
}
|
||||
|
||||
static AreaDockTarget area_docking_target(sAreaJoinData *jd, const wmEvent *event)
|
||||
{
|
||||
if (!U.experimental.use_docking || !jd->sa2 || !jd->win2) {
|
||||
return AreaDockTarget::None;
|
||||
}
|
||||
|
||||
/* Convert to local coordinates in sa2. */
|
||||
const int x = event->xy[0] + jd->win1->posx - jd->win2->posx - jd->sa2->totrct.xmin;
|
||||
const int y = event->xy[1] + jd->win1->posy - jd->win2->posy - jd->sa2->totrct.ymin;
|
||||
|
||||
const float fac_x = float(x) / float(jd->sa2->winx);
|
||||
const float fac_y = float(y) / float(jd->sa2->winy);
|
||||
const int min_x = 2 * AREAMINX * UI_SCALE_FAC;
|
||||
const int min_y = 2 * HEADERY * UI_SCALE_FAC;
|
||||
|
||||
if (ELEM(jd->dir, SCREEN_DIR_N, SCREEN_DIR_S)) {
|
||||
/* Up or Down to immediate neighbor. */
|
||||
if (event->xy[0] <= jd->sa1->totrct.xmax && event->xy[0] >= jd->sa1->totrct.xmin) {
|
||||
const int join_y = std::min(jd->sa2->winy * 0.3f, 6 * HEADERY * UI_SCALE_FAC);
|
||||
if (jd->sa2->winy < min_y || (jd->dir == SCREEN_DIR_N && y < join_y) ||
|
||||
(jd->dir == SCREEN_DIR_S && (jd->sa2->winy - y) < join_y))
|
||||
{
|
||||
return AreaDockTarget::None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ELEM(jd->dir, SCREEN_DIR_W, SCREEN_DIR_E)) {
|
||||
/* Left or Right to immediate neighbor. */
|
||||
if (event->xy[1] <= jd->sa1->totrct.ymax && event->xy[1] >= jd->sa1->totrct.ymin) {
|
||||
const int join_x = std::min(jd->sa2->winx * 0.3f, 6 * AREAMINX * UI_SCALE_FAC);
|
||||
if (jd->sa2->winx < min_x || (jd->dir == SCREEN_DIR_W && (jd->sa2->winx - x) < join_x) ||
|
||||
(jd->dir == SCREEN_DIR_E && x < join_x))
|
||||
{
|
||||
return AreaDockTarget::None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If we've made it here, then there can be no joining possible. */
|
||||
jd->dir = SCREEN_DIR_NONE;
|
||||
|
||||
/* if the area is narrow then there are only two docking targets. */
|
||||
if (jd->sa2->winx < min_x) {
|
||||
return (y < jd->sa2->winy / 2) ? AreaDockTarget::Bottom : AreaDockTarget::Top;
|
||||
}
|
||||
if (jd->sa2->winy < min_y) {
|
||||
return (x < jd->sa2->winx / 2) ? AreaDockTarget::Left : AreaDockTarget::Right;
|
||||
}
|
||||
|
||||
/* Are we in the center? But not in same area! */
|
||||
if (jd->sa1 != jd->sa2 && fac_x > 0.4f && fac_x < 0.6f && fac_y > 0.4f && fac_y < 0.6f) {
|
||||
return AreaDockTarget::Center;
|
||||
}
|
||||
|
||||
/* Area is large enough for four docking targets. */
|
||||
const float area_ratio = float(jd->sa2->winx) / float(jd->sa2->winy);
|
||||
/* Split the area diagonally from top-right to bottom-left. */
|
||||
const bool upper_left = float(x) / float(y + 1) < area_ratio;
|
||||
/* Split the area diagonally from top-left to bottom-right. */
|
||||
const bool lower_left = float(x) / float(jd->sa2->winy - y + 1) < area_ratio;
|
||||
if (upper_left && !lower_left) {
|
||||
return AreaDockTarget::Top;
|
||||
}
|
||||
if (!upper_left && lower_left) {
|
||||
return AreaDockTarget::Bottom;
|
||||
}
|
||||
if (upper_left && lower_left) {
|
||||
return AreaDockTarget::Left;
|
||||
}
|
||||
if (!upper_left && !lower_left) {
|
||||
return AreaDockTarget::Right;
|
||||
}
|
||||
return AreaDockTarget::None;
|
||||
}
|
||||
|
||||
static float area_split_factor(bContext *C, sAreaJoinData *jd, const wmEvent *event)
|
||||
{
|
||||
float fac = (jd->split_dir == SCREEN_AXIS_V) ?
|
||||
float(event->xy[0] - jd->sa1->totrct.xmin) / float(jd->sa1->winx + 1) :
|
||||
float(event->xy[1] - jd->sa1->totrct.ymin) / float(jd->sa1->winy + 1);
|
||||
|
||||
if (event->modifier & KM_CTRL) {
|
||||
/* Snapping on. */
|
||||
|
||||
/* Find nearest neighboring vertex. */
|
||||
const int axis = (jd->split_dir == SCREEN_AXIS_V) ? 0 : 1;
|
||||
int dist = INT_MAX;
|
||||
int loc = 0;
|
||||
LISTBASE_FOREACH (const ScrVert *, v1, &CTX_wm_screen(C)->vertbase) {
|
||||
const int v_loc = (&v1->vec.x)[axis];
|
||||
const int v_dist = abs(v_loc - event->xy[axis]);
|
||||
if (v_dist < dist) {
|
||||
loc = v_loc;
|
||||
dist = v_dist;
|
||||
}
|
||||
}
|
||||
float near_fac = (axis) ? float(loc - jd->sa1->totrct.ymin) / float(jd->sa1->winy + 1) :
|
||||
float(loc - jd->sa1->totrct.xmin) / float(jd->sa1->winx + 1);
|
||||
|
||||
/* Rounded to nearest 12th. */
|
||||
float frac_fac = round(fac * 12.0f) / 12.0f;
|
||||
|
||||
/* Use nearest neighbor or fractional, whichever is closest. */
|
||||
fac = (fabs(near_fac - fac) < fabs(frac_fac - fac)) ? near_fac : frac_fac;
|
||||
}
|
||||
|
||||
return std::clamp(fac, 0.001f, 0.999f);
|
||||
}
|
||||
|
||||
static void area_join_update_data(bContext *C, sAreaJoinData *jd, const wmEvent *event)
|
||||
{
|
||||
ScrArea *area = ED_area_find_under_cursor(C, SPACE_TYPE_ANY, event->xy);
|
||||
|
||||
jd->win2 = WM_window_find_by_area(CTX_wm_manager(C), jd->sa2);
|
||||
jd->dir = SCREEN_DIR_NONE;
|
||||
jd->dock_target = AreaDockTarget::None;
|
||||
jd->dir = area_getorientation(jd->sa1, jd->sa2);
|
||||
jd->dock_target = area_docking_target(jd, event);
|
||||
|
||||
if (!U.experimental.use_docking && area == jd->sa1) {
|
||||
/* Hovering current source, so change direction. */
|
||||
jd->sa1 = jd->sa2;
|
||||
jd->sa2 = area;
|
||||
jd->dir = area_getorientation(jd->sa1, jd->sa2);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(abs(jd->x - event->xy[0]) > (10 * U.pixelsize) ||
|
||||
abs(jd->y - event->xy[1]) > (10 * U.pixelsize)))
|
||||
{
|
||||
jd->sa2 = area;
|
||||
return;
|
||||
}
|
||||
|
||||
if (jd->sa1 == area) {
|
||||
jd->sa2 = area;
|
||||
jd->split_dir = (abs(event->xy[0] - jd->x) > abs(event->xy[1] - jd->y)) ? SCREEN_AXIS_V :
|
||||
SCREEN_AXIS_H;
|
||||
jd->split_fac = area_split_factor(C, jd, event);
|
||||
return;
|
||||
}
|
||||
|
||||
if (U.experimental.use_docking) {
|
||||
jd->sa2 = area;
|
||||
jd->win2 = WM_window_find_by_area(CTX_wm_manager(C), jd->sa2);
|
||||
jd->dir = area_getorientation(jd->sa1, jd->sa2);
|
||||
jd->dock_target = area_docking_target(jd, event);
|
||||
}
|
||||
}
|
||||
|
||||
static void area_join_cancel(bContext *C, wmOperator *op)
|
||||
{
|
||||
WM_event_add_notifier(C, NC_WINDOW, nullptr);
|
||||
|
||||
sAreaJoinData *jd = static_cast<sAreaJoinData *>(op->customdata);
|
||||
if (!jd || jd->dock_target != AreaDockTarget::None || jd->dir != SCREEN_DIR_NONE) {
|
||||
WM_cursor_set(CTX_wm_window(C), WM_CURSOR_DEFAULT);
|
||||
}
|
||||
|
||||
area_join_exit(C, op);
|
||||
}
|
||||
|
||||
/* modal callback while selecting area (space) that will be removed */
|
||||
static int area_join_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
bScreen *screen = CTX_wm_screen(C);
|
||||
wmWindow *win = CTX_wm_window(C);
|
||||
|
||||
if (op->customdata == nullptr) {
|
||||
if (!area_join_init(C, op, nullptr, nullptr)) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
}
|
||||
sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
|
||||
if (jd == nullptr) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
/* execute the events */
|
||||
switch (event->type) {
|
||||
|
||||
case MOUSEMOVE: {
|
||||
ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, event->xy);
|
||||
jd->dir = area_getorientation(jd->sa1, jd->sa2);
|
||||
|
||||
if (area == jd->sa1) {
|
||||
/* Hovering current source, so change direction. */
|
||||
jd->sa1 = jd->sa2;
|
||||
jd->sa2 = area;
|
||||
jd->dir = area_getorientation(jd->sa1, jd->sa2);
|
||||
}
|
||||
else if (area != jd->sa2) {
|
||||
jd->dir = SCREEN_DIR_NONE;
|
||||
}
|
||||
|
||||
area_join_update_data(C, jd, event);
|
||||
area_join_dock_cb_window(jd, op);
|
||||
WM_cursor_set(jd->win1, area_join_cursor(jd, event));
|
||||
WM_event_add_notifier(C, NC_WINDOW, nullptr);
|
||||
|
||||
if (jd->dir == SCREEN_DIR_N) {
|
||||
WM_cursor_set(win, WM_CURSOR_N_ARROW);
|
||||
}
|
||||
else if (jd->dir == SCREEN_DIR_S) {
|
||||
WM_cursor_set(win, WM_CURSOR_S_ARROW);
|
||||
}
|
||||
else if (jd->dir == SCREEN_DIR_E) {
|
||||
WM_cursor_set(win, WM_CURSOR_E_ARROW);
|
||||
}
|
||||
else if (jd->dir == SCREEN_DIR_W) {
|
||||
WM_cursor_set(win, WM_CURSOR_W_ARROW);
|
||||
}
|
||||
else {
|
||||
WM_cursor_set(win, WM_CURSOR_STOP);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case LEFTMOUSE:
|
||||
if (event->val == KM_RELEASE) {
|
||||
if (jd->dir == SCREEN_DIR_NONE) {
|
||||
area_join_update_data(C, jd, event);
|
||||
area_join_dock_cb_window(jd, op);
|
||||
ED_area_tag_redraw(jd->sa1);
|
||||
ED_area_tag_redraw(jd->sa2);
|
||||
if (U.experimental.use_docking && jd->sa1 && !jd->sa2) {
|
||||
/* Break out into new window if we are really outside the source window bounds. */
|
||||
if (event->xy[0] < 0 || event->xy[0] > jd->win1->sizex || event->xy[1] < 1 ||
|
||||
event->xy[1] > jd->win1->sizey)
|
||||
{
|
||||
/* We have to clear handlers or we get an error in wm_gizmomap_modal_get. */
|
||||
WM_event_modal_handler_region_replace(jd->win1, CTX_wm_region(C), nullptr);
|
||||
area_dupli_open(C, jd->sa1, blender::int2(event->xy[0], event->xy[1] - jd->sa1->winy));
|
||||
screen_area_close(C, WM_window_get_active_screen(jd->win1), jd->sa1);
|
||||
}
|
||||
}
|
||||
else if (jd->sa1 && jd->sa1 == jd->sa2) {
|
||||
/* Same area so split. */
|
||||
if (area_split_allowed(jd->sa1, jd->split_dir) && jd->split_fac > 0.0001) {
|
||||
jd->sa1 = area_split(jd->win2,
|
||||
WM_window_get_active_screen(jd->win1),
|
||||
jd->sa1,
|
||||
jd->split_dir,
|
||||
jd->split_fac,
|
||||
true);
|
||||
ED_area_tag_redraw(jd->sa1);
|
||||
}
|
||||
}
|
||||
else if (U.experimental.use_docking && jd->sa1 && jd->sa2 &&
|
||||
jd->dock_target != AreaDockTarget::None)
|
||||
{
|
||||
/* Dock this to the new location. */
|
||||
area_docking_apply(C, op);
|
||||
}
|
||||
else if (jd->sa1 && jd->sa2 && jd->dir != SCREEN_DIR_NONE) {
|
||||
/* Join to neighbor. */
|
||||
area_join_apply(C, op);
|
||||
}
|
||||
else {
|
||||
area_join_cancel(C, op);
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
ED_area_tag_redraw(jd->sa1);
|
||||
ED_area_tag_redraw(jd->sa2);
|
||||
|
||||
area_join_apply(C, op);
|
||||
WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, nullptr);
|
||||
const bool close_win = jd->close_win;
|
||||
area_join_exit(C, op);
|
||||
if (close_win) {
|
||||
wm_window_close(C, CTX_wm_manager(C), CTX_wm_window(C));
|
||||
}
|
||||
|
||||
WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, nullptr);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
break;
|
||||
@@ -3747,8 +4066,26 @@ static void SCREEN_OT_area_join(wmOperatorType *ot)
|
||||
ot->flag = OPTYPE_BLOCKING | OPTYPE_INTERNAL;
|
||||
|
||||
/* rna */
|
||||
RNA_def_int_vector(
|
||||
ot->srna, "cursor", 2, nullptr, INT_MIN, INT_MAX, "Cursor", "", INT_MIN, INT_MAX);
|
||||
RNA_def_int_vector(ot->srna,
|
||||
"source_xy",
|
||||
2,
|
||||
nullptr,
|
||||
INT_MIN,
|
||||
INT_MAX,
|
||||
"Source location",
|
||||
"",
|
||||
INT_MIN,
|
||||
INT_MAX);
|
||||
RNA_def_int_vector(ot->srna,
|
||||
"target_xy",
|
||||
2,
|
||||
nullptr,
|
||||
INT_MIN,
|
||||
INT_MAX,
|
||||
"Target location",
|
||||
"",
|
||||
INT_MIN,
|
||||
INT_MAX);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
@@ -3800,16 +4137,35 @@ static int screen_area_options_invoke(bContext *C, wmOperator *op, const wmEvent
|
||||
}
|
||||
|
||||
/* Join needs two very similar areas. */
|
||||
if (sa1 && sa2 && (area_getorientation(sa1, sa2) != -1)) {
|
||||
uiItemFullO(layout,
|
||||
"SCREEN_OT_area_join",
|
||||
IFACE_("Join Areas"),
|
||||
ICON_AREA_JOIN,
|
||||
nullptr,
|
||||
WM_OP_INVOKE_DEFAULT,
|
||||
UI_ITEM_NONE,
|
||||
&ptr);
|
||||
RNA_int_set_array(&ptr, "cursor", event->xy);
|
||||
if (sa1 && sa2) {
|
||||
eScreenDir dir = area_getorientation(sa1, sa2);
|
||||
if (dir != SCREEN_DIR_NONE) {
|
||||
uiItemFullO(layout,
|
||||
"SCREEN_OT_area_join",
|
||||
(ELEM(dir, SCREEN_DIR_N, SCREEN_DIR_S)) ? IFACE_("Join Up") :
|
||||
IFACE_("Join Right"),
|
||||
ICON_AREA_JOIN,
|
||||
nullptr,
|
||||
WM_OP_EXEC_DEFAULT,
|
||||
UI_ITEM_NONE,
|
||||
&ptr);
|
||||
RNA_int_set_array(&ptr, "source_xy", blender::int2{sa2->totrct.xmin, sa2->totrct.ymin});
|
||||
RNA_int_set_array(&ptr, "target_xy", blender::int2{sa1->totrct.xmin, sa1->totrct.ymin});
|
||||
|
||||
uiItemFullO(layout,
|
||||
"SCREEN_OT_area_join",
|
||||
(ELEM(dir, SCREEN_DIR_N, SCREEN_DIR_S)) ? IFACE_("Join Down") :
|
||||
IFACE_("Join Left"),
|
||||
ICON_AREA_JOIN,
|
||||
nullptr,
|
||||
WM_OP_EXEC_DEFAULT,
|
||||
UI_ITEM_NONE,
|
||||
&ptr);
|
||||
RNA_int_set_array(&ptr, "source_xy", blender::int2{sa1->totrct.xmin, sa1->totrct.ymin});
|
||||
RNA_int_set_array(&ptr, "target_xy", blender::int2{sa2->totrct.xmin, sa2->totrct.ymin});
|
||||
|
||||
uiItemS(layout);
|
||||
}
|
||||
}
|
||||
|
||||
/* Swap just needs two areas. */
|
||||
|
||||
@@ -756,7 +756,8 @@ typedef struct UserDef_Experimental {
|
||||
char use_new_file_import_nodes;
|
||||
char use_shader_node_previews;
|
||||
char use_animation_baklava;
|
||||
char _pad[3];
|
||||
char use_docking;
|
||||
char _pad[2];
|
||||
/** `makesdna` does not allow empty structs. */
|
||||
} UserDef_Experimental;
|
||||
|
||||
|
||||
@@ -7469,6 +7469,12 @@ static void rna_def_userdef_experimental(BlenderRNA *brna)
|
||||
"Multi-Slot Actions",
|
||||
"The new 'layered' Action can contain the animation for multiple data-blocks at once");
|
||||
RNA_def_property_update(prop, 0, "rna_userdef_update");
|
||||
|
||||
prop = RNA_def_property(srna, "use_docking", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_ui_text(prop,
|
||||
"Interactive Editor Docking",
|
||||
"Move editor areas to new locations, including between windows");
|
||||
RNA_def_property_update(prop, 0, "rna_userdef_update");
|
||||
}
|
||||
|
||||
static void rna_def_userdef_addon_collection(BlenderRNA *brna, PropertyRNA *cprop)
|
||||
|
||||
Reference in New Issue
Block a user