Add Chromium-only Blender WebEngine parity work
This commit is contained in:
64
blender-5.2.0/source/blender/editors/screen/CMakeLists.txt
Normal file
64
blender-5.2.0/source/blender/editors/screen/CMakeLists.txt
Normal file
@@ -0,0 +1,64 @@
|
||||
# SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
set(INC
|
||||
../asset
|
||||
../include
|
||||
../space_graph
|
||||
../../makesrna
|
||||
../../nodes
|
||||
# RNA_prototypes.hh
|
||||
${CMAKE_BINARY_DIR}/source/blender/makesrna
|
||||
)
|
||||
|
||||
set(INC_SYS
|
||||
)
|
||||
|
||||
set(SRC
|
||||
area.cc
|
||||
area_query.cc
|
||||
area_utils.cc
|
||||
glutil.cc
|
||||
screen_context.cc
|
||||
screen_draw.cc
|
||||
screen_edit.cc
|
||||
screen_geometry.cc
|
||||
screen_ops.cc
|
||||
screen_user_menu.cc
|
||||
screendump.cc
|
||||
workspace_edit.cc
|
||||
workspace_layout_edit.cc
|
||||
workspace_listen.cc
|
||||
|
||||
screen_intern.hh
|
||||
)
|
||||
|
||||
set(LIB
|
||||
PRIVATE bf::animrig
|
||||
PRIVATE bf::asset_system
|
||||
PRIVATE bf::blenfont
|
||||
PRIVATE bf::blenkernel
|
||||
PRIVATE bf::blenlib
|
||||
PRIVATE bf::blenloader
|
||||
PRIVATE bf::blentranslation
|
||||
PRIVATE bf::bmesh
|
||||
PRIVATE bf::depsgraph
|
||||
PRIVATE bf::dna
|
||||
bf_editor_datafiles
|
||||
bf_editor_space_sequencer
|
||||
PRIVATE bf::gpu
|
||||
PRIVATE bf::imbuf
|
||||
PRIVATE bf::intern::guardedalloc
|
||||
PRIVATE bf::sequencer
|
||||
PRIVATE bf::windowmanager
|
||||
)
|
||||
|
||||
if(WITH_HEADLESS)
|
||||
add_definitions(-DWITH_HEADLESS)
|
||||
endif()
|
||||
|
||||
blender_add_lib(bf_editor_screen "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
|
||||
|
||||
# RNA_prototypes.hh
|
||||
add_dependencies(bf_editor_screen bf_rna)
|
||||
4508
blender-5.2.0/source/blender/editors/screen/area.cc
Normal file
4508
blender-5.2.0/source/blender/editors/screen/area.cc
Normal file
File diff suppressed because it is too large
Load Diff
229
blender-5.2.0/source/blender/editors/screen/area_query.cc
Normal file
229
blender-5.2.0/source/blender/editors/screen/area_query.cc
Normal file
@@ -0,0 +1,229 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup edscr
|
||||
*
|
||||
* Query functions for area/region.
|
||||
*/
|
||||
|
||||
#include "BKE_screen.hh"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_math_base.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "ED_screen.hh"
|
||||
|
||||
#include "UI_interface.hh"
|
||||
#include "UI_view2d.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
bool ED_region_overlap_isect_x(const ARegion *region, const int event_x)
|
||||
{
|
||||
BLI_assert(region->overlap);
|
||||
/* No contents, skip it. */
|
||||
if (region->v2d.mask.xmin == region->v2d.mask.xmax) {
|
||||
return false;
|
||||
}
|
||||
if ((event_x < region->winrct.xmin) || (event_x > region->winrct.xmax)) {
|
||||
return false;
|
||||
}
|
||||
return BLI_rctf_isect_x(
|
||||
®ion->v2d.tot, ui::view2d_region_to_view_x(®ion->v2d, event_x - region->winrct.xmin));
|
||||
}
|
||||
|
||||
bool ED_region_overlap_isect_y(const ARegion *region, const int event_y)
|
||||
{
|
||||
BLI_assert(region->overlap);
|
||||
/* No contents, skip it. */
|
||||
if (region->v2d.mask.ymin == region->v2d.mask.ymax) {
|
||||
return false;
|
||||
}
|
||||
if ((event_y < region->winrct.ymin) || (event_y > region->winrct.ymax)) {
|
||||
return false;
|
||||
}
|
||||
return BLI_rctf_isect_y(
|
||||
®ion->v2d.tot, ui::view2d_region_to_view_y(®ion->v2d, event_y - region->winrct.ymin));
|
||||
}
|
||||
|
||||
bool ED_region_overlap_isect_xy(const ARegion *region, const int event_xy[2])
|
||||
{
|
||||
return (ED_region_overlap_isect_x(region, event_xy[0]) &&
|
||||
ED_region_overlap_isect_y(region, event_xy[1]));
|
||||
}
|
||||
|
||||
bool ED_region_overlap_isect_any_xy(const ScrArea *area, const int event_xy[2])
|
||||
{
|
||||
for (ARegion ®ion : area->regionbase) {
|
||||
if (!region.runtime->visible) {
|
||||
continue;
|
||||
}
|
||||
if (ED_region_is_overlap(area->spacetype, region.regiontype)) {
|
||||
if (ED_region_overlap_isect_xy(®ion, event_xy)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ED_region_panel_category_gutter_calc_rect(const ARegion *region, rcti *r_region_gutter)
|
||||
{
|
||||
*r_region_gutter = region->winrct;
|
||||
if (ui::panel_category_tabs_is_visible(region)) {
|
||||
const int category_tabs_width = round_fl_to_int(ui::view2d_scale_get_x(®ion->v2d) *
|
||||
UI_PANEL_CATEGORY_MARGIN_WIDTH);
|
||||
const int alignment = RGN_ALIGN_ENUM_FROM_MASK(region->alignment);
|
||||
|
||||
if (alignment == RGN_ALIGN_LEFT) {
|
||||
r_region_gutter->xmax = r_region_gutter->xmin + category_tabs_width;
|
||||
}
|
||||
else if (alignment == RGN_ALIGN_RIGHT) {
|
||||
r_region_gutter->xmin = r_region_gutter->xmax - category_tabs_width;
|
||||
}
|
||||
else {
|
||||
BLI_assert_msg(0, "Unsupported alignment");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ED_region_panel_category_gutter_isect_xy(const ARegion *region, const int event_xy[2])
|
||||
{
|
||||
rcti region_gutter;
|
||||
if (ED_region_panel_category_gutter_calc_rect(region, ®ion_gutter)) {
|
||||
return BLI_rcti_isect_pt_v(®ion_gutter, event_xy);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ED_region_overlap_isect_x_with_margin(const ARegion *region,
|
||||
const int event_x,
|
||||
const int margin)
|
||||
{
|
||||
BLI_assert(region->overlap);
|
||||
/* No contents, skip it. */
|
||||
if (region->v2d.mask.xmin == region->v2d.mask.xmax) {
|
||||
return false;
|
||||
}
|
||||
if ((event_x < region->winrct.xmin) || (event_x > region->winrct.xmax)) {
|
||||
return false;
|
||||
}
|
||||
const int region_x = event_x - region->winrct.xmin;
|
||||
return ((region->v2d.tot.xmin <= ui::view2d_region_to_view_x(®ion->v2d, region_x + margin)) &&
|
||||
(region->v2d.tot.xmax >= ui::view2d_region_to_view_x(®ion->v2d, region_x - margin)));
|
||||
}
|
||||
|
||||
bool ED_region_overlap_isect_y_with_margin(const ARegion *region,
|
||||
const int event_y,
|
||||
const int margin)
|
||||
{
|
||||
BLI_assert(region->overlap);
|
||||
/* No contents, skip it. */
|
||||
if (region->v2d.mask.ymin == region->v2d.mask.ymax) {
|
||||
return false;
|
||||
}
|
||||
if ((event_y < region->winrct.ymin) || (event_y > region->winrct.ymax)) {
|
||||
return false;
|
||||
}
|
||||
const int region_y = event_y - region->winrct.ymin;
|
||||
return (region->v2d.tot.ymin <= ui::view2d_region_to_view_y(®ion->v2d, region_y + margin)) &&
|
||||
(region->v2d.tot.ymax >= ui::view2d_region_to_view_y(®ion->v2d, region_y - margin));
|
||||
}
|
||||
|
||||
bool ED_region_overlap_isect_xy_with_margin(const ARegion *region,
|
||||
const int event_xy[2],
|
||||
const int margin)
|
||||
{
|
||||
return (ED_region_overlap_isect_x_with_margin(region, event_xy[0], margin) &&
|
||||
ED_region_overlap_isect_y_with_margin(region, event_xy[1], margin));
|
||||
}
|
||||
|
||||
bool ED_region_contains_xy(const ARegion *region, const int event_xy[2])
|
||||
{
|
||||
/* Only use the margin when inside the region. */
|
||||
if (BLI_rcti_isect_pt_v(®ion->winrct, event_xy)) {
|
||||
if (region->overlap) {
|
||||
const int overlap_margin = UI_REGION_OVERLAP_MARGIN;
|
||||
/* Note the View2D.tot isn't reliable for headers with spacers otherwise
|
||||
* we'd check #ED_region_overlap_isect_xy_with_margin for both bases. */
|
||||
if (region->v2d.keeptot == V2D_KEEPTOT_STRICT) {
|
||||
/* Header. */
|
||||
rcti rect;
|
||||
BLI_rcti_init_pt_radius(&rect, event_xy, overlap_margin);
|
||||
if (ui::region_but_find_rect_over(region, &rect) == nullptr) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Side-bar & any other kind of overlapping region. */
|
||||
|
||||
const int alignment = RGN_ALIGN_ENUM_FROM_MASK(region->alignment);
|
||||
|
||||
/* Check alignment to avoid region tabs being clipped out
|
||||
* by only clipping a single axis for aligned regions. */
|
||||
if (ELEM(alignment, RGN_ALIGN_TOP, RGN_ALIGN_BOTTOM)) {
|
||||
if (!ED_region_overlap_isect_x_with_margin(region, event_xy[0], overlap_margin)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (ELEM(alignment, RGN_ALIGN_LEFT, RGN_ALIGN_RIGHT)) {
|
||||
if (ED_region_panel_category_gutter_isect_xy(region, event_xy)) {
|
||||
/* pass */
|
||||
}
|
||||
else if (!ED_region_overlap_isect_y_with_margin(region, event_xy[1], overlap_margin)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* No panel categories for horizontal regions currently. */
|
||||
if (!ED_region_overlap_isect_xy_with_margin(region, event_xy, overlap_margin)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
ARegion *ED_area_find_region_xy_visual(const ScrArea *area,
|
||||
const int regiontype,
|
||||
const int event_xy[2])
|
||||
{
|
||||
if (!area) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Check overlapped regions first. */
|
||||
for (ARegion ®ion : area->regionbase) {
|
||||
if (!region.overlap) {
|
||||
continue;
|
||||
}
|
||||
if (ELEM(regiontype, RGN_TYPE_ANY, region.regiontype)) {
|
||||
if (ED_region_contains_xy(®ion, event_xy)) {
|
||||
return ®ion;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Now non-overlapping ones. */
|
||||
for (ARegion ®ion : area->regionbase) {
|
||||
if (region.overlap) {
|
||||
continue;
|
||||
}
|
||||
if (ELEM(regiontype, RGN_TYPE_ANY, region.regiontype)) {
|
||||
if (ED_region_contains_xy(®ion, event_xy)) {
|
||||
return ®ion;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
90
blender-5.2.0/source/blender/editors/screen/area_utils.cc
Normal file
90
blender-5.2.0/source/blender/editors/screen/area_utils.cc
Normal file
@@ -0,0 +1,90 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup edscr
|
||||
*
|
||||
* Helper functions for area/region API.
|
||||
*/
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include "BKE_screen.hh"
|
||||
|
||||
#include "BLI_rect.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "WM_message.hh"
|
||||
|
||||
#include "ED_screen.hh"
|
||||
|
||||
#include "UI_interface.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Generic Tool System Region Callbacks
|
||||
* \{ */
|
||||
|
||||
void ED_region_generic_tools_region_message_subscribe(const wmRegionMessageSubscribeParams *params)
|
||||
{
|
||||
wmMsgBus *mbus = params->message_bus;
|
||||
ARegion *region = params->region;
|
||||
|
||||
wmMsgSubscribeValue msg_sub_value_region_tag_redraw{};
|
||||
msg_sub_value_region_tag_redraw.owner = region;
|
||||
msg_sub_value_region_tag_redraw.user_data = region;
|
||||
msg_sub_value_region_tag_redraw.notify = ED_region_do_msg_notify_tag_redraw;
|
||||
WM_msg_subscribe_rna_anon_prop(mbus, WorkSpace, tools, &msg_sub_value_region_tag_redraw);
|
||||
}
|
||||
|
||||
int ED_region_generic_tools_region_snap_size(const ARegion *region, int size, int axis)
|
||||
{
|
||||
if (axis == 0) {
|
||||
/* Using Y axis avoids slight feedback loop when adjusting X. */
|
||||
const float aspect = BLI_rctf_size_y(®ion->v2d.cur) /
|
||||
(BLI_rcti_size_y(®ion->v2d.mask) + 1);
|
||||
const float column = UI_TOOLBAR_COLUMN / aspect;
|
||||
const float margin = UI_TOOLBAR_MARGIN / aspect;
|
||||
const float snap_units[] = {
|
||||
column + margin,
|
||||
(2.0f * column) + margin,
|
||||
(2.7f * column) + margin,
|
||||
};
|
||||
int best_diff = std::numeric_limits<int>::max();
|
||||
int best_size = size;
|
||||
/* Only snap if less than last snap unit. */
|
||||
if (size <= snap_units[ARRAY_SIZE(snap_units) - 1]) {
|
||||
for (uint i = 0; i < ARRAY_SIZE(snap_units); i += 1) {
|
||||
const int test_size = snap_units[i];
|
||||
const int test_diff = abs(test_size - size);
|
||||
if (test_diff < best_diff) {
|
||||
best_size = test_size;
|
||||
best_diff = test_diff;
|
||||
}
|
||||
}
|
||||
}
|
||||
return best_size;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
int ED_region_generic_panel_region_snap_size(const ARegion *region, int size, int axis)
|
||||
{
|
||||
if (axis == 0) {
|
||||
if (!ui::panel_category_tabs_is_visible(region)) {
|
||||
return size;
|
||||
}
|
||||
|
||||
/* Using Y axis avoids slight feedback loop when adjusting X. */
|
||||
const float aspect = BLI_rctf_size_y(®ion->v2d.cur) /
|
||||
(BLI_rcti_size_y(®ion->v2d.mask) + 1);
|
||||
return int(UI_PANEL_CATEGORY_MIN_WIDTH / aspect);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
228
blender-5.2.0/source/blender/editors/screen/glutil.cc
Normal file
228
blender-5.2.0/source/blender/editors/screen/glutil.cc
Normal file
@@ -0,0 +1,228 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup edscr
|
||||
*/
|
||||
|
||||
#include "DNA_userdef_types.h"
|
||||
#include "DNA_vec_types.h"
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BIF_glutil.hh"
|
||||
|
||||
#include "IMB_colormanagement.hh"
|
||||
#include "IMB_imbuf_types.hh"
|
||||
|
||||
#include "GPU_immediate.hh"
|
||||
#include "GPU_texture.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
void PixelBitmapDrawer::init_vertex_attributes()
|
||||
{
|
||||
GPUVertFormat *vert_format = immVertexFormat();
|
||||
this->pos = GPU_vertformat_attr_add(vert_format, "pos", gpu::VertAttrType::SFLOAT_32_32);
|
||||
this->texco = GPU_vertformat_attr_add(vert_format, "texCoord", gpu::VertAttrType::SFLOAT_32_32);
|
||||
}
|
||||
|
||||
PixelBitmapDrawer::PixelBitmapDrawer() : shader(nullptr)
|
||||
{
|
||||
init_vertex_attributes();
|
||||
}
|
||||
|
||||
PixelBitmapDrawer::PixelBitmapDrawer(GPUBuiltinShader builtin_shader)
|
||||
{
|
||||
init_vertex_attributes();
|
||||
|
||||
this->shader = GPU_shader_get_builtin_shader(builtin_shader);
|
||||
/* Shader will be unbound in draw(). */
|
||||
immBindBuiltinProgram(builtin_shader);
|
||||
}
|
||||
|
||||
void PixelBitmapDrawer::draw(const float x,
|
||||
const float y,
|
||||
const int img_w,
|
||||
const int img_h,
|
||||
const gpu::TextureFormat gpu_format,
|
||||
const bool use_filter,
|
||||
const void *rect,
|
||||
const float scale_x,
|
||||
const float scale_y,
|
||||
const float color[4])
|
||||
{
|
||||
const float draw_width = img_w * scale_x;
|
||||
const float draw_height = img_h * scale_y;
|
||||
|
||||
/* When scaling down by more than 2x, create mipmaps for the texture and
|
||||
* use trilinear filtering. */
|
||||
const bool use_mipmap = use_filter && (scale_x < 0.5f || scale_y < 0.5f);
|
||||
const int mip_len = use_mipmap ? 9999 : 1;
|
||||
|
||||
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ;
|
||||
if (use_mipmap) {
|
||||
usage |= GPU_TEXTURE_USAGE_SHADER_WRITE;
|
||||
}
|
||||
gpu::Texture *tex = GPU_texture_create_2d(
|
||||
"immDrawPixels", img_w, img_h, mip_len, gpu_format, usage, nullptr);
|
||||
if (tex == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bool use_float_data = ELEM(gpu_format,
|
||||
gpu::TextureFormat::SFLOAT_16_16_16_16,
|
||||
gpu::TextureFormat::SFLOAT_16_16_16,
|
||||
gpu::TextureFormat::SFLOAT_16);
|
||||
eGPUDataFormat gpu_data_format = use_float_data ? GPU_DATA_FLOAT : GPU_DATA_UBYTE;
|
||||
GPU_texture_update(tex, gpu_data_format, rect);
|
||||
|
||||
GPU_texture_filter_mode(tex, use_filter);
|
||||
if (use_mipmap) {
|
||||
GPU_texture_update_mipmap_chain(tex);
|
||||
GPU_texture_mipmap_mode(tex, true, true);
|
||||
}
|
||||
GPU_texture_extend_mode(tex, GPU_SAMPLER_EXTEND_MODE_EXTEND);
|
||||
|
||||
GPU_texture_bind(tex, 0);
|
||||
|
||||
/* NOTE: Shader could be null for GLSL OCIO drawing, it is fine, since
|
||||
* it does not need color. */
|
||||
static const float white[4] = {1.0f, 1.0f, 1.0f, 1.0f};
|
||||
if (this->shader != nullptr && GPU_shader_get_uniform(this->shader, "color") != -1) {
|
||||
immUniformColor4fv((color) ? color : white);
|
||||
}
|
||||
|
||||
const uint pos = this->pos, texco = this->texco;
|
||||
|
||||
immBegin(GPU_PRIM_TRI_FAN, 4);
|
||||
immAttr2f(texco, 0.0f, 0.0f);
|
||||
immVertex2f(pos, x, y);
|
||||
|
||||
immAttr2f(texco, 1.0f, 0.0f);
|
||||
immVertex2f(pos, x + draw_width, y);
|
||||
|
||||
immAttr2f(texco, 1.0f, 1.0f);
|
||||
immVertex2f(pos, x + draw_width, y + draw_height);
|
||||
|
||||
immAttr2f(texco, 0.0f, 1.0f);
|
||||
immVertex2f(pos, x, y + draw_height);
|
||||
immEnd();
|
||||
|
||||
if (this->shader) {
|
||||
immUnbindProgram();
|
||||
}
|
||||
|
||||
GPU_texture_unbind(tex);
|
||||
GPU_texture_free(tex);
|
||||
}
|
||||
|
||||
void ED_draw_imbuf(const ImBuf *ibuf,
|
||||
float x,
|
||||
float y,
|
||||
bool use_filter,
|
||||
const ColorManagedViewSettings *view_settings,
|
||||
const ColorManagedDisplaySettings *display_settings,
|
||||
float zoom_x,
|
||||
float zoom_y)
|
||||
{
|
||||
using namespace blender::gpu;
|
||||
|
||||
/* Early out */
|
||||
if (ibuf->byte_data() == nullptr && ibuf->float_data() == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
PixelBitmapDrawer drawer;
|
||||
|
||||
const ColorSpace *colorspace = ibuf->float_data() ? ibuf->float_buffer.colorspace :
|
||||
ibuf->byte_buffer.colorspace;
|
||||
const bool predivide = ibuf->float_data() != nullptr;
|
||||
if (!IMB_colormanagement_setup_glsl_draw_from_space(
|
||||
view_settings, display_settings, colorspace, ibuf->dither, predivide, false))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const void *texture_data = nullptr;
|
||||
TextureFormat format = TextureFormat::Invalid;
|
||||
if (ibuf->float_data()) {
|
||||
texture_data = ibuf->float_data();
|
||||
if (ibuf->channels == 1) {
|
||||
format = TextureFormat::SFLOAT_16;
|
||||
}
|
||||
else if (ibuf->channels == 3) {
|
||||
format = TextureFormat::SFLOAT_16_16_16;
|
||||
}
|
||||
else if (ibuf->channels == 4) {
|
||||
format = TextureFormat::SFLOAT_16_16_16_16;
|
||||
}
|
||||
else {
|
||||
BLI_assert_msg(0, "Incompatible number of channels for GLSL display");
|
||||
}
|
||||
}
|
||||
else {
|
||||
texture_data = ibuf->byte_data();
|
||||
format = TextureFormat::UNORM_8_8_8_8;
|
||||
}
|
||||
|
||||
if (format != TextureFormat::Invalid) {
|
||||
drawer.draw(x, y, ibuf->x, ibuf->y, format, use_filter, texture_data, zoom_x, zoom_y, nullptr);
|
||||
}
|
||||
|
||||
IMB_colormanagement_finish_glsl_draw();
|
||||
}
|
||||
|
||||
void ED_draw_imbuf_ctx(const bContext *C,
|
||||
const ImBuf *ibuf,
|
||||
float x,
|
||||
float y,
|
||||
bool use_filter,
|
||||
float zoom_x,
|
||||
float zoom_y)
|
||||
{
|
||||
ColorManagedViewSettings *view_settings;
|
||||
ColorManagedDisplaySettings *display_settings;
|
||||
IMB_colormanagement_display_settings_from_ctx(C, &view_settings, &display_settings);
|
||||
ED_draw_imbuf(ibuf, x, y, use_filter, view_settings, display_settings, zoom_x, zoom_y);
|
||||
}
|
||||
|
||||
void immDrawBorderCorners(uint pos, const rcti *border, float zoomx, float zoomy)
|
||||
{
|
||||
float delta_x = 4.0f * UI_SCALE_FAC / zoomx;
|
||||
float delta_y = 4.0f * UI_SCALE_FAC / zoomy;
|
||||
|
||||
delta_x = min_ff(delta_x, border->xmax - border->xmin);
|
||||
delta_y = min_ff(delta_y, border->ymax - border->ymin);
|
||||
|
||||
/* left bottom corner */
|
||||
immBegin(GPU_PRIM_LINE_STRIP, 3);
|
||||
immVertex2f(pos, border->xmin, border->ymin + delta_y);
|
||||
immVertex2f(pos, border->xmin, border->ymin);
|
||||
immVertex2f(pos, border->xmin + delta_x, border->ymin);
|
||||
immEnd();
|
||||
|
||||
/* left top corner */
|
||||
immBegin(GPU_PRIM_LINE_STRIP, 3);
|
||||
immVertex2f(pos, border->xmin, border->ymax - delta_y);
|
||||
immVertex2f(pos, border->xmin, border->ymax);
|
||||
immVertex2f(pos, border->xmin + delta_x, border->ymax);
|
||||
immEnd();
|
||||
|
||||
/* right bottom corner */
|
||||
immBegin(GPU_PRIM_LINE_STRIP, 3);
|
||||
immVertex2f(pos, border->xmax - delta_x, border->ymin);
|
||||
immVertex2f(pos, border->xmax, border->ymin);
|
||||
immVertex2f(pos, border->xmax, border->ymin + delta_y);
|
||||
immEnd();
|
||||
|
||||
/* right top corner */
|
||||
immBegin(GPU_PRIM_LINE_STRIP, 3);
|
||||
immVertex2f(pos, border->xmax - delta_x, border->ymax);
|
||||
immVertex2f(pos, border->xmax, border->ymax);
|
||||
immVertex2f(pos, border->xmax, border->ymax - delta_y);
|
||||
immEnd();
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
1274
blender-5.2.0/source/blender/editors/screen/screen_context.cc
Normal file
1274
blender-5.2.0/source/blender/editors/screen/screen_context.cc
Normal file
File diff suppressed because it is too large
Load Diff
753
blender-5.2.0/source/blender/editors/screen/screen_draw.cc
Normal file
753
blender-5.2.0/source/blender/editors/screen/screen_draw.cc
Normal file
@@ -0,0 +1,753 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup edscr
|
||||
*/
|
||||
|
||||
#include "ED_screen.hh"
|
||||
#include "ED_screen_types.hh"
|
||||
|
||||
#include "GPU_batch_presets.hh"
|
||||
#include "GPU_immediate.hh"
|
||||
#include "GPU_platform.hh"
|
||||
#include "GPU_state.hh"
|
||||
|
||||
#include "BKE_global.hh"
|
||||
#include "BKE_screen.hh"
|
||||
|
||||
#include "BLF_api.hh"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_math_color.h"
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_rect.h"
|
||||
#include "BLI_time.h"
|
||||
|
||||
#include "BLT_translation.hh"
|
||||
|
||||
#include "WM_api.hh"
|
||||
|
||||
#include "UI_interface.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
#include "screen_intern.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
#define CORNER_RESOLUTION 3
|
||||
|
||||
static void do_vert_pair(gpu::VertBuf *vbo, uint pos, uint *vidx, int corner, int i)
|
||||
{
|
||||
float inter[2];
|
||||
inter[0] = cosf(corner * M_PI_2 + (i * M_PI_2 / (CORNER_RESOLUTION - 1.0f)));
|
||||
inter[1] = sinf(corner * M_PI_2 + (i * M_PI_2 / (CORNER_RESOLUTION - 1.0f)));
|
||||
|
||||
/* Snap point to edge */
|
||||
float div = 1.0f / max_ff(fabsf(inter[0]), fabsf(inter[1]));
|
||||
float exter[2];
|
||||
mul_v2_v2fl(exter, inter, div);
|
||||
exter[0] = roundf(exter[0]);
|
||||
exter[1] = roundf(exter[1]);
|
||||
|
||||
if (i == 0 || i == (CORNER_RESOLUTION - 1)) {
|
||||
copy_v2_v2(inter, exter);
|
||||
}
|
||||
|
||||
/* Small offset to be able to tell inner and outer vertex apart inside the shader.
|
||||
* Edge width is specified in the shader. */
|
||||
mul_v2_fl(inter, 1.0f - 0.0001f);
|
||||
mul_v2_fl(exter, 1.0f);
|
||||
|
||||
GPU_vertbuf_attr_set(vbo, pos, (*vidx)++, inter);
|
||||
GPU_vertbuf_attr_set(vbo, pos, (*vidx)++, exter);
|
||||
}
|
||||
|
||||
static gpu::Batch *batch_screen_edges_get(int *corner_len)
|
||||
{
|
||||
static gpu::Batch *screen_edges_batch = nullptr;
|
||||
|
||||
if (screen_edges_batch == nullptr) {
|
||||
GPUVertFormat format = {0};
|
||||
uint pos = GPU_vertformat_attr_add(&format, "pos", gpu::VertAttrType::SFLOAT_32_32);
|
||||
|
||||
gpu::VertBuf *vbo = GPU_vertbuf_create_with_format(format);
|
||||
GPU_vertbuf_data_alloc(*vbo, CORNER_RESOLUTION * 2 * 4 + 2);
|
||||
|
||||
uint vidx = 0;
|
||||
for (int corner = 0; corner < 4; corner++) {
|
||||
for (int c = 0; c < CORNER_RESOLUTION; c++) {
|
||||
do_vert_pair(vbo, pos, &vidx, corner, c);
|
||||
}
|
||||
}
|
||||
/* close the loop */
|
||||
do_vert_pair(vbo, pos, &vidx, 0, 0);
|
||||
|
||||
screen_edges_batch = GPU_batch_create_ex(GPU_PRIM_TRI_STRIP, vbo, nullptr, GPU_BATCH_OWNS_VBO);
|
||||
gpu_batch_presets_register(screen_edges_batch);
|
||||
}
|
||||
|
||||
if (corner_len) {
|
||||
*corner_len = CORNER_RESOLUTION * 2;
|
||||
}
|
||||
return screen_edges_batch;
|
||||
}
|
||||
|
||||
#undef CORNER_RESOLUTION
|
||||
|
||||
/**
|
||||
* \brief Screen edges drawing.
|
||||
*/
|
||||
static void drawscredge_area(const ScrArea &area, float edge_thickness)
|
||||
{
|
||||
rctf rect;
|
||||
BLI_rctf_rcti_copy(&rect, &area.totrct);
|
||||
BLI_rctf_pad(&rect, edge_thickness, edge_thickness);
|
||||
|
||||
gpu::Batch *batch = batch_screen_edges_get(nullptr);
|
||||
GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_AREA_BORDERS);
|
||||
GPU_batch_uniform_4fv(batch, "rect", (float *)&rect);
|
||||
GPU_batch_draw(batch);
|
||||
}
|
||||
|
||||
void ED_screen_draw_edges(wmWindow *win)
|
||||
{
|
||||
bScreen *screen = WM_window_get_active_screen(win);
|
||||
screen->do_draw = false;
|
||||
|
||||
if (screen->state != SCREENNORMAL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (screen->areabase.is_single() && win->global_areas.areabase.first == nullptr) {
|
||||
/* Do not show edges on windows without global areas and with only one editor. */
|
||||
return;
|
||||
}
|
||||
|
||||
ARegion *region = screen->active_region;
|
||||
ScrArea *active_area = nullptr;
|
||||
|
||||
if (region) {
|
||||
/* Find active area from active region. */
|
||||
const int pos[2] = {BLI_rcti_cent_x(®ion->winrct), BLI_rcti_cent_y(®ion->winrct)};
|
||||
active_area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, pos);
|
||||
}
|
||||
|
||||
if (!active_area) {
|
||||
for (ScrArea &area : screen->areabase) {
|
||||
AZone *zone = ED_area_actionzone_find_xy(&area, win->runtime->eventstate->xy);
|
||||
/* Get area from action zone, if not scroll-bar. */
|
||||
if (zone && zone->type != AZONE_REGION_SCROLL) {
|
||||
active_area = &area;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (G.moving & G_TRANSFORM_WM) {
|
||||
active_area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, win->runtime->eventstate->xy);
|
||||
/* We don't want an active area when resizing, otherwise outline for active area flickers, see:
|
||||
* #136314. */
|
||||
if (active_area && !win->runtime->drawcalls.is_empty()) {
|
||||
active_area = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
rcti scissor_rect;
|
||||
BLI_rcti_init_minmax(&scissor_rect);
|
||||
for (ScrArea &area : screen->areabase) {
|
||||
BLI_rcti_do_minmax_v(&scissor_rect, int2{area.v1->vec.x, area.v1->vec.y});
|
||||
BLI_rcti_do_minmax_v(&scissor_rect, int2{area.v3->vec.x, area.v3->vec.y});
|
||||
}
|
||||
|
||||
if (GPU_type_matches_ex(GPU_DEVICE_INTEL_UHD, GPU_OS_UNIX, GPU_DRIVER_ANY, GPU_BACKEND_OPENGL)) {
|
||||
/* For some reason, on linux + Intel UHD Graphics 620 the driver
|
||||
* hangs if we don't flush before this. (See #57455) */
|
||||
GPU_flush();
|
||||
}
|
||||
|
||||
GPU_scissor(scissor_rect.xmin,
|
||||
scissor_rect.ymin,
|
||||
BLI_rcti_size_x(&scissor_rect) + 1,
|
||||
BLI_rcti_size_y(&scissor_rect) + 1);
|
||||
GPU_scissor_test(true);
|
||||
|
||||
float col[4];
|
||||
ui::theme::get_color_4fv(TH_EDITOR_BORDER, col);
|
||||
|
||||
const float edge_thickness = float(U.border_width) * UI_SCALE_FAC;
|
||||
|
||||
/* Entire width of the evaluated outline as far as the shader is concerned. */
|
||||
const float shader_scale = edge_thickness + EDITORRADIUS;
|
||||
const float corner_coverage[10] = {
|
||||
0.144f, 0.25f, 0.334f, 0.40f, 0.455, 0.5, 0.538, 0.571, 0.6, 0.625f};
|
||||
const float shader_width = corner_coverage[U.border_width - 1];
|
||||
|
||||
GPU_blend(GPU_BLEND_ALPHA);
|
||||
|
||||
int verts_per_corner = 0;
|
||||
gpu::Batch *batch = batch_screen_edges_get(&verts_per_corner);
|
||||
|
||||
GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_AREA_BORDERS);
|
||||
GPU_batch_uniform_1i(batch, "cornerLen", verts_per_corner);
|
||||
GPU_batch_uniform_1f(batch, "scale", shader_scale);
|
||||
GPU_batch_uniform_1f(batch, "width", shader_width);
|
||||
GPU_batch_uniform_4fv(batch, "color", col);
|
||||
|
||||
for (ScrArea &area : screen->areabase) {
|
||||
drawscredge_area(area, edge_thickness);
|
||||
}
|
||||
|
||||
float outline1[4];
|
||||
float outline2[4];
|
||||
rctf bounds;
|
||||
/* Outset by 1/2 pixel, regardless of UI scale or pixel size. #141550. */
|
||||
const float padding = 0.5f;
|
||||
ui::theme::get_color_4fv(TH_EDITOR_OUTLINE, outline1);
|
||||
ui::theme::get_color_4fv(TH_EDITOR_OUTLINE_ACTIVE, outline2);
|
||||
ui::draw_roundbox_corner_set(ui::CNR_ALL);
|
||||
for (ScrArea &area : screen->areabase) {
|
||||
BLI_rctf_rcti_copy(&bounds, &area.totrct);
|
||||
BLI_rctf_pad(&bounds, padding, padding);
|
||||
ui::draw_roundbox_4fv_ex(&bounds,
|
||||
nullptr,
|
||||
nullptr,
|
||||
1.0f,
|
||||
(&area == active_area) ? outline2 : outline1,
|
||||
U.pixelsize,
|
||||
EDITORRADIUS);
|
||||
}
|
||||
|
||||
GPU_blend(GPU_BLEND_NONE);
|
||||
GPU_scissor_test(false);
|
||||
}
|
||||
|
||||
void screen_draw_move_highlight(const wmWindow *win,
|
||||
bScreen *screen,
|
||||
eScreenAxis dir_axis,
|
||||
float anim_factor)
|
||||
{
|
||||
rctf rect = {SHRT_MAX, SHRT_MIN, SHRT_MAX, SHRT_MIN};
|
||||
|
||||
for (const ScrEdge &edge : screen->edgebase) {
|
||||
if (edge.v1->editflag && edge.v2->editflag) {
|
||||
if (dir_axis == SCREEN_AXIS_H) {
|
||||
rect.xmin = std::min({rect.xmin, float(edge.v1->vec.x), float(edge.v2->vec.x)});
|
||||
rect.xmax = std::max({rect.xmax, float(edge.v1->vec.x), float(edge.v2->vec.x)});
|
||||
rect.ymin = rect.ymax = float(edge.v1->vec.y);
|
||||
}
|
||||
else {
|
||||
rect.ymin = std::min({rect.ymin, float(edge.v1->vec.y), float(edge.v2->vec.y)});
|
||||
rect.ymax = std::max({rect.ymax, float(edge.v1->vec.y), float(edge.v2->vec.y)});
|
||||
rect.xmin = rect.xmax = float(edge.v1->vec.x);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
rcti window_rect;
|
||||
WM_window_screen_rect_calc(win, &window_rect);
|
||||
const float offset = U.border_width * UI_SCALE_FAC;
|
||||
const float width = std::min(2.0f * offset, 5.0f * UI_SCALE_FAC);
|
||||
if (dir_axis == SCREEN_AXIS_H) {
|
||||
BLI_rctf_pad(&rect, -offset, width);
|
||||
}
|
||||
else {
|
||||
BLI_rctf_pad(&rect, width, -offset);
|
||||
}
|
||||
|
||||
float inner[4] = {1.0f, 1.0f, 1.0f, 0.4f * anim_factor};
|
||||
float outline[4];
|
||||
ui::theme::get_color_4fv(TH_EDITOR_BORDER, outline);
|
||||
outline[3] *= anim_factor;
|
||||
|
||||
ui::draw_roundbox_corner_set(ui::CNR_ALL);
|
||||
ui::draw_roundbox_4fv_ex(
|
||||
&rect, inner, nullptr, 1.0f, outline, width - U.pixelsize, 2.5f * UI_SCALE_FAC);
|
||||
}
|
||||
|
||||
void screen_draw_region_scale_highlight(ARegion *region)
|
||||
{
|
||||
rctf rect;
|
||||
BLI_rctf_rcti_copy(&rect, ®ion->winrct);
|
||||
ui::draw_roundbox_corner_set(ui::CNR_ALL);
|
||||
|
||||
switch (region->alignment) {
|
||||
case RGN_ALIGN_RIGHT:
|
||||
rect.xmax = rect.xmin - U.pixelsize;
|
||||
rect.xmin = rect.xmax - (4.0f * U.pixelsize);
|
||||
rect.ymax -= EDITORRADIUS;
|
||||
rect.ymin += EDITORRADIUS;
|
||||
break;
|
||||
case RGN_ALIGN_LEFT:
|
||||
rect.xmin = rect.xmax + U.pixelsize;
|
||||
rect.xmax = rect.xmin + (4.0f * U.pixelsize);
|
||||
rect.ymax -= EDITORRADIUS;
|
||||
rect.ymin += EDITORRADIUS;
|
||||
break;
|
||||
case RGN_ALIGN_TOP:
|
||||
rect.ymax = rect.ymin - U.pixelsize;
|
||||
rect.ymin = rect.ymax - (4.0f * U.pixelsize);
|
||||
rect.xmax -= EDITORRADIUS;
|
||||
rect.xmin += EDITORRADIUS;
|
||||
break;
|
||||
case RGN_ALIGN_BOTTOM:
|
||||
rect.ymin = rect.ymax + U.pixelsize;
|
||||
rect.ymax = rect.ymin + (4.0f * U.pixelsize);
|
||||
rect.xmax -= EDITORRADIUS;
|
||||
rect.xmin += EDITORRADIUS;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
float inner[4] = {1.0f, 1.0f, 1.0f, 0.4f};
|
||||
float outline[4] = {0.0f, 0.0f, 0.0f, 0.3f};
|
||||
ui::draw_roundbox_4fv_ex(
|
||||
&rect, inner, nullptr, 1.0f, outline, 1.0f * U.pixelsize, 2.5f * UI_SCALE_FAC);
|
||||
}
|
||||
|
||||
static void screen_draw_area_drag_tip(
|
||||
const wmWindow *win, int x, int y, const ScrArea *source, const std::string &hint)
|
||||
{
|
||||
const char *area_name = IFACE_(ED_area_name(source).c_str());
|
||||
const uiFontStyle *fstyle = UI_FSTYLE_TOOLTIP;
|
||||
const bTheme *btheme = ui::theme::theme_get();
|
||||
const uiWidgetColors *wcol = &btheme->tui.wcol_tooltip;
|
||||
float col_fg[4], col_bg[4];
|
||||
rgba_uchar_to_float(col_fg, wcol->text);
|
||||
rgba_uchar_to_float(col_bg, wcol->inner);
|
||||
|
||||
float scale = fstyle->points * UI_SCALE_FAC / UI_DEFAULT_TOOLTIP_POINTS;
|
||||
BLF_size(fstyle->uifont_id, UI_DEFAULT_TOOLTIP_POINTS * scale);
|
||||
|
||||
const float margin = scale * 4.0f;
|
||||
const float icon_width = (scale * ICON_DEFAULT_WIDTH / 1.4f);
|
||||
const float icon_gap = scale * 3.0f;
|
||||
const float line_gap = scale * 5.0f;
|
||||
const int lheight = BLF_height_max(fstyle->uifont_id);
|
||||
const int descent = BLF_descender(fstyle->uifont_id);
|
||||
const float line1_len = BLF_width(fstyle->uifont_id, hint.c_str(), hint.size());
|
||||
const float line2_len = BLF_width(fstyle->uifont_id, area_name, BLF_DRAW_STR_DUMMY_MAX);
|
||||
const float width = margin + std::max(line1_len, line2_len + icon_width + icon_gap) + margin;
|
||||
const float height = margin + lheight + line_gap + lheight + margin;
|
||||
|
||||
/* Position of this hint relative to the mouse position. */
|
||||
const int left = std::min(x + int(5.0f * UI_SCALE_FAC),
|
||||
WM_window_native_pixel_x(win) - int(width));
|
||||
const int top = std::max(y - int(7.0f * UI_SCALE_FAC), int(height));
|
||||
|
||||
rctf rect;
|
||||
rect.xmin = left;
|
||||
rect.xmax = left + width;
|
||||
rect.ymax = top;
|
||||
rect.ymin = top - height;
|
||||
ui::draw_roundbox_corner_set(ui::CNR_ALL);
|
||||
ui::draw_roundbox_4fv(&rect, true, wcol->roundness * U.widget_unit, col_bg);
|
||||
|
||||
ui::icon_draw_ex(left + margin,
|
||||
top - height + margin + (1.0f * scale),
|
||||
ED_area_icon(source),
|
||||
1.4f / scale,
|
||||
1.0f,
|
||||
0.0f,
|
||||
wcol->text,
|
||||
true,
|
||||
UI_NO_ICON_OVERLAY_TEXT);
|
||||
|
||||
BLF_size(fstyle->uifont_id, UI_DEFAULT_TOOLTIP_POINTS * scale);
|
||||
BLF_color4fv(fstyle->uifont_id, col_fg);
|
||||
|
||||
BLF_position(fstyle->uifont_id, left + margin, top - margin - lheight + (2.0f * scale), 0.0f);
|
||||
BLF_draw(fstyle->uifont_id, hint.c_str(), hint.size());
|
||||
|
||||
BLF_position(fstyle->uifont_id,
|
||||
left + margin + icon_width + icon_gap,
|
||||
top - height + margin - descent,
|
||||
0.0f);
|
||||
BLF_draw(fstyle->uifont_id, area_name, BLF_DRAW_STR_DUMMY_MAX);
|
||||
}
|
||||
|
||||
static void screen_draw_area_closed(int xmin, int xmax, int ymin, int ymax, float anim_factor)
|
||||
{
|
||||
/* Darken the area. */
|
||||
rctf rect = {float(xmin), float(xmax), float(ymin), float(ymax)};
|
||||
float darken[4] = {0.0f, 0.0f, 0.0f, 0.7f * anim_factor};
|
||||
ui::draw_roundbox_corner_set(ui::CNR_ALL);
|
||||
ui::draw_roundbox_4fv_ex(&rect, darken, nullptr, 1.0f, nullptr, U.pixelsize, EDITORRADIUS);
|
||||
}
|
||||
|
||||
void screen_draw_join_highlight(
|
||||
const wmWindow *win, ScrArea *sa1, ScrArea *sa2, eScreenDir dir, float anim_factor)
|
||||
{
|
||||
if (dir == SCREEN_DIR_NONE || !sa2) {
|
||||
/* Darken source if docking. Done here because it might be a different window.
|
||||
* Do not animate this as we don't want to reset every time we change areas. */
|
||||
screen_draw_area_closed(
|
||||
sa1->totrct.xmin, sa1->totrct.xmax, sa1->totrct.ymin, sa1->totrct.ymax, 1.0f);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Rect of the combined areas. */
|
||||
const bool vertical = SCREEN_DIR_IS_VERTICAL(dir);
|
||||
rctf combined{};
|
||||
combined.xmin = vertical ? std::max(sa1->totrct.xmin, sa2->totrct.xmin) :
|
||||
std::min(sa1->totrct.xmin, sa2->totrct.xmin);
|
||||
combined.xmax = vertical ? std::min(sa1->totrct.xmax, sa2->totrct.xmax) :
|
||||
std::max(sa1->totrct.xmax, sa2->totrct.xmax);
|
||||
combined.ymin = vertical ? std::min(sa1->totrct.ymin, sa2->totrct.ymin) :
|
||||
std::max(sa1->totrct.ymin, sa2->totrct.ymin);
|
||||
combined.ymax = vertical ? std::max(sa1->totrct.ymax, sa2->totrct.ymax) :
|
||||
std::min(sa1->totrct.ymax, sa2->totrct.ymax);
|
||||
|
||||
int offset1;
|
||||
int offset2;
|
||||
area_getoffsets(sa1, sa2, dir, &offset1, &offset2);
|
||||
if (offset1 < 0 || offset2 > 0) {
|
||||
/* Show partial areas that will be closed. */
|
||||
if (vertical) {
|
||||
if (sa1->totrct.xmin < combined.xmin) {
|
||||
screen_draw_area_closed(
|
||||
sa1->totrct.xmin, combined.xmin, sa1->totrct.ymin, sa1->totrct.ymax, anim_factor);
|
||||
}
|
||||
if (sa2->totrct.xmin < combined.xmin) {
|
||||
screen_draw_area_closed(
|
||||
sa2->totrct.xmin, combined.xmin, sa2->totrct.ymin, sa2->totrct.ymax, anim_factor);
|
||||
}
|
||||
if (sa1->totrct.xmax > combined.xmax) {
|
||||
screen_draw_area_closed(
|
||||
combined.xmax, sa1->totrct.xmax, sa1->totrct.ymin, sa1->totrct.ymax, anim_factor);
|
||||
}
|
||||
if (sa2->totrct.xmax > combined.xmax) {
|
||||
screen_draw_area_closed(
|
||||
combined.xmax, sa2->totrct.xmax, sa2->totrct.ymin, sa2->totrct.ymax, anim_factor);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (sa1->totrct.ymin < combined.ymin) {
|
||||
screen_draw_area_closed(
|
||||
sa1->totrct.xmin, sa1->totrct.xmax, sa1->totrct.ymin, combined.ymin, anim_factor);
|
||||
}
|
||||
if (sa2->totrct.ymin < combined.ymin) {
|
||||
screen_draw_area_closed(
|
||||
sa2->totrct.xmin, sa2->totrct.xmax, sa2->totrct.ymin, combined.ymin, anim_factor);
|
||||
}
|
||||
if (sa1->totrct.ymax > combined.ymax) {
|
||||
screen_draw_area_closed(
|
||||
sa1->totrct.xmin, sa1->totrct.xmax, combined.ymax, sa1->totrct.ymax, anim_factor);
|
||||
}
|
||||
if (sa2->totrct.ymax > combined.ymax) {
|
||||
screen_draw_area_closed(
|
||||
sa2->totrct.xmin, sa2->totrct.xmax, combined.ymax, sa2->totrct.ymax, anim_factor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Outline the combined area. */
|
||||
draw_roundbox_corner_set(ui::CNR_ALL);
|
||||
float outline[4] = {1.0f, 1.0f, 1.0f, 0.4f * anim_factor};
|
||||
float inner[4] = {1.0f, 1.0f, 1.0f, 0.10f * anim_factor};
|
||||
ui::draw_roundbox_4fv_ex(&combined, inner, nullptr, 1.0f, outline, U.pixelsize, EDITORRADIUS);
|
||||
|
||||
screen_draw_area_drag_tip(win,
|
||||
win->runtime->eventstate->xy[0],
|
||||
win->runtime->eventstate->xy[1],
|
||||
sa1,
|
||||
IFACE_("Join Areas"));
|
||||
}
|
||||
|
||||
static void rounded_corners(rctf rect, float color[4], int corners)
|
||||
{
|
||||
GPUVertFormat *format = immVertexFormat();
|
||||
const uint pos = GPU_vertformat_attr_add(format, "pos", gpu::VertAttrType::SFLOAT_32_32);
|
||||
|
||||
const float rad = EDITORRADIUS;
|
||||
|
||||
float vec[4][2] = {
|
||||
{0.195, 0.02},
|
||||
{0.55, 0.169},
|
||||
{0.831, 0.45},
|
||||
{0.98, 0.805},
|
||||
};
|
||||
for (int a = 0; a < 4; a++) {
|
||||
mul_v2_fl(vec[a], rad);
|
||||
}
|
||||
|
||||
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
|
||||
immUniformColor4fv(color);
|
||||
|
||||
if (corners & ui::CNR_TOP_LEFT) {
|
||||
immBegin(GPU_PRIM_TRI_FAN, 7);
|
||||
immVertex2f(pos, rect.xmin - 1, rect.ymax);
|
||||
immVertex2f(pos, rect.xmin, rect.ymax - rad);
|
||||
for (int a = 0; a < 4; a++) {
|
||||
immVertex2f(pos, rect.xmin + vec[a][1], rect.ymax - rad + vec[a][0]);
|
||||
}
|
||||
immVertex2f(pos, rect.xmin + rad, rect.ymax);
|
||||
immEnd();
|
||||
}
|
||||
|
||||
if (corners & ui::CNR_TOP_RIGHT) {
|
||||
immBegin(GPU_PRIM_TRI_FAN, 7);
|
||||
immVertex2f(pos, rect.xmax + 1, rect.ymax);
|
||||
immVertex2f(pos, rect.xmax - rad, rect.ymax);
|
||||
for (int a = 0; a < 4; a++) {
|
||||
immVertex2f(pos, rect.xmax - rad + vec[a][0], rect.ymax - vec[a][1]);
|
||||
}
|
||||
immVertex2f(pos, rect.xmax, rect.ymax - rad);
|
||||
immEnd();
|
||||
}
|
||||
|
||||
if (corners & ui::CNR_BOTTOM_RIGHT) {
|
||||
immBegin(GPU_PRIM_TRI_FAN, 7);
|
||||
immVertex2f(pos, rect.xmax + 1, rect.ymin);
|
||||
immVertex2f(pos, rect.xmax, rect.ymin + rad);
|
||||
for (int a = 0; a < 4; a++) {
|
||||
immVertex2f(pos, rect.xmax - vec[a][1], rect.ymin + rad - vec[a][0]);
|
||||
}
|
||||
immVertex2f(pos, rect.xmax - rad, rect.ymin);
|
||||
immEnd();
|
||||
}
|
||||
|
||||
if (corners & ui::CNR_BOTTOM_LEFT) {
|
||||
immBegin(GPU_PRIM_TRI_FAN, 7);
|
||||
immVertex2f(pos, rect.xmin - 1, rect.ymin);
|
||||
immVertex2f(pos, rect.xmin + rad, rect.ymin);
|
||||
for (int a = 0; a < 4; a++) {
|
||||
immVertex2f(pos, rect.xmin + rad - vec[a][0], rect.ymin + vec[a][1]);
|
||||
}
|
||||
immVertex2f(pos, rect.xmin, rect.ymin + rad);
|
||||
immEnd();
|
||||
}
|
||||
|
||||
immUnbindProgram();
|
||||
}
|
||||
|
||||
void screen_draw_dock_preview(const wmWindow *win,
|
||||
ScrArea *source,
|
||||
ScrArea *target,
|
||||
AreaDockTarget dock_target,
|
||||
float factor,
|
||||
int x,
|
||||
int y,
|
||||
float anim_factor)
|
||||
{
|
||||
if (dock_target == AreaDockTarget::None) {
|
||||
return;
|
||||
}
|
||||
|
||||
float outline[4] = {1.0f, 1.0f, 1.0f, 0.4f * anim_factor};
|
||||
float inner[4] = {1.0f, 1.0f, 1.0f, 0.1f * anim_factor};
|
||||
float border[4];
|
||||
ui::theme::get_color_4fv(TH_EDITOR_BORDER, border);
|
||||
border[3] *= anim_factor;
|
||||
ui::draw_roundbox_corner_set(ui::CNR_ALL);
|
||||
float half_line_width = float(U.border_width) * UI_SCALE_FAC;
|
||||
|
||||
rctf dest;
|
||||
rctf remainder;
|
||||
BLI_rctf_rcti_copy(&dest, &target->totrct);
|
||||
BLI_rctf_rcti_copy(&remainder, &target->totrct);
|
||||
|
||||
float split;
|
||||
int corners = ui::CNR_NONE;
|
||||
|
||||
if (dock_target == AreaDockTarget::Right) {
|
||||
split = std::min(dest.xmin + target->winx * (1.0f - factor),
|
||||
dest.xmax - AREAMINX * UI_SCALE_FAC);
|
||||
dest.xmin = split + half_line_width;
|
||||
remainder.xmax = split - half_line_width;
|
||||
corners = ui::CNR_TOP_LEFT | ui::CNR_BOTTOM_LEFT;
|
||||
}
|
||||
else if (dock_target == AreaDockTarget::Left) {
|
||||
split = std::max(dest.xmax - target->winx * (1.0f - factor),
|
||||
dest.xmin + AREAMINX * UI_SCALE_FAC);
|
||||
dest.xmax = split - half_line_width;
|
||||
remainder.xmin = split + half_line_width;
|
||||
corners = ui::CNR_TOP_RIGHT | ui::CNR_BOTTOM_RIGHT;
|
||||
}
|
||||
else if (dock_target == AreaDockTarget::Top) {
|
||||
split = std::min(dest.ymin + target->winy * (1.0f - factor),
|
||||
dest.ymax - HEADERY * UI_SCALE_FAC);
|
||||
dest.ymin = split + half_line_width;
|
||||
remainder.ymax = split - half_line_width;
|
||||
corners = ui::CNR_BOTTOM_RIGHT | ui::CNR_BOTTOM_LEFT;
|
||||
}
|
||||
else if (dock_target == AreaDockTarget::Bottom) {
|
||||
split = std::max(dest.ymax - target->winy * (1.0f - factor),
|
||||
dest.ymin + HEADERY * UI_SCALE_FAC);
|
||||
dest.ymax = split - half_line_width;
|
||||
remainder.ymin = split + half_line_width;
|
||||
corners = ui::CNR_TOP_RIGHT | ui::CNR_TOP_LEFT;
|
||||
}
|
||||
|
||||
rounded_corners(dest, border, corners);
|
||||
ui::draw_roundbox_4fv_ex(&dest, inner, nullptr, 1.0f, outline, U.pixelsize, EDITORRADIUS);
|
||||
|
||||
if (dock_target != AreaDockTarget::Center) {
|
||||
/* 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);
|
||||
}
|
||||
|
||||
screen_draw_area_drag_tip(win,
|
||||
x,
|
||||
y,
|
||||
source,
|
||||
dock_target == AreaDockTarget::Center ? IFACE_("Replace this area") :
|
||||
IFACE_("Move area here"));
|
||||
}
|
||||
|
||||
void screen_draw_split_preview(ScrArea *area, const eScreenAxis dir_axis, const float factor)
|
||||
{
|
||||
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::theme::get_color_4fv(TH_EDITOR_BORDER, border);
|
||||
draw_roundbox_corner_set(ui::CNR_ALL);
|
||||
|
||||
rctf rect;
|
||||
BLI_rctf_rcti_copy(&rect, &area->totrct);
|
||||
|
||||
if (factor < 0.0001 || factor > 0.9999) {
|
||||
/* Highlight the entire area. */
|
||||
ui::draw_roundbox_4fv_ex(&rect, inner, nullptr, 1.0f, outline, U.pixelsize, EDITORRADIUS);
|
||||
return;
|
||||
}
|
||||
|
||||
float x = (1 - factor) * rect.xmin + factor * rect.xmax;
|
||||
float y = (1 - factor) * rect.ymin + factor * rect.ymax;
|
||||
x = std::clamp(x, rect.xmin, rect.xmax);
|
||||
y = std::clamp(y, rect.ymin, rect.ymax);
|
||||
float half_line_width = float(U.border_width) * UI_SCALE_FAC;
|
||||
|
||||
/* 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;
|
||||
|
||||
rounded_corners(rect,
|
||||
border,
|
||||
(dir_axis == SCREEN_AXIS_H) ? ui::CNR_TOP_RIGHT | ui::CNR_TOP_LEFT :
|
||||
ui::CNR_BOTTOM_RIGHT | ui::CNR_TOP_RIGHT);
|
||||
ui::draw_roundbox_4fv_ex(&rect, inner, nullptr, 1.0f, outline, U.pixelsize, EDITORRADIUS);
|
||||
|
||||
/* Outlined rectangle to right/below split position. */
|
||||
if (dir_axis == SCREEN_AXIS_H) {
|
||||
rect.ymin = y + half_line_width;
|
||||
rect.ymax = area->totrct.ymax;
|
||||
}
|
||||
else {
|
||||
rect.xmin = x + half_line_width;
|
||||
rect.xmax = area->totrct.xmax;
|
||||
}
|
||||
|
||||
rounded_corners(rect,
|
||||
border,
|
||||
(dir_axis == SCREEN_AXIS_H) ? ui::CNR_BOTTOM_RIGHT | ui::CNR_BOTTOM_LEFT :
|
||||
ui::CNR_BOTTOM_LEFT | ui::CNR_TOP_LEFT);
|
||||
ui::draw_roundbox_4fv_ex(&rect, inner, nullptr, 1.0f, outline, U.pixelsize, EDITORRADIUS);
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
struct AreaAnimateHighlightData {
|
||||
wmWindow *win;
|
||||
bScreen *screen;
|
||||
rctf rect;
|
||||
float inner[4];
|
||||
float outline[4];
|
||||
double start_time;
|
||||
double end_time;
|
||||
void *draw_callback;
|
||||
};
|
||||
|
||||
static void area_animate_highlight_cb(const wmWindow * /*win*/, void *userdata)
|
||||
{
|
||||
const AreaAnimateHighlightData *data = static_cast<const AreaAnimateHighlightData *>(userdata);
|
||||
|
||||
double now = BLI_time_now_seconds();
|
||||
if (now > data->end_time) {
|
||||
WM_draw_cb_exit(data->win, data->draw_callback);
|
||||
MEM_delete(const_cast<AreaAnimateHighlightData *>(data));
|
||||
data = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
const float factor = pow((now - data->start_time) / (data->end_time - data->start_time), 2);
|
||||
const bool do_inner = data->inner[3] > 0.0f;
|
||||
const bool do_outline = data->outline[3] > 0.0f;
|
||||
|
||||
float inner_color[4];
|
||||
if (do_inner) {
|
||||
inner_color[0] = data->inner[0];
|
||||
inner_color[1] = data->inner[1];
|
||||
inner_color[2] = data->inner[2];
|
||||
inner_color[3] = (1.0f - factor) * data->inner[3];
|
||||
}
|
||||
|
||||
float outline_color[4];
|
||||
if (do_outline) {
|
||||
outline_color[0] = data->outline[0];
|
||||
outline_color[1] = data->outline[1];
|
||||
outline_color[2] = data->outline[2];
|
||||
outline_color[3] = (1.0f - factor) * data->outline[3];
|
||||
}
|
||||
|
||||
ui::draw_roundbox_corner_set(ui::CNR_ALL);
|
||||
ui::draw_roundbox_4fv_ex(&data->rect,
|
||||
do_inner ? inner_color : nullptr,
|
||||
nullptr,
|
||||
1.0f,
|
||||
do_outline ? outline_color : nullptr,
|
||||
U.pixelsize,
|
||||
EDITORRADIUS);
|
||||
|
||||
data->screen->do_refresh = true;
|
||||
}
|
||||
|
||||
void screen_animate_area_highlight(wmWindow *win,
|
||||
bScreen *screen,
|
||||
const rcti *rect,
|
||||
float inner[4],
|
||||
float outline[4],
|
||||
float seconds)
|
||||
{
|
||||
/* Disabling for now, see #147487. This can cause memory leaks since the
|
||||
* data is only freed when the animation completes, which might not happen
|
||||
* during automated tests. Freeing wmWindow->runtime->drawcalls on window close might
|
||||
* be enough, but will have to be investigated. */
|
||||
return;
|
||||
|
||||
AreaAnimateHighlightData *data = MEM_new_zeroed<AreaAnimateHighlightData>(
|
||||
"screen_animate_area_highlight");
|
||||
data->win = win;
|
||||
data->screen = screen;
|
||||
BLI_rctf_rcti_copy(&data->rect, rect);
|
||||
if (inner) {
|
||||
copy_v4_v4(data->inner, inner);
|
||||
}
|
||||
if (outline) {
|
||||
copy_v4_v4(data->outline, outline);
|
||||
}
|
||||
data->start_time = BLI_time_now_seconds();
|
||||
data->end_time = data->start_time + seconds;
|
||||
data->draw_callback = WM_draw_cb_activate(win, area_animate_highlight_cb, data);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
2188
blender-5.2.0/source/blender/editors/screen/screen_edit.cc
Normal file
2188
blender-5.2.0/source/blender/editors/screen/screen_edit.cc
Normal file
File diff suppressed because it is too large
Load Diff
552
blender-5.2.0/source/blender/editors/screen/screen_geometry.cc
Normal file
552
blender-5.2.0/source/blender/editors/screen/screen_geometry.cc
Normal file
@@ -0,0 +1,552 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup edscr
|
||||
* \brief Functions for screen vertices and edges
|
||||
*
|
||||
* Screen geometry refers to the vertices (ScrVert) and edges (ScrEdge) through
|
||||
* which the flexible screen-layout system of Blender is established.
|
||||
*/
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_rect.h"
|
||||
|
||||
#include "BKE_screen.hh"
|
||||
|
||||
#include "DNA_screen_types.h"
|
||||
#include "DNA_windowmanager_types.h"
|
||||
|
||||
#include "ED_screen.hh"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "WM_api.hh"
|
||||
|
||||
#include "screen_intern.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
int screen_geom_area_height(const ScrArea *area)
|
||||
{
|
||||
return area->v2->vec.y - area->v1->vec.y + 1;
|
||||
}
|
||||
int screen_geom_area_width(const ScrArea *area)
|
||||
{
|
||||
return area->v4->vec.x - area->v1->vec.x + 1;
|
||||
}
|
||||
|
||||
ScrVert *screen_geom_vertex_add_ex(ScrAreaMap *area_map, short x, short y)
|
||||
{
|
||||
ScrVert *sv = MEM_new<ScrVert>("addscrvert");
|
||||
sv->vec.x = x;
|
||||
sv->vec.y = y;
|
||||
|
||||
BLI_addtail(&area_map->vertbase, sv);
|
||||
return sv;
|
||||
}
|
||||
ScrVert *screen_geom_vertex_add(bScreen *screen, short x, short y)
|
||||
{
|
||||
return screen_geom_vertex_add_ex(AREAMAP_FROM_SCREEN(screen), x, y);
|
||||
}
|
||||
|
||||
ScrEdge *screen_geom_edge_add_ex(ScrAreaMap *area_map, ScrVert *v1, ScrVert *v2)
|
||||
{
|
||||
ScrEdge *se = MEM_new<ScrEdge>("addscredge");
|
||||
|
||||
BKE_screen_sort_scrvert(&v1, &v2);
|
||||
se->v1 = v1;
|
||||
se->v2 = v2;
|
||||
|
||||
BLI_addtail(&area_map->edgebase, se);
|
||||
return se;
|
||||
}
|
||||
ScrEdge *screen_geom_edge_add(bScreen *screen, ScrVert *v1, ScrVert *v2)
|
||||
{
|
||||
return screen_geom_edge_add_ex(AREAMAP_FROM_SCREEN(screen), v1, v2);
|
||||
}
|
||||
|
||||
bool screen_geom_edge_is_horizontal(ScrEdge *se)
|
||||
{
|
||||
return (se->v1->vec.y == se->v2->vec.y);
|
||||
}
|
||||
|
||||
ScrEdge *screen_geom_area_map_find_active_scredge(
|
||||
const ScrAreaMap *area_map, const rcti *bounds_rect, const int mx, const int my, int safety)
|
||||
{
|
||||
CLAMP_MIN(safety, 2);
|
||||
|
||||
for (ScrEdge &se : area_map->edgebase) {
|
||||
if (screen_geom_edge_is_horizontal(&se)) {
|
||||
if ((se.v1->vec.y > bounds_rect->ymin) && (se.v1->vec.y < (bounds_rect->ymax - 1))) {
|
||||
short min, max;
|
||||
min = std::min(se.v1->vec.x, se.v2->vec.x);
|
||||
max = std::max(se.v1->vec.x, se.v2->vec.x);
|
||||
|
||||
if (abs(my - se.v1->vec.y) <= safety && mx >= min && mx <= max) {
|
||||
return &se;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ((se.v1->vec.x > bounds_rect->xmin) && (se.v1->vec.x < (bounds_rect->xmax - 1))) {
|
||||
short min, max;
|
||||
min = std::min(se.v1->vec.y, se.v2->vec.y);
|
||||
max = std::max(se.v1->vec.y, se.v2->vec.y);
|
||||
|
||||
if (abs(mx - se.v1->vec.x) <= safety && my >= min && my <= max) {
|
||||
return &se;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ScrEdge *screen_geom_find_active_scredge(const wmWindow *win,
|
||||
const bScreen *screen,
|
||||
const int mx,
|
||||
const int my)
|
||||
{
|
||||
if (U.app_flag & USER_APP_LOCK_EDGE_RESIZE) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Use layout size (screen excluding global areas) for screen-layout area edges */
|
||||
rcti screen_rect;
|
||||
WM_window_screen_rect_calc(win, &screen_rect);
|
||||
ScrEdge *se = screen_geom_area_map_find_active_scredge(
|
||||
AREAMAP_FROM_SCREEN(screen), &screen_rect, mx, my, BORDERPADDING);
|
||||
|
||||
if (!se) {
|
||||
/* Use entire window size (screen including global areas) for global area edges */
|
||||
rcti win_rect;
|
||||
WM_window_rect_calc(win, &win_rect);
|
||||
se = screen_geom_area_map_find_active_scredge(
|
||||
&win->global_areas, &win_rect, mx, my, int(BORDERPADDING_GLOBAL));
|
||||
}
|
||||
return se;
|
||||
}
|
||||
|
||||
/**
|
||||
* A single pass for moving all screen vertices to fit into \a screen_rect.
|
||||
* \return true if another pass should be run.
|
||||
*/
|
||||
static bool screen_geom_vertices_scale_pass(const wmWindow *win,
|
||||
const bScreen *screen,
|
||||
const rcti *screen_rect)
|
||||
{
|
||||
|
||||
const int screen_size_x = BLI_rcti_size_x(screen_rect);
|
||||
const int screen_size_y = BLI_rcti_size_y(screen_rect);
|
||||
bool needs_another_pass = false;
|
||||
|
||||
/* calculate size */
|
||||
float min[2] = {20000.0f, 20000.0f};
|
||||
float max[2] = {0.0f, 0.0f};
|
||||
|
||||
for (ScrVert &sv : screen->vertbase) {
|
||||
const float fv[2] = {float(sv.vec.x), float(sv.vec.y)};
|
||||
minmax_v2v2_v2(min, max, fv);
|
||||
}
|
||||
|
||||
int screen_size_x_prev = (max[0] - min[0]) + 1;
|
||||
int screen_size_y_prev = (max[1] - min[1]) + 1;
|
||||
|
||||
if (screen_size_x_prev != screen_size_x || screen_size_y_prev != screen_size_y) {
|
||||
const float facx = (float(screen_size_x) - 1) / (float(screen_size_x_prev) - 1);
|
||||
const float facy = (float(screen_size_y) - 1) / (float(screen_size_y_prev) - 1);
|
||||
|
||||
/* make sure it fits! */
|
||||
for (ScrVert &sv : screen->vertbase) {
|
||||
sv.vec.x = screen_rect->xmin + round_fl_to_short((sv.vec.x - min[0]) * facx);
|
||||
CLAMP(sv.vec.x, screen_rect->xmin, screen_rect->xmax - 1);
|
||||
|
||||
sv.vec.y = screen_rect->ymin + round_fl_to_short((sv.vec.y - min[1]) * facy);
|
||||
CLAMP(sv.vec.y, screen_rect->ymin, screen_rect->ymax - 1);
|
||||
}
|
||||
|
||||
/* test for collapsed areas. This could happen in some blender version... */
|
||||
/* ton: removed option now, it needs Context... */
|
||||
|
||||
if (facy > 1) {
|
||||
/* Keep timeline small in video edit workspace. */
|
||||
for (ScrArea &area : screen->areabase) {
|
||||
const int border_width = int(ceil(float(U.border_width) * UI_SCALE_FAC));
|
||||
int min = ED_area_headersize() + border_width;
|
||||
if (area.v1->vec.y > screen_rect->ymin) {
|
||||
min += border_width;
|
||||
}
|
||||
if (area.spacetype == SPACE_ACTION && area.v1->vec.y == screen_rect->ymin &&
|
||||
screen_geom_area_height(&area) <= int(min * 1.5f))
|
||||
{
|
||||
ScrEdge *se = BKE_screen_find_edge(screen, area.v2, area.v3);
|
||||
if (se) {
|
||||
const int yval = area.v1->vec.y + min - 1;
|
||||
|
||||
screen_geom_select_connected_edge(win, se);
|
||||
|
||||
/* all selected vertices get the right offset */
|
||||
for (ScrVert &sv : screen->vertbase) {
|
||||
/* if is a collapsed area */
|
||||
if (!ELEM(&sv, area.v1, area.v4)) {
|
||||
if (sv.flag) {
|
||||
sv.vec.y = yval;
|
||||
/* Changed size of a area. Run another pass to ensure everything still fits. */
|
||||
needs_another_pass = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Make each window at least ED_area_headersize() high. This
|
||||
* should be done whether we are increasing or decreasing the
|
||||
* vertical size since this is called on file load, not just
|
||||
* during resize operations. */
|
||||
for (ScrArea &area : screen->areabase) {
|
||||
const int border_width = int(ceil(float(U.border_width) * UI_SCALE_FAC));
|
||||
int min = ED_area_headersize() + border_width + border_width - U.pixelsize;
|
||||
if (area.v3->vec.y >= (screen_rect->ymax - 1)) {
|
||||
/* Area aligned to top screen edge. */
|
||||
min = ED_area_headersize() + border_width;
|
||||
}
|
||||
else if (area.v4->vec.y <= (screen_rect->ymin + 1)) {
|
||||
/* Area aligned to bottom screen edge. */
|
||||
min = ED_area_headersize() + border_width + 1;
|
||||
}
|
||||
|
||||
const int height = screen_geom_area_height(&area);
|
||||
if (height < min) {
|
||||
/* lower edge */
|
||||
ScrEdge *se = BKE_screen_find_edge(screen, area.v4, area.v1);
|
||||
if (se && area.v1 != area.v2) {
|
||||
const int yval = area.v2->vec.y - min;
|
||||
|
||||
screen_geom_select_connected_edge(win, se);
|
||||
|
||||
/* all selected vertices get the right offset */
|
||||
for (ScrVert &sv : screen->vertbase) {
|
||||
/* if is not a collapsed area */
|
||||
if (!ELEM(&sv, area.v2, area.v3)) {
|
||||
if (sv.flag) {
|
||||
sv.vec.y = yval;
|
||||
/* Changed size of a area. Run another pass to ensure everything still fits. */
|
||||
needs_another_pass = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return needs_another_pass;
|
||||
}
|
||||
|
||||
void screen_geom_vertices_scale(const wmWindow *win, bScreen *screen)
|
||||
{
|
||||
rcti window_rect, screen_rect;
|
||||
WM_window_rect_calc(win, &window_rect);
|
||||
WM_window_screen_rect_calc(win, &screen_rect);
|
||||
|
||||
bool needs_another_pass;
|
||||
int max_passes_left = 10; /* Avoids endless loop. Number is rather arbitrary. */
|
||||
do {
|
||||
needs_another_pass = screen_geom_vertices_scale_pass(win, screen, &screen_rect);
|
||||
max_passes_left--;
|
||||
} while (needs_another_pass && (max_passes_left > 0));
|
||||
|
||||
/* Global areas have a fixed size that only changes with the DPI.
|
||||
* Here we ensure that exactly this size is set. */
|
||||
for (ScrArea &area : win->global_areas.areabase) {
|
||||
if (area.global->flag & GLOBAL_AREA_IS_HIDDEN) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int height = ED_area_global_size_y(&area) - 1;
|
||||
|
||||
if (area.v1->vec.y > window_rect.ymin) {
|
||||
height += U.pixelsize;
|
||||
}
|
||||
if (area.v2->vec.y < (window_rect.ymax - 1)) {
|
||||
height += U.pixelsize;
|
||||
}
|
||||
|
||||
/* width */
|
||||
area.v1->vec.x = area.v2->vec.x = window_rect.xmin;
|
||||
area.v3->vec.x = area.v4->vec.x = window_rect.xmax - 1;
|
||||
/* height */
|
||||
area.v1->vec.y = area.v4->vec.y = window_rect.ymin;
|
||||
area.v2->vec.y = area.v3->vec.y = window_rect.ymax - 1;
|
||||
|
||||
switch (area.global->align) {
|
||||
case GLOBAL_AREA_ALIGN_TOP:
|
||||
area.v1->vec.y = area.v4->vec.y = area.v2->vec.y - height;
|
||||
break;
|
||||
case GLOBAL_AREA_ALIGN_BOTTOM:
|
||||
area.v2->vec.y = area.v3->vec.y = area.v1->vec.y + height;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
short screen_geom_find_area_split_point(const ScrArea *area,
|
||||
const rcti *window_rect,
|
||||
const eScreenAxis dir_axis,
|
||||
float fac)
|
||||
{
|
||||
const int cur_area_width = screen_geom_area_width(area);
|
||||
const int cur_area_height = screen_geom_area_height(area);
|
||||
const short area_min_x = AREAMINX * UI_SCALE_FAC;
|
||||
const short area_min_y = ED_area_headersize();
|
||||
|
||||
/* area big enough? */
|
||||
if (dir_axis == SCREEN_AXIS_V) {
|
||||
if (cur_area_width <= 2 * area_min_x) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else if (dir_axis == SCREEN_AXIS_H) {
|
||||
if (cur_area_height <= 2 * area_min_y) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* to be sure */
|
||||
CLAMP(fac, 0.0f, 1.0f);
|
||||
|
||||
if (dir_axis == SCREEN_AXIS_H) {
|
||||
short y = area->v1->vec.y + round_fl_to_short(fac * cur_area_height);
|
||||
|
||||
int area_min = area_min_y;
|
||||
|
||||
if (area->v1->vec.y > window_rect->ymin) {
|
||||
area_min += U.pixelsize;
|
||||
}
|
||||
if (area->v2->vec.y < (window_rect->ymax - 1)) {
|
||||
area_min += U.pixelsize;
|
||||
}
|
||||
|
||||
if (y - area->v1->vec.y < area_min) {
|
||||
y = area->v1->vec.y + area_min;
|
||||
}
|
||||
else if (area->v2->vec.y - y < area_min) {
|
||||
y = area->v2->vec.y - area_min;
|
||||
}
|
||||
|
||||
return y;
|
||||
}
|
||||
|
||||
short x = area->v1->vec.x + round_fl_to_short(fac * cur_area_width);
|
||||
|
||||
int area_min = area_min_x;
|
||||
|
||||
if (area->v1->vec.x > window_rect->xmin) {
|
||||
area_min += U.pixelsize;
|
||||
}
|
||||
if (area->v4->vec.x < (window_rect->xmax - 1)) {
|
||||
area_min += U.pixelsize;
|
||||
}
|
||||
|
||||
if (x - area->v1->vec.x < area_min) {
|
||||
x = area->v1->vec.x + area_min;
|
||||
}
|
||||
else if (area->v4->vec.x - x < area_min) {
|
||||
x = area->v4->vec.x - area_min;
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
void screen_geom_select_connected_edge(const wmWindow *win, ScrEdge *edge)
|
||||
{
|
||||
bScreen *screen = WM_window_get_active_screen(win);
|
||||
|
||||
/* 'dir_axis' is the direction of EDGE */
|
||||
eScreenAxis dir_axis;
|
||||
if (edge->v1->vec.x == edge->v2->vec.x) {
|
||||
dir_axis = SCREEN_AXIS_V;
|
||||
}
|
||||
else {
|
||||
dir_axis = SCREEN_AXIS_H;
|
||||
}
|
||||
|
||||
ED_screen_verts_iter(win, screen, sv)
|
||||
{
|
||||
sv->flag = 0;
|
||||
}
|
||||
|
||||
edge->v1->flag = 1;
|
||||
edge->v2->flag = 1;
|
||||
|
||||
/* select connected, only in the right direction */
|
||||
bool oneselected = true;
|
||||
while (oneselected) {
|
||||
oneselected = false;
|
||||
for (ScrEdge &se : screen->edgebase) {
|
||||
if (se.v1->flag + se.v2->flag == 1) {
|
||||
if (dir_axis == SCREEN_AXIS_H) {
|
||||
if (se.v1->vec.y == se.v2->vec.y) {
|
||||
se.v1->flag = se.v2->flag = 1;
|
||||
oneselected = true;
|
||||
}
|
||||
}
|
||||
else if (dir_axis == SCREEN_AXIS_V) {
|
||||
if (se.v1->vec.x == se.v2->vec.x) {
|
||||
se.v1->flag = se.v2->flag = 1;
|
||||
oneselected = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool screen_geom_edge_can_extend(const wmWindow *win, ScrEdge *edge)
|
||||
{
|
||||
bScreen *screen = WM_window_get_active_screen(win);
|
||||
bool can_extend = false;
|
||||
const eScreenAxis dir_axis = (edge->v1->vec.x == edge->v2->vec.x) ? SCREEN_AXIS_V :
|
||||
SCREEN_AXIS_H;
|
||||
screen_geom_select_connected_edge(win, edge);
|
||||
|
||||
for (ScrEdge &se : screen->edgebase) {
|
||||
if (se.v1->flag + se.v2->flag != 0) {
|
||||
continue;
|
||||
}
|
||||
if (dir_axis == SCREEN_AXIS_H) {
|
||||
for (ScrVert &v : screen->vertbase) {
|
||||
if (v.flag && v.vec.x == se.v1->vec.x &&
|
||||
(abs(v.vec.y - se.v1->vec.y) < EDGE_ALIGN_TOLERANCE ||
|
||||
abs(v.vec.y - se.v2->vec.y) < EDGE_ALIGN_TOLERANCE))
|
||||
{
|
||||
se.v1->flag = se.v2->flag = 1;
|
||||
can_extend = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (dir_axis == SCREEN_AXIS_V) {
|
||||
for (ScrVert &v : screen->vertbase) {
|
||||
if (v.flag && v.vec.y == se.v1->vec.y &&
|
||||
(abs(v.vec.x - se.v1->vec.x) < EDGE_ALIGN_TOLERANCE ||
|
||||
abs(v.vec.x - se.v2->vec.x) < EDGE_ALIGN_TOLERANCE))
|
||||
{
|
||||
se.v1->flag = se.v2->flag = 1;
|
||||
can_extend = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ED_screen_verts_iter(win, screen, sv)
|
||||
{
|
||||
sv->flag = 0;
|
||||
}
|
||||
|
||||
return can_extend;
|
||||
}
|
||||
|
||||
void screen_geom_select_extended_edge(const wmWindow *win, ScrEdge *edge)
|
||||
{
|
||||
bScreen *screen = WM_window_get_active_screen(win);
|
||||
const eScreenAxis dir_axis = (edge->v1->vec.x == edge->v2->vec.x) ? SCREEN_AXIS_V :
|
||||
SCREEN_AXIS_H;
|
||||
ED_screen_verts_iter(win, screen, sv)
|
||||
{
|
||||
sv->flag = 0;
|
||||
}
|
||||
|
||||
for (ScrVert &v : screen->vertbase) {
|
||||
if (dir_axis == SCREEN_AXIS_H) {
|
||||
if (abs(v.vec.y - edge->v1->vec.y) < EDGE_ALIGN_TOLERANCE) {
|
||||
v.flag = 1;
|
||||
}
|
||||
}
|
||||
else if (dir_axis == SCREEN_AXIS_V) {
|
||||
if (abs(v.vec.x - edge->v1->vec.x) < EDGE_ALIGN_TOLERANCE) {
|
||||
v.flag = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void screen_geom_edge_aligned_merge(const wmWindow *win, ScrEdge *edge)
|
||||
{
|
||||
bScreen *screen = WM_window_get_active_screen(win);
|
||||
screen_geom_select_extended_edge(win, edge);
|
||||
const eScreenAxis dir_axis = (edge->v1->vec.x == edge->v2->vec.x) ? SCREEN_AXIS_V :
|
||||
SCREEN_AXIS_H;
|
||||
/* Align the vertices if close. */
|
||||
for (ScrVert &v : screen->vertbase) {
|
||||
if (dir_axis == SCREEN_AXIS_V && abs(v.vec.x - edge->v2->vec.x) < EDGE_ALIGN_TOLERANCE) {
|
||||
v.vec.x = edge->v2->vec.x;
|
||||
}
|
||||
else if (abs(v.vec.y - edge->v2->vec.y) < EDGE_ALIGN_TOLERANCE) {
|
||||
v.vec.y = edge->v2->vec.y;
|
||||
}
|
||||
}
|
||||
|
||||
for (ScrVert &v : screen->vertbase) {
|
||||
if (v.flag == 1 && v.newv == nullptr) { /* !!! */
|
||||
ScrVert *v1 = v.next;
|
||||
while (v1) {
|
||||
if (v1->newv == nullptr) { /* !?! */
|
||||
if (abs(v1->vec.x - v.vec.x) < EDGE_ALIGN_TOLERANCE &&
|
||||
abs(v1->vec.y - v.vec.y) < EDGE_ALIGN_TOLERANCE)
|
||||
{
|
||||
v1->newv = &v;
|
||||
}
|
||||
}
|
||||
v1 = v1->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Replace pointers in edges and faces. */
|
||||
for (ScrEdge &se : screen->edgebase) {
|
||||
if (se.v1->newv) {
|
||||
se.v1 = se.v1->newv;
|
||||
}
|
||||
if (se.v2->newv) {
|
||||
se.v2 = se.v2->newv;
|
||||
}
|
||||
BKE_screen_sort_scrvert(&(se.v1), &(se.v2));
|
||||
}
|
||||
for (ScrArea &area : screen->areabase) {
|
||||
if (area.v1->newv) {
|
||||
area.v1 = area.v1->newv;
|
||||
}
|
||||
if (area.v2->newv) {
|
||||
area.v2 = area.v2->newv;
|
||||
}
|
||||
if (area.v3->newv) {
|
||||
area.v3 = area.v3->newv;
|
||||
}
|
||||
if (area.v4->newv) {
|
||||
area.v4 = area.v4->newv;
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove. */
|
||||
for (ScrVert &v : screen->vertbase.items_mutable()) {
|
||||
if (v.newv) {
|
||||
BLI_remlink(&screen->vertbase, &v);
|
||||
MEM_delete(&v);
|
||||
}
|
||||
}
|
||||
|
||||
ED_screen_verts_iter(win, screen, sv)
|
||||
{
|
||||
sv->flag = 0;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
276
blender-5.2.0/source/blender/editors/screen/screen_intern.hh
Normal file
276
blender-5.2.0/source/blender/editors/screen/screen_intern.hh
Normal file
@@ -0,0 +1,276 @@
|
||||
/* SPDX-FileCopyrightText: 2008 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup edscr
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DNA_space_types.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct ARegion;
|
||||
struct AZone;
|
||||
struct ReportList;
|
||||
struct bContext;
|
||||
struct bContextDataResult;
|
||||
struct bScreen;
|
||||
struct Main;
|
||||
struct rcti;
|
||||
struct ScrArea;
|
||||
struct ScrAreaMap;
|
||||
struct ScrEdge;
|
||||
struct ScrVert;
|
||||
struct WorkSpaceLayout;
|
||||
struct wmOperatorType;
|
||||
struct wmWindow;
|
||||
struct wmWindowManager;
|
||||
|
||||
/* internal exports only */
|
||||
|
||||
enum eScreenDir {
|
||||
/** This can mean unset, unknown or invalid. */
|
||||
SCREEN_DIR_NONE = -1,
|
||||
/** West/Left. */
|
||||
SCREEN_DIR_W = 0,
|
||||
/** North/Up. */
|
||||
SCREEN_DIR_N = 1,
|
||||
/** East/Right. */
|
||||
SCREEN_DIR_E = 2,
|
||||
/** South/Down. */
|
||||
SCREEN_DIR_S = 3,
|
||||
};
|
||||
|
||||
#define SCREEN_DIR_IS_VERTICAL(dir) (ELEM(dir, SCREEN_DIR_N, SCREEN_DIR_S))
|
||||
#define SCREEN_DIR_IS_HORIZONTAL(dir) (ELEM(dir, SCREEN_DIR_W, SCREEN_DIR_E))
|
||||
|
||||
enum eScreenAxis {
|
||||
/** Horizontal. */
|
||||
SCREEN_AXIS_H = 'h',
|
||||
/** Vertical. */
|
||||
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 AZONEFADEIN (5.0f * U.widget_unit) /* when #AZone is totally visible */
|
||||
#define AZONEFADEOUT (6.5f * U.widget_unit) /* when we start seeing the #AZone */
|
||||
|
||||
/* Edges must be within these to allow joining. */
|
||||
#define AREAJOINTOLERANCEX (AREAMINX * UI_SCALE_FAC)
|
||||
#define AREAJOINTOLERANCEY (HEADERY * UI_SCALE_FAC)
|
||||
|
||||
/* Edges must be within this amount to allow aligned edge merging and moving. */
|
||||
#define EDGE_ALIGN_TOLERANCE (7 * UI_SCALE_FAC)
|
||||
|
||||
/**
|
||||
* Expanded interaction influence of area borders.
|
||||
*/
|
||||
#define BORDERPADDING (U.border_width * UI_SCALE_FAC + 3.0f * UI_SCALE_FAC)
|
||||
|
||||
/**
|
||||
* Number of pixels of the area border corner radius.
|
||||
*/
|
||||
#define EDITORRADIUS (6.0f * UI_SCALE_FAC)
|
||||
|
||||
/* Less expansion needed for global edges. */
|
||||
#define BORDERPADDING_GLOBAL (3.0f * UI_SCALE_FAC)
|
||||
|
||||
#define AREA_CLOSE_FADEOUT 0.15f /* seconds */
|
||||
#define AREA_DOCK_FADEOUT 0.15f /* seconds */
|
||||
#define AREA_DOCK_FADEIN 0.15f /* seconds */
|
||||
#define AREA_JOIN_FADEOUT 0.15f /* seconds */
|
||||
#define AREA_SPLIT_FADEOUT 0.15f /* seconds */
|
||||
#define AREA_MOVE_LINE_FADEIN 0.1f /* seconds */
|
||||
#define AREA_MOVE_LINE_FADEOUT 0.15f /* seconds */
|
||||
|
||||
/* `area.cc` */
|
||||
|
||||
/**
|
||||
* We swap spaces for full-screen to keep all allocated data area vertices were set.
|
||||
*/
|
||||
void ED_area_data_copy(ScrArea *area_dst, ScrArea *area_src, bool do_free);
|
||||
void ED_area_data_swap(ScrArea *area_dst, ScrArea *area_src);
|
||||
/* for quick toggle, can skip fades */
|
||||
void region_toggle_hidden(bContext *C, ARegion *region, bool do_fade);
|
||||
|
||||
/* `screen_draw.cc` */
|
||||
|
||||
/**
|
||||
* Visual indication of the two areas involved in a proposed join.
|
||||
*
|
||||
* \param sa1: Area from which the resultant originates.
|
||||
* \param sa2: Target area that will be replaced.
|
||||
*/
|
||||
void screen_draw_join_highlight(
|
||||
const wmWindow *win, ScrArea *sa1, ScrArea *sa2, eScreenDir dir, float anim_factor);
|
||||
void screen_draw_dock_preview(const wmWindow *win,
|
||||
ScrArea *source,
|
||||
ScrArea *target,
|
||||
AreaDockTarget dock_target,
|
||||
float factor,
|
||||
int x,
|
||||
int y,
|
||||
float anim_factor);
|
||||
void screen_draw_split_preview(ScrArea *area, eScreenAxis dir_axis, float factor);
|
||||
|
||||
void screen_draw_move_highlight(const wmWindow *win,
|
||||
bScreen *screen,
|
||||
eScreenAxis dir_axis,
|
||||
float anim_factor);
|
||||
|
||||
void screen_draw_region_scale_highlight(ARegion *region);
|
||||
|
||||
void screen_animate_area_highlight(wmWindow *win,
|
||||
bScreen *screen,
|
||||
const rcti *rect,
|
||||
float inner[4],
|
||||
float outline[4],
|
||||
float seconds);
|
||||
|
||||
/* `screen_edit.cc` */
|
||||
|
||||
/**
|
||||
* Empty screen, with 1 dummy area without space-data. Uses window size.
|
||||
*/
|
||||
bScreen *screen_add(Main *bmain, const char *name, const rcti *rect);
|
||||
/**
|
||||
* Prepare a newly created screen for initializing it as active screen.
|
||||
*/
|
||||
void screen_new_activate_prepare(const wmWindow *win, bScreen *screen_new);
|
||||
void screen_change_update(bContext *C, wmWindow *win, bScreen *screen);
|
||||
/**
|
||||
* \return the screen to activate.
|
||||
* \warning The returned screen may not always equal \a screen_new!
|
||||
*/
|
||||
void screen_change_prepare(
|
||||
bScreen *screen_old, bScreen *screen_new, Main *bmain, bContext *C, wmWindow *win);
|
||||
ScrArea *area_split(
|
||||
const wmWindow *win, bScreen *screen, ScrArea *area, eScreenAxis dir_axis, float fac);
|
||||
/**
|
||||
* Join any two neighboring areas. Might involve complex changes.
|
||||
*/
|
||||
int screen_area_join(
|
||||
bContext *C, ReportList *reports, bScreen *screen, ScrArea *sa1, ScrArea *sa2);
|
||||
/**
|
||||
* with `sa_a` as center, `sa_b` is located at: 0=W, 1=N, 2=E, 3=S
|
||||
* -1 = not valid check.
|
||||
* used with join operator.
|
||||
*/
|
||||
eScreenDir area_getorientation(ScrArea *sa_a, ScrArea *sa_b);
|
||||
/**
|
||||
* Get alignment offset of adjacent areas. 'dir' value is like #area_getorientation().
|
||||
*/
|
||||
void area_getoffsets(ScrArea *sa_a, ScrArea *sa_b, eScreenDir dir, int *r_offset1, int *r_offset2);
|
||||
/**
|
||||
* Close a screen area, allowing most-aligned neighbor to take its place.
|
||||
* not_area is optional area to NOT join into.
|
||||
*/
|
||||
bool screen_area_close(
|
||||
bContext *C, ReportList *reports, bScreen *screen, ScrArea *area, ScrArea *not_area = nullptr);
|
||||
|
||||
void screen_area_spacelink_add(const Scene *scene, ScrArea *area, eSpace_Type space_type);
|
||||
AZone *ED_area_actionzone_find_xy(ScrArea *area, const int xy[2]);
|
||||
|
||||
/**
|
||||
* \return true if any region polling state changed, and an area re-init is needed.
|
||||
*/
|
||||
bool area_regions_poll(bContext *C, const bScreen *screen, ScrArea *area);
|
||||
|
||||
/* `screen_geometry.cc` */
|
||||
|
||||
int screen_geom_area_height(const ScrArea *area);
|
||||
int screen_geom_area_width(const ScrArea *area);
|
||||
ScrVert *screen_geom_vertex_add_ex(ScrAreaMap *area_map, short x, short y);
|
||||
ScrVert *screen_geom_vertex_add(bScreen *screen, short x, short y);
|
||||
ScrEdge *screen_geom_edge_add_ex(ScrAreaMap *area_map, ScrVert *v1, ScrVert *v2);
|
||||
ScrEdge *screen_geom_edge_add(bScreen *screen, ScrVert *v1, ScrVert *v2);
|
||||
bool screen_geom_edge_is_horizontal(ScrEdge *se);
|
||||
/**
|
||||
* \param bounds_rect: Either window or screen bounds.
|
||||
* Used to exclude edges along window/screen edges.
|
||||
*/
|
||||
ScrEdge *screen_geom_area_map_find_active_scredge(const ScrAreaMap *area_map,
|
||||
const rcti *bounds_rect,
|
||||
int mx,
|
||||
int my,
|
||||
int safety = BORDERPADDING);
|
||||
/**
|
||||
* Need win size to make sure not to include edges along screen edge.
|
||||
*/
|
||||
ScrEdge *screen_geom_find_active_scredge(const wmWindow *win,
|
||||
const bScreen *screen,
|
||||
int mx,
|
||||
int my);
|
||||
/**
|
||||
* \brief Main screen-layout calculation function.
|
||||
*
|
||||
* * Scale areas nicely on window size and DPI changes.
|
||||
* * Ensure areas have a minimum height.
|
||||
* * Correctly set global areas to their fixed height.
|
||||
*/
|
||||
void screen_geom_vertices_scale(const wmWindow *win, bScreen *screen);
|
||||
/**
|
||||
* \return 0 if no split is possible, otherwise the screen-coordinate at which to split.
|
||||
*/
|
||||
short screen_geom_find_area_split_point(const ScrArea *area,
|
||||
const rcti *window_rect,
|
||||
eScreenAxis dir_axis,
|
||||
float fac);
|
||||
/**
|
||||
* Select all edges that are directly or indirectly connected to \a edge.
|
||||
*/
|
||||
void screen_geom_select_connected_edge(const wmWindow *win, ScrEdge *edge);
|
||||
|
||||
/**
|
||||
* Select all edges that are aligned with \a edge.
|
||||
*/
|
||||
void screen_geom_select_extended_edge(const wmWindow *win, ScrEdge *edge);
|
||||
|
||||
/**
|
||||
* True if the edge can be extended.
|
||||
*/
|
||||
bool screen_geom_edge_can_extend(const wmWindow *win, ScrEdge *edge);
|
||||
|
||||
/**
|
||||
* Merge aligned edges into a single edge.
|
||||
*/
|
||||
void screen_geom_edge_aligned_merge(const wmWindow *win, ScrEdge *edge);
|
||||
|
||||
/* `screen_context.cc` */
|
||||
|
||||
/**
|
||||
* Entry point for the screen context.
|
||||
*/
|
||||
int ed_screen_context(const bContext *C, const char *member, bContextDataResult *result);
|
||||
|
||||
extern "C" const char *screen_context_dir[]; /* doc access */
|
||||
|
||||
/* `screen_ops.cc` */
|
||||
|
||||
/**
|
||||
* Stop animation playback in the given screen.
|
||||
* If there is no animation playing back in that screen, this is a no-op.
|
||||
*/
|
||||
void screen_stop_playback(Main *bmain, wmWindowManager *wm, wmWindow *win, bScreen *screen);
|
||||
|
||||
/* `screendump.cc` */
|
||||
|
||||
void SCREEN_OT_screenshot(wmOperatorType *ot);
|
||||
void SCREEN_OT_screenshot_area(wmOperatorType *ot);
|
||||
|
||||
/* `workspace_layout_edit.cc` */
|
||||
|
||||
bool workspace_layout_set_poll(const WorkSpaceLayout *layout);
|
||||
|
||||
} // namespace blender
|
||||
7902
blender-5.2.0/source/blender/editors/screen/screen_ops.cc
Normal file
7902
blender-5.2.0/source/blender/editors/screen/screen_ops.cc
Normal file
File diff suppressed because it is too large
Load Diff
403
blender-5.2.0/source/blender/editors/screen/screen_user_menu.cc
Normal file
403
blender-5.2.0/source/blender/editors/screen/screen_user_menu.cc
Normal file
@@ -0,0 +1,403 @@
|
||||
/* SPDX-FileCopyrightText: 2009 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup spview3d
|
||||
*/
|
||||
|
||||
#include <cfloat>
|
||||
#include <cstring>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "AS_asset_library.hh"
|
||||
#include "AS_asset_representation.hh"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_string_utf8.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BLT_translation.hh"
|
||||
|
||||
#include "BKE_blender_user_menu.hh"
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_idprop.hh"
|
||||
#include "BKE_screen.hh"
|
||||
|
||||
#include "WM_api.hh"
|
||||
#include "WM_types.hh"
|
||||
|
||||
#include "ED_asset_list.hh"
|
||||
#include "ED_asset_menu_utils.hh"
|
||||
#include "ED_screen.hh"
|
||||
|
||||
#include "UI_interface_layout.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
#include "RNA_path.hh"
|
||||
#include "RNA_prototypes.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Internal Utilities
|
||||
* \{ */
|
||||
|
||||
static const char *screen_menu_context_string(const bContext *C, const SpaceLink *sl)
|
||||
{
|
||||
if (sl->spacetype == SPACE_NODE) {
|
||||
const SpaceNode *snode = reinterpret_cast<const SpaceNode *>(sl);
|
||||
return snode->tree_idname;
|
||||
}
|
||||
return CTX_data_mode_string(C);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Menu Type
|
||||
* \{ */
|
||||
|
||||
bUserMenu **ED_screen_user_menus_find(const bContext *C, uint *r_len)
|
||||
{
|
||||
SpaceLink *sl = CTX_wm_space_data(C);
|
||||
|
||||
if (sl == nullptr) {
|
||||
*r_len = 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char *context_mode = CTX_data_mode_string(C);
|
||||
const char *context = screen_menu_context_string(C, sl);
|
||||
uint array_len = 3;
|
||||
bUserMenu **um_array = MEM_new_array_zeroed<bUserMenu *>(array_len, __func__);
|
||||
um_array[0] = BKE_blender_user_menu_find(&U.user_menus, sl->spacetype, context);
|
||||
um_array[1] = (sl->spacetype != SPACE_TOPBAR) ?
|
||||
BKE_blender_user_menu_find(&U.user_menus, SPACE_TOPBAR, context_mode) :
|
||||
nullptr;
|
||||
um_array[2] = (sl->spacetype == SPACE_VIEW3D) ?
|
||||
BKE_blender_user_menu_find(&U.user_menus, SPACE_PROPERTIES, context_mode) :
|
||||
nullptr;
|
||||
|
||||
*r_len = array_len;
|
||||
return um_array;
|
||||
}
|
||||
|
||||
bUserMenu *ED_screen_user_menu_ensure(bContext *C)
|
||||
{
|
||||
SpaceLink *sl = CTX_wm_space_data(C);
|
||||
const char *context = screen_menu_context_string(C, sl);
|
||||
return BKE_blender_user_menu_ensure(&U.user_menus, sl->spacetype, context);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Menu Item
|
||||
* \{ */
|
||||
|
||||
bUserMenuItem_Op *ED_screen_user_menu_item_find_operator(ListBaseT<bUserMenuItem> *lb,
|
||||
const wmOperatorType *ot,
|
||||
IDProperty *prop,
|
||||
const char *op_prop_enum,
|
||||
wm::OpCallContext opcontext)
|
||||
{
|
||||
for (bUserMenuItem &umi : *lb) {
|
||||
if (umi.type == USER_MENU_TYPE_OPERATOR) {
|
||||
bUserMenuItem_Op *umi_op = reinterpret_cast<bUserMenuItem_Op *>(&umi);
|
||||
const bool is_strict = prop && umi_op->prop;
|
||||
const bool ok_idprop = IDP_EqualsProperties_ex(prop, umi_op->prop, is_strict);
|
||||
const bool ok_prop_enum = (umi_op->op_prop_enum[0] != '\0') ?
|
||||
STREQ(umi_op->op_prop_enum, op_prop_enum) :
|
||||
true;
|
||||
if (STREQ(ot->idname, umi_op->op_idname) &&
|
||||
(opcontext == wm::OpCallContext(umi_op->opcontext)) && ok_idprop && ok_prop_enum)
|
||||
{
|
||||
return umi_op;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bUserMenuItem_Menu *ED_screen_user_menu_item_find_menu(ListBaseT<bUserMenuItem> *lb,
|
||||
const MenuType *mt)
|
||||
{
|
||||
for (bUserMenuItem &umi : *lb) {
|
||||
if (umi.type == USER_MENU_TYPE_MENU) {
|
||||
bUserMenuItem_Menu *umi_mt = reinterpret_cast<bUserMenuItem_Menu *>(&umi);
|
||||
if (STREQ(mt->idname, umi_mt->mt_idname)) {
|
||||
return umi_mt;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bUserMenuItem_Prop *ED_screen_user_menu_item_find_prop(ListBaseT<bUserMenuItem> *lb,
|
||||
const char *context_data_path,
|
||||
const char *prop_id,
|
||||
int prop_index)
|
||||
{
|
||||
for (bUserMenuItem &umi : *lb) {
|
||||
if (umi.type == USER_MENU_TYPE_PROP) {
|
||||
bUserMenuItem_Prop *umi_pr = reinterpret_cast<bUserMenuItem_Prop *>(&umi);
|
||||
if (STREQ(context_data_path, umi_pr->context_data_path) && STREQ(prop_id, umi_pr->prop_id) &&
|
||||
(prop_index == umi_pr->prop_index))
|
||||
{
|
||||
return umi_pr;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ED_screen_user_menu_item_add_operator(ListBaseT<bUserMenuItem> *lb,
|
||||
const char *ui_name,
|
||||
const wmOperatorType *ot,
|
||||
const IDProperty *prop,
|
||||
const char *op_prop_enum,
|
||||
wm::OpCallContext opcontext)
|
||||
{
|
||||
bUserMenuItem_Op *umi_op = reinterpret_cast<bUserMenuItem_Op *>(
|
||||
BKE_blender_user_menu_item_add(lb, USER_MENU_TYPE_OPERATOR));
|
||||
umi_op->opcontext = int8_t(opcontext);
|
||||
if (!STREQ(ui_name, ot->name)) {
|
||||
STRNCPY_UTF8(umi_op->item.ui_name, ui_name);
|
||||
}
|
||||
STRNCPY_UTF8(umi_op->op_idname, ot->idname);
|
||||
STRNCPY_UTF8(umi_op->op_prop_enum, op_prop_enum);
|
||||
umi_op->prop = prop ? IDP_CopyProperty(prop) : nullptr;
|
||||
}
|
||||
|
||||
void ED_screen_user_menu_item_add_menu(ListBaseT<bUserMenuItem> *lb,
|
||||
const char *ui_name,
|
||||
const MenuType *mt)
|
||||
{
|
||||
bUserMenuItem_Menu *umi_mt = reinterpret_cast<bUserMenuItem_Menu *>(
|
||||
BKE_blender_user_menu_item_add(lb, USER_MENU_TYPE_MENU));
|
||||
if (!STREQ(ui_name, mt->label)) {
|
||||
STRNCPY_UTF8(umi_mt->item.ui_name, ui_name);
|
||||
}
|
||||
STRNCPY_UTF8(umi_mt->mt_idname, mt->idname);
|
||||
}
|
||||
|
||||
void ED_screen_user_menu_item_add_prop(ListBaseT<bUserMenuItem> *lb,
|
||||
const char *ui_name,
|
||||
const char *context_data_path,
|
||||
const char *prop_id,
|
||||
int prop_index)
|
||||
{
|
||||
bUserMenuItem_Prop *umi_pr = reinterpret_cast<bUserMenuItem_Prop *>(
|
||||
BKE_blender_user_menu_item_add(lb, USER_MENU_TYPE_PROP));
|
||||
STRNCPY_UTF8(umi_pr->item.ui_name, ui_name);
|
||||
STRNCPY_UTF8(umi_pr->context_data_path, context_data_path);
|
||||
STRNCPY_UTF8(umi_pr->prop_id, prop_id);
|
||||
umi_pr->prop_index = prop_index;
|
||||
}
|
||||
|
||||
void ED_screen_user_menu_item_remove(ListBaseT<bUserMenuItem> *lb, bUserMenuItem *umi)
|
||||
{
|
||||
BLI_remlink(lb, umi);
|
||||
BKE_blender_user_menu_item_free(umi);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Menu Definition
|
||||
* \{ */
|
||||
|
||||
static bool all_loading_finished()
|
||||
{
|
||||
AssetLibraryReference all_library_ref = asset_system::all_library_reference();
|
||||
return ed::asset::list::is_loaded(&all_library_ref);
|
||||
}
|
||||
|
||||
/**
|
||||
* When adding operators that reference an asset (see
|
||||
* `ed::asset::operator_asset_reference_props_is_set`) make sure the asset libraries are loaded.
|
||||
*/
|
||||
static void handle_operator_asset_reference_props(const bContext &C,
|
||||
bUserMenuItem_Op &umi_op,
|
||||
ui::Layout &row,
|
||||
wmOperatorType *ot,
|
||||
int &r_icon,
|
||||
bool &r_add_operator)
|
||||
{
|
||||
if (!umi_op.prop) {
|
||||
return;
|
||||
}
|
||||
PointerRNA opptr = WM_operator_properties_create_ptr(ot);
|
||||
opptr.data = bke::idprop::create_group("wmOperatorProperties").release();
|
||||
IDP_CopyPropertyContent(opptr.data_as<IDProperty>(), umi_op.prop);
|
||||
if (ed::asset::operator_asset_reference_props_is_set(opptr)) {
|
||||
const bool loading_finished = all_loading_finished();
|
||||
if (!loading_finished) {
|
||||
row.label(IFACE_("Loading Asset Libraries"), ICON_INFO);
|
||||
r_add_operator = false;
|
||||
}
|
||||
else {
|
||||
/* Set `check_context_asset` to false because we're setting the context pointer after getting
|
||||
* the asset from the operator properties. */
|
||||
const asset_system::AssetRepresentation *asset =
|
||||
ed::asset::operator_asset_reference_props_get_asset_from_all_library(
|
||||
C, opptr, CTX_wm_reports(&C));
|
||||
if (asset) {
|
||||
if (asset->is_online_only()) {
|
||||
r_icon = ICON_INTERNET;
|
||||
}
|
||||
PointerRNA asset_ptr = RNA_pointer_create_discrete(
|
||||
nullptr,
|
||||
RNA_AssetRepresentation,
|
||||
const_cast<asset_system::AssetRepresentation *>(asset));
|
||||
row.context_ptr_set("asset", &asset_ptr);
|
||||
}
|
||||
else {
|
||||
r_add_operator = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
WM_operator_properties_free(&opptr);
|
||||
}
|
||||
|
||||
static void screen_user_menu_draw(const bContext *C, Menu *menu)
|
||||
{
|
||||
/* Enable when we have the ability to edit menus. */
|
||||
const bool show_missing = false;
|
||||
char label[512];
|
||||
|
||||
uint um_array_len;
|
||||
bUserMenu **um_array = ED_screen_user_menus_find(C, &um_array_len);
|
||||
bool is_empty = true;
|
||||
for (int um_index = 0; um_index < um_array_len; um_index++) {
|
||||
bUserMenu *um = um_array[um_index];
|
||||
if (um == nullptr) {
|
||||
continue;
|
||||
}
|
||||
for (bUserMenuItem &umi : um->items) {
|
||||
std::optional<StringRefNull> ui_name = umi.ui_name[0] ?
|
||||
std::make_optional<StringRefNull>(umi.ui_name) :
|
||||
std::nullopt;
|
||||
if (umi.type == USER_MENU_TYPE_OPERATOR) {
|
||||
bUserMenuItem_Op *umi_op = reinterpret_cast<bUserMenuItem_Op *>(&umi);
|
||||
if (wmOperatorType *ot = WM_operatortype_find(umi_op->op_idname, false)) {
|
||||
if (ui_name) {
|
||||
ui_name = CTX_IFACE_(ot->translation_context, ui_name->c_str());
|
||||
}
|
||||
if (umi_op->op_prop_enum[0] == '\0') {
|
||||
ui::Layout &row = menu->layout->row(true);
|
||||
int icon = ICON_NONE;
|
||||
bool add_operator = true;
|
||||
handle_operator_asset_reference_props(*C, *umi_op, row, ot, icon, add_operator);
|
||||
if (add_operator) {
|
||||
PointerRNA ptr = row.op(
|
||||
ot, ui_name, icon, wm::OpCallContext(umi_op->opcontext), UI_ITEM_NONE);
|
||||
if (umi_op->prop) {
|
||||
IDP_CopyPropertyContent(ptr.data_as<IDProperty>(), umi_op->prop);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* umi_op->prop could be used to set other properties but it's currently unsupported.
|
||||
*/
|
||||
menu->layout->op_menu_enum(C, ot, umi_op->op_prop_enum, ui_name, ICON_NONE);
|
||||
}
|
||||
is_empty = false;
|
||||
}
|
||||
else {
|
||||
if (show_missing) {
|
||||
SNPRINTF_UTF8(label, RPT_("Missing: %s"), umi_op->op_idname);
|
||||
menu->layout->label(label, ICON_NONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (umi.type == USER_MENU_TYPE_MENU) {
|
||||
bUserMenuItem_Menu *umi_mt = reinterpret_cast<bUserMenuItem_Menu *>(&umi);
|
||||
MenuType *mt = WM_menutype_find(umi_mt->mt_idname, false);
|
||||
if (mt != nullptr) {
|
||||
menu->layout->menu(mt, ui_name, ICON_NONE);
|
||||
is_empty = false;
|
||||
}
|
||||
else {
|
||||
if (show_missing) {
|
||||
SNPRINTF_UTF8(label, RPT_("Missing: %s"), umi_mt->mt_idname);
|
||||
menu->layout->label(label, ICON_NONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (umi.type == USER_MENU_TYPE_PROP) {
|
||||
bUserMenuItem_Prop *umi_pr = reinterpret_cast<bUserMenuItem_Prop *>(&umi);
|
||||
|
||||
char *data_path = strchr(umi_pr->context_data_path, '.');
|
||||
if (data_path) {
|
||||
*data_path = '\0';
|
||||
}
|
||||
PointerRNA ptr = CTX_data_pointer_get(C, umi_pr->context_data_path);
|
||||
if (ptr.type == nullptr) {
|
||||
PointerRNA ctx_ptr = RNA_pointer_create_discrete(nullptr, RNA_Context, (void *)C);
|
||||
if (!RNA_path_resolve_full(&ctx_ptr, umi_pr->context_data_path, &ptr, nullptr, nullptr))
|
||||
{
|
||||
ptr.type = nullptr;
|
||||
}
|
||||
}
|
||||
if (data_path) {
|
||||
*data_path = '.';
|
||||
data_path += 1;
|
||||
}
|
||||
|
||||
bool ok = false;
|
||||
if (ptr.type != nullptr) {
|
||||
PropertyRNA *prop = nullptr;
|
||||
PointerRNA prop_ptr = ptr;
|
||||
if ((data_path == nullptr) ||
|
||||
RNA_path_resolve_full(&ptr, data_path, &prop_ptr, nullptr, nullptr))
|
||||
{
|
||||
prop = RNA_struct_find_property(&prop_ptr, umi_pr->prop_id);
|
||||
if (prop) {
|
||||
ok = true;
|
||||
menu->layout->prop(
|
||||
&prop_ptr, prop, umi_pr->prop_index, 0, UI_ITEM_NONE, ui_name, ICON_NONE);
|
||||
is_empty = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!ok) {
|
||||
if (show_missing) {
|
||||
SNPRINTF_UTF8(
|
||||
label, RPT_("Missing: %s.%s"), umi_pr->context_data_path, umi_pr->prop_id);
|
||||
menu->layout->label(label, ICON_NONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (umi.type == USER_MENU_TYPE_SEP) {
|
||||
menu->layout->separator();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (um_array) {
|
||||
MEM_delete(um_array);
|
||||
}
|
||||
|
||||
if (is_empty) {
|
||||
menu->layout->label(RPT_("No menu items found"), ICON_NONE);
|
||||
menu->layout->label(RPT_("Right click on buttons to add them to this menu"), ICON_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
void ED_screen_user_menu_register()
|
||||
{
|
||||
MenuType *mt = MEM_new_zeroed<MenuType>(__func__);
|
||||
STRNCPY_UTF8(mt->idname, "SCREEN_MT_user_menu");
|
||||
STRNCPY_UTF8(mt->label, N_("Quick Favorites"));
|
||||
STRNCPY_UTF8(mt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA);
|
||||
mt->draw = screen_user_menu_draw;
|
||||
mt->listener = ed::asset::list::asset_reading_region_listen_fn;
|
||||
WM_menutype_add(mt);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
283
blender-5.2.0/source/blender/editors/screen/screendump.cc
Normal file
283
blender-5.2.0/source/blender/editors/screen/screendump.cc
Normal file
@@ -0,0 +1,283 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup edscr
|
||||
* Making screenshots of the entire window or sub-regions.
|
||||
*/
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_path_utils.hh"
|
||||
#include "BLI_rect.h"
|
||||
#include "BLI_string_utf8.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "IMB_imbuf.hh"
|
||||
#include "IMB_imbuf_types.hh"
|
||||
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_screen_types.h"
|
||||
#include "DNA_space_types.h"
|
||||
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_global.hh"
|
||||
#include "BKE_image.hh"
|
||||
#include "BKE_image_format.hh"
|
||||
#include "BKE_main.hh"
|
||||
#include "BKE_report.hh"
|
||||
#include "BKE_screen.hh"
|
||||
|
||||
#include "BLT_translation.hh"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
#include "RNA_prototypes.hh"
|
||||
|
||||
#include "UI_interface.hh"
|
||||
#include "UI_interface_layout.hh"
|
||||
|
||||
#include "WM_api.hh"
|
||||
#include "WM_types.hh"
|
||||
|
||||
#include "screen_intern.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct ScreenshotData {
|
||||
/* Ownership transferred to ImBuf in exec function. */
|
||||
uint8_t *dumprect = nullptr;
|
||||
int dumpsx = 0, dumpsy = 0;
|
||||
rcti crop = {};
|
||||
bool use_crop = false;
|
||||
|
||||
ImageFormatData im_format;
|
||||
};
|
||||
|
||||
/* call from both exec and invoke */
|
||||
static int screenshot_data_create(bContext *C, wmOperator *op, ScrArea *area)
|
||||
{
|
||||
int dumprect_size[2];
|
||||
|
||||
wmWindow *win = CTX_wm_window(C);
|
||||
|
||||
/* do redraw so we don't show popups/menus */
|
||||
WM_redraw_windows(C);
|
||||
|
||||
uint8_t *dumprect = WM_window_pixels_read(C, win, dumprect_size);
|
||||
|
||||
if (dumprect) {
|
||||
ScreenshotData *scd = MEM_new<ScreenshotData>("screenshot");
|
||||
|
||||
scd->dumpsx = dumprect_size[0];
|
||||
scd->dumpsy = dumprect_size[1];
|
||||
scd->dumprect = dumprect;
|
||||
if (area) {
|
||||
scd->crop = area->totrct;
|
||||
}
|
||||
|
||||
BKE_image_format_init(&scd->im_format);
|
||||
|
||||
op->customdata = scd;
|
||||
|
||||
return true;
|
||||
}
|
||||
op->customdata = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
static void screenshot_data_free(wmOperator *op)
|
||||
{
|
||||
MEM_delete(static_cast<ScreenshotData *>(op->customdata));
|
||||
op->customdata = nullptr;
|
||||
}
|
||||
|
||||
static wmOperatorStatus screenshot_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
const bool use_crop = STREQ(op->idname, "SCREEN_OT_screenshot_area");
|
||||
ScreenshotData *scd = static_cast<ScreenshotData *>(op->customdata);
|
||||
bool ok = false;
|
||||
|
||||
if (scd == nullptr) {
|
||||
/* when running exec directly */
|
||||
screenshot_data_create(C, op, use_crop ? CTX_wm_area(C) : nullptr);
|
||||
scd = static_cast<ScreenshotData *>(op->customdata);
|
||||
}
|
||||
|
||||
if (scd) {
|
||||
if (scd->dumprect) {
|
||||
ImBuf *ibuf;
|
||||
char filepath[FILE_MAX];
|
||||
|
||||
RNA_string_get(op->ptr, "filepath", filepath);
|
||||
BLI_path_abs(filepath, BKE_main_blendfile_path_from_global());
|
||||
|
||||
/* operator ensures the extension */
|
||||
ibuf = IMB_allocImBuf(scd->dumpsx, scd->dumpsy, ImBufFlags::Zero);
|
||||
ibuf->color_mode = ImColorMode::RGB;
|
||||
ibuf->assign_byte_data(scd->dumprect);
|
||||
scd->dumprect = nullptr;
|
||||
|
||||
/* crop to show only single editor */
|
||||
if (use_crop) {
|
||||
IMB_crop(ibuf,
|
||||
int2(scd->crop.xmin, scd->crop.ymin),
|
||||
int2(BLI_rcti_size_x(&scd->crop) + 1, BLI_rcti_size_y(&scd->crop) + 1));
|
||||
}
|
||||
|
||||
if ((scd->im_format.color_mode == ImColorMode::BW) &&
|
||||
(scd->im_format.imtype != R_IMF_IMTYPE_MULTILAYER))
|
||||
{
|
||||
/* bw screenshot? - users will notice if it fails! */
|
||||
IMB_color_to_bw(ibuf);
|
||||
}
|
||||
if (BKE_imbuf_write(ibuf, filepath, &scd->im_format)) {
|
||||
ok = true;
|
||||
}
|
||||
else {
|
||||
BKE_reportf(op->reports, RPT_ERROR, "Could not write image: %s", strerror(errno));
|
||||
}
|
||||
|
||||
IMB_freeImBuf(ibuf);
|
||||
}
|
||||
}
|
||||
|
||||
screenshot_data_free(op);
|
||||
|
||||
return ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
static wmOperatorStatus screenshot_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
const bool use_crop = STREQ(op->idname, "SCREEN_OT_screenshot_area");
|
||||
ScrArea *area = nullptr;
|
||||
if (use_crop) {
|
||||
area = CTX_wm_area(C);
|
||||
bScreen *screen = CTX_wm_screen(C);
|
||||
ScrArea *area_test = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, event->xy);
|
||||
if (area_test != nullptr) {
|
||||
area = area_test;
|
||||
}
|
||||
}
|
||||
|
||||
if (screenshot_data_create(C, op, area)) {
|
||||
if (RNA_struct_property_is_set(op->ptr, "filepath")) {
|
||||
return screenshot_exec(C, op);
|
||||
}
|
||||
|
||||
/* extension is added by 'screenshot_check' after */
|
||||
char filepath[FILE_MAX];
|
||||
const char *blendfile_path = BKE_main_blendfile_path_from_global();
|
||||
if (blendfile_path[0] != '\0') {
|
||||
STRNCPY(filepath, blendfile_path);
|
||||
BLI_path_extension_strip(filepath); /* Strip `.blend`. */
|
||||
}
|
||||
else {
|
||||
/* As the file isn't saved, only set the name and let the file selector pick a directory. */
|
||||
STRNCPY_UTF8(filepath, DATA_("screen"));
|
||||
}
|
||||
RNA_string_set(op->ptr, "filepath", filepath);
|
||||
|
||||
WM_event_add_fileselect(C, op);
|
||||
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
static bool screenshot_check(bContext * /*C*/, wmOperator *op)
|
||||
{
|
||||
ScreenshotData *scd = static_cast<ScreenshotData *>(op->customdata);
|
||||
return WM_operator_filesel_ensure_ext_imtype(op, &scd->im_format);
|
||||
}
|
||||
|
||||
static void screenshot_cancel(bContext * /*C*/, wmOperator *op)
|
||||
{
|
||||
screenshot_data_free(op);
|
||||
}
|
||||
|
||||
static bool screenshot_draw_check_prop(PointerRNA * /*ptr*/,
|
||||
PropertyRNA *prop,
|
||||
void * /*user_data*/)
|
||||
{
|
||||
const char *prop_id = RNA_property_identifier(prop);
|
||||
|
||||
return !STREQ(prop_id, "filepath");
|
||||
}
|
||||
|
||||
static void screenshot_draw(bContext *C, wmOperator *op)
|
||||
{
|
||||
ui::Layout &layout = *op->layout;
|
||||
ScreenshotData *scd = static_cast<ScreenshotData *>(op->customdata);
|
||||
|
||||
layout.use_property_split_set(true);
|
||||
layout.use_property_decorate_set(false);
|
||||
|
||||
/* image template */
|
||||
PointerRNA ptr = RNA_pointer_create_discrete(nullptr, RNA_ImageFormatSettings, &scd->im_format);
|
||||
uiTemplateImageSettings(&layout, C, &ptr, false);
|
||||
|
||||
/* main draw call */
|
||||
uiDefAutoButsRNA(&layout,
|
||||
op->ptr,
|
||||
screenshot_draw_check_prop,
|
||||
nullptr,
|
||||
nullptr,
|
||||
ui::BUT_LABEL_ALIGN_NONE,
|
||||
false);
|
||||
}
|
||||
|
||||
static bool screenshot_poll(bContext *C)
|
||||
{
|
||||
if (G.background) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return WM_operator_winactive(C);
|
||||
}
|
||||
|
||||
static void screen_screenshot_impl(wmOperatorType *ot)
|
||||
{
|
||||
ot->invoke = screenshot_invoke;
|
||||
ot->check = screenshot_check;
|
||||
ot->exec = screenshot_exec;
|
||||
ot->cancel = screenshot_cancel;
|
||||
ot->ui = screenshot_draw;
|
||||
ot->poll = screenshot_poll;
|
||||
|
||||
WM_operator_properties_filesel(ot,
|
||||
FILE_TYPE_FOLDER | FILE_TYPE_IMAGE,
|
||||
FILE_SPECIAL,
|
||||
FILE_SAVE,
|
||||
WM_FILESEL_FILEPATH,
|
||||
FILE_DEFAULTDISPLAY,
|
||||
FILE_SORT_DEFAULT);
|
||||
}
|
||||
|
||||
void SCREEN_OT_screenshot(wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "Save Screenshot";
|
||||
ot->idname = "SCREEN_OT_screenshot";
|
||||
ot->description = "Capture a picture of the whole Blender window";
|
||||
|
||||
screen_screenshot_impl(ot);
|
||||
|
||||
ot->flag = 0;
|
||||
}
|
||||
|
||||
void SCREEN_OT_screenshot_area(wmOperatorType *ot)
|
||||
{
|
||||
/* NOTE: the term "area" is a Blender internal name, "Editor" makes more sense for the UI. */
|
||||
ot->name = "Save Screenshot (Editor)";
|
||||
ot->idname = "SCREEN_OT_screenshot_area";
|
||||
ot->description = "Capture a picture of an editor";
|
||||
|
||||
screen_screenshot_impl(ot);
|
||||
|
||||
ot->flag = OPTYPE_DEPENDS_ON_CURSOR;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
749
blender-5.2.0/source/blender/editors/screen/workspace_edit.cc
Normal file
749
blender-5.2.0/source/blender/editors/screen/workspace_edit.cc
Normal file
@@ -0,0 +1,749 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup edscr
|
||||
*/
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "BLI_fileops.h"
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_path_utils.hh"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_string_utf8.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BKE_appdir.hh"
|
||||
#include "BKE_blendfile.hh"
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_layer.hh"
|
||||
#include "BKE_lib_id.hh"
|
||||
#include "BKE_main.hh"
|
||||
#include "BKE_screen.hh"
|
||||
#include "BKE_workspace.hh"
|
||||
|
||||
#include "BLO_readfile.hh"
|
||||
|
||||
#include "DNA_screen_types.h"
|
||||
#include "DNA_windowmanager_types.h"
|
||||
#include "DNA_workspace_types.h"
|
||||
|
||||
#include "ED_datafiles.h"
|
||||
#include "ED_object.hh"
|
||||
#include "ED_screen.hh"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
#include "RNA_define.hh"
|
||||
|
||||
#include "UI_interface.hh"
|
||||
#include "UI_interface_layout.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
#include "BLT_translation.hh"
|
||||
|
||||
#include "WM_api.hh"
|
||||
#include "WM_types.hh"
|
||||
|
||||
#include "screen_intern.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Workspace API
|
||||
*
|
||||
* \brief API for managing workspaces and their data.
|
||||
* \{ */
|
||||
|
||||
WorkSpace *ED_workspace_add(Main *bmain, const char *name)
|
||||
{
|
||||
return BKE_workspace_add(bmain, name);
|
||||
}
|
||||
|
||||
static void workspace_exit(WorkSpace *workspace, wmWindow *win)
|
||||
{
|
||||
/* Scene pinning: Store whatever scene was active when leaving the workspace. It's reactivated
|
||||
* when the workspace gets reactivated as well. */
|
||||
if (workspace->flags & WORKSPACE_USE_PIN_SCENE) {
|
||||
workspace->pin_scene = WM_window_get_active_scene(win);
|
||||
}
|
||||
else {
|
||||
/* The active scene may have been changed. So also always update the unpinned scene to the
|
||||
* latest when leaving a workspace that has no scene pinning. */
|
||||
win->unpinned_scene = WM_window_get_active_scene(win);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* State changes (old workspace to new workspace):
|
||||
* 1) unpinned -> pinned
|
||||
* * Store current scene as the unpinned one (done in #workspace_exit()).
|
||||
* * Change the current scene to the pinned one.
|
||||
* 2) pinned -> pinned
|
||||
* * Change the current scene to the new pinned one.
|
||||
* 3) pinned -> unpinned
|
||||
* * Change current scene back to the unpinned one
|
||||
* 4) unpinned -> unpinned
|
||||
* * Make sure the unpinned scene is active.
|
||||
*
|
||||
* Note that the pin scene must also be updated when leaving a workspace with a pinned scene.
|
||||
* That's done separately via workspace_exit() above.
|
||||
*/
|
||||
static void workspace_scene_pinning_update(WorkSpace *workspace_new,
|
||||
const WorkSpace *workspace_old,
|
||||
bContext *C)
|
||||
{
|
||||
wmWindow *win = CTX_wm_window(C);
|
||||
Main *bmain = CTX_data_main(C);
|
||||
Scene *active_scene = WM_window_get_active_scene(win);
|
||||
|
||||
const bool is_new_pinned = (workspace_new->flags & WORKSPACE_USE_PIN_SCENE);
|
||||
const bool is_old_pinned = (workspace_old->flags & WORKSPACE_USE_PIN_SCENE);
|
||||
|
||||
/* State changes 1 and 2. */
|
||||
if (is_new_pinned) {
|
||||
if (workspace_new->pin_scene && (workspace_new->pin_scene != active_scene)) {
|
||||
WM_window_set_active_scene(bmain, C, win, workspace_new->pin_scene);
|
||||
workspace_new->pin_scene = nullptr;
|
||||
}
|
||||
}
|
||||
/* State change 3 - Changing from workspace with pinned scene to unpinned scene. */
|
||||
else if (is_old_pinned) {
|
||||
if (win->unpinned_scene) {
|
||||
WM_window_set_active_scene(bmain, C, win, win->unpinned_scene);
|
||||
}
|
||||
else {
|
||||
/* When leaving a workspace where the pinning was just enabled, the unpinned scene wasn't set
|
||||
* yet. */
|
||||
win->unpinned_scene = active_scene;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* When leaving a workspace where the pinning was just disabled, we still want to restore the
|
||||
* unpinned scene. */
|
||||
if (win->unpinned_scene) {
|
||||
WM_window_set_active_scene(bmain, C, win, win->unpinned_scene);
|
||||
}
|
||||
}
|
||||
|
||||
BLI_assert(WM_window_get_active_scene(win));
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the object mode (if needed) to the one set in \a workspace_new.
|
||||
* Object mode is still stored on object level. In future it should all be workspace level instead.
|
||||
*/
|
||||
static void workspace_change_update(WorkSpace *workspace_new,
|
||||
WorkSpace *workspace_old,
|
||||
bContext *C,
|
||||
wmWindowManager *wm)
|
||||
{
|
||||
workspace_scene_pinning_update(workspace_new, workspace_old, C);
|
||||
/* needs to be done before changing mode! (to ensure right context) */
|
||||
UNUSED_VARS(wm);
|
||||
#if 0
|
||||
Object *ob_act = CTX_data_active_object(C);
|
||||
eObjectMode mode_old = workspace_old->object_mode;
|
||||
eObjectMode mode_new = workspace_new->object_mode;
|
||||
|
||||
if (mode_old != mode_new) {
|
||||
ed::object::mode_set(C, mode_new);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static WorkSpaceLayout *workspace_change_get_new_layout(Main *bmain,
|
||||
WorkSpace *workspace_new,
|
||||
wmWindow *win)
|
||||
{
|
||||
WorkSpaceLayout *layout_old = WM_window_get_active_layout(win);
|
||||
WorkSpaceLayout *layout_new;
|
||||
|
||||
/* ED_workspace_duplicate may have stored a layout to activate
|
||||
* once the workspace gets activated. */
|
||||
if (win->workspace_hook->temp_layout_store) {
|
||||
layout_new = win->workspace_hook->temp_layout_store;
|
||||
}
|
||||
else {
|
||||
layout_new = BKE_workspace_active_layout_for_workspace_get(win->workspace_hook, workspace_new);
|
||||
if (!layout_new) {
|
||||
layout_new = static_cast<WorkSpaceLayout *>(workspace_new->layouts.first);
|
||||
}
|
||||
}
|
||||
|
||||
return ED_workspace_screen_change_ensure_unused_layout(
|
||||
bmain, workspace_new, layout_new, layout_old, win);
|
||||
}
|
||||
|
||||
bool ED_workspace_change(WorkSpace *workspace_new, bContext *C, wmWindowManager *wm, wmWindow *win)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
WorkSpace *workspace_old = WM_window_get_active_workspace(win);
|
||||
WorkSpaceLayout *layout_new = workspace_change_get_new_layout(bmain, workspace_new, win);
|
||||
bScreen *screen_new = BKE_workspace_layout_screen_get(layout_new);
|
||||
bScreen *screen_old = BKE_workspace_active_screen_get(win->workspace_hook);
|
||||
|
||||
win->workspace_hook->temp_layout_store = nullptr;
|
||||
if (workspace_old == workspace_new) {
|
||||
/* Could also return true, everything that needs to be done was done (nothing :P),
|
||||
* but nothing changed */
|
||||
return false;
|
||||
}
|
||||
|
||||
workspace_exit(workspace_old, win);
|
||||
|
||||
screen_change_prepare(screen_old, screen_new, bmain, C, win);
|
||||
|
||||
if (screen_new == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BKE_workspace_active_layout_set(win->workspace_hook, win->winid, workspace_new, layout_new);
|
||||
BKE_workspace_active_set(win->workspace_hook, workspace_new);
|
||||
|
||||
/* update screen *after* changing workspace - which also causes the
|
||||
* actual screen change and updates context (including CTX_wm_workspace) */
|
||||
screen_change_update(C, win, screen_new);
|
||||
workspace_change_update(workspace_new, workspace_old, C, wm);
|
||||
|
||||
BLI_assert(CTX_wm_workspace(C) == workspace_new);
|
||||
|
||||
/* Automatic mode switching. */
|
||||
if (workspace_new->object_mode != workspace_old->object_mode) {
|
||||
const Object *object = nullptr;
|
||||
if (const Base *base = CTX_data_active_base(C)) {
|
||||
object = base->object;
|
||||
/* Behavior that depends on the active area is not expected in the context of workspace
|
||||
* switching, ignore the view-port even if it's available. */
|
||||
const View3D *v3d = nullptr;
|
||||
|
||||
const bool base_visible = BKE_base_is_visible(v3d, base);
|
||||
if (!base_visible && object->mode == OB_MODE_OBJECT) {
|
||||
/* Set this to nullptr to indicate that the mode should not be switched. This matches
|
||||
* CTX_data_active_object behavior in the 3D Viewport. See `view3d_context` for more
|
||||
* details. */
|
||||
object = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (object) {
|
||||
ed::object::mode_set(C, eObjectMode(workspace_new->object_mode));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
WorkSpace *ED_workspace_duplicate(WorkSpace *workspace_old, Main *bmain, wmWindow *win)
|
||||
{
|
||||
WorkSpaceLayout *layout_active_old = BKE_workspace_active_layout_get(win->workspace_hook);
|
||||
WorkSpace *workspace_new = id_cast<WorkSpace *>(BKE_id_copy(bmain, &workspace_old->id));
|
||||
|
||||
/* Try to keep active the layout from the new workspace matching the current active one from
|
||||
* the old workspace. */
|
||||
WorkSpaceLayout *layout_old = static_cast<WorkSpaceLayout *>(workspace_old->layouts.first);
|
||||
WorkSpaceLayout *layout_new = static_cast<WorkSpaceLayout *>(workspace_new->layouts.first);
|
||||
for (; layout_old && layout_new; layout_old = layout_old->next, layout_new = layout_new->next) {
|
||||
if (layout_old == layout_active_old) {
|
||||
win->workspace_hook->temp_layout_store = layout_new;
|
||||
}
|
||||
}
|
||||
return workspace_new;
|
||||
}
|
||||
|
||||
bool ED_workspace_delete(WorkSpace *workspace, Main *bmain, bContext *C, wmWindowManager *wm)
|
||||
{
|
||||
if (bmain->workspaces.is_single()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Vector<ID *> ordered = BKE_id_ordered_list(
|
||||
reinterpret_cast<const ListBaseT<ID> *>(&bmain->workspaces));
|
||||
const int index = ordered.first_index_of(&workspace->id);
|
||||
|
||||
WorkSpace *new_active = reinterpret_cast<WorkSpace *>(index == 0 ? ordered[1] :
|
||||
ordered[index - 1]);
|
||||
|
||||
for (wmWindow &win : wm->windows) {
|
||||
WorkSpace *workspace_active = WM_window_get_active_workspace(&win);
|
||||
if (workspace_active == workspace) {
|
||||
ED_workspace_change(new_active, C, wm, &win);
|
||||
}
|
||||
}
|
||||
|
||||
/* Also delete managed screens if they have no other users. */
|
||||
for (WorkSpaceLayout &layout : workspace->layouts) {
|
||||
BKE_id_free_us(bmain, layout.screen);
|
||||
layout.screen = nullptr;
|
||||
}
|
||||
|
||||
BKE_id_free(bmain, &workspace->id);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ED_workspace_scene_data_sync(WorkSpaceInstanceHook *hook, Scene *scene)
|
||||
{
|
||||
bScreen *screen = BKE_workspace_active_screen_get(hook);
|
||||
BKE_screen_view3d_scene_sync(screen, scene);
|
||||
}
|
||||
|
||||
/** \} Workspace API */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Workspace Operators
|
||||
* \{ */
|
||||
|
||||
static WorkSpace *workspace_context_get(bContext *C)
|
||||
{
|
||||
ID *id = ui::context_active_but_get_tab_ID(C);
|
||||
if (id && GS(id->name) == ID_WS) {
|
||||
return id_cast<WorkSpace *>(id);
|
||||
}
|
||||
|
||||
return CTX_wm_workspace(C);
|
||||
}
|
||||
|
||||
static bool workspace_context_poll(bContext *C)
|
||||
{
|
||||
return workspace_context_get(C) != nullptr;
|
||||
}
|
||||
|
||||
static wmOperatorStatus workspace_new_exec(bContext *C, wmOperator * /*op*/)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
wmWindow *win = CTX_wm_window(C);
|
||||
WorkSpace *workspace = workspace_context_get(C);
|
||||
|
||||
workspace = ED_workspace_duplicate(workspace, bmain, win);
|
||||
|
||||
WM_event_add_notifier(C, NC_SCREEN | ND_WORKSPACE_SET, workspace);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static void WORKSPACE_OT_duplicate(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "New Workspace";
|
||||
ot->description = "Add a new workspace";
|
||||
ot->idname = "WORKSPACE_OT_duplicate";
|
||||
|
||||
/* API callbacks. */
|
||||
ot->poll = workspace_context_poll;
|
||||
ot->exec = workspace_new_exec;
|
||||
}
|
||||
|
||||
static wmOperatorStatus workspace_delete_exec(bContext *C, wmOperator * /*op*/)
|
||||
{
|
||||
WorkSpace *workspace = workspace_context_get(C);
|
||||
WM_event_add_notifier(C, NC_SCREEN | ND_WORKSPACE_DELETE, workspace);
|
||||
WM_event_add_notifier(C, NC_WINDOW, nullptr);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static void WORKSPACE_OT_delete(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Delete Workspace";
|
||||
ot->description = "Delete the active workspace";
|
||||
ot->idname = "WORKSPACE_OT_delete";
|
||||
|
||||
/* API callbacks. */
|
||||
ot->poll = workspace_context_poll;
|
||||
ot->exec = workspace_delete_exec;
|
||||
}
|
||||
|
||||
static wmOperatorStatus workspace_delete_all_others_exec(bContext *C, wmOperator * /*op*/)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
WorkSpace *workspace = workspace_context_get(C);
|
||||
|
||||
for (WorkSpace &ws : bmain->workspaces) {
|
||||
if (&ws != workspace) {
|
||||
WM_event_add_notifier(C, NC_SCREEN | ND_WORKSPACE_DELETE, &ws);
|
||||
WM_event_add_notifier(C, NC_WINDOW, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static void WORKSPACE_OT_delete_all_others(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Delete Other Workspaces";
|
||||
ot->description = "Delete all workspaces except this one";
|
||||
ot->idname = "WORKSPACE_OT_delete_all_others";
|
||||
|
||||
/* api callbacks */
|
||||
ot->poll = workspace_context_poll;
|
||||
ot->exec = workspace_delete_all_others_exec;
|
||||
}
|
||||
|
||||
static wmOperatorStatus workspace_append_activate_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
char idname[MAX_ID_NAME - 2], filepath[FILE_MAX];
|
||||
|
||||
if (!RNA_struct_property_is_set(op->ptr, "idname") ||
|
||||
!RNA_struct_property_is_set(op->ptr, "filepath"))
|
||||
{
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
RNA_string_get(op->ptr, "idname", idname);
|
||||
RNA_string_get(op->ptr, "filepath", filepath);
|
||||
/* Not expected, but a blank filename causes an assert
|
||||
* (trips up the "importing from self" assert as both paths are blank). */
|
||||
if (idname[0] == '\0' || filepath[0] == '\0') {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
WorkSpace *appended_workspace = nullptr;
|
||||
/* NOTE: Need to check `filepath`, in the rare case where the usual source of work-spaces
|
||||
* (the startup blend-file) is the one currently open (see #144305). */
|
||||
const char *blendfile_path = BKE_main_blendfile_path(bmain);
|
||||
if ((blendfile_path[0] != '\0') && (BLI_path_cmp(blendfile_path, filepath) == 0)) {
|
||||
appended_workspace = reinterpret_cast<WorkSpace *>(
|
||||
BKE_libblock_find_name(bmain, ID_WS, idname, nullptr));
|
||||
if (appended_workspace) {
|
||||
/* Copy, to mimic behavior when appending from another file (which always creates a new copy
|
||||
* of the data). */
|
||||
appended_workspace = ED_workspace_duplicate(appended_workspace, bmain, CTX_wm_window(C));
|
||||
}
|
||||
}
|
||||
else {
|
||||
appended_workspace = reinterpret_cast<WorkSpace *>(
|
||||
WM_file_append_datablock(bmain,
|
||||
CTX_data_scene(C),
|
||||
CTX_data_view_layer(C),
|
||||
CTX_wm_view3d(C),
|
||||
filepath,
|
||||
ID_WS,
|
||||
idname,
|
||||
BLO_LIBLINK_APPEND_RECURSIVE));
|
||||
}
|
||||
|
||||
if (appended_workspace) {
|
||||
/* Translate workspace name, unless it was taken from current blendfile. */
|
||||
if (BLT_translate_new_dataname()) {
|
||||
BKE_libblock_rename(
|
||||
*bmain, appended_workspace->id, CTX_DATA_(BLT_I18NCONTEXT_ID_WORKSPACE, idname));
|
||||
}
|
||||
|
||||
/* Set defaults. */
|
||||
BLO_update_defaults_workspace(appended_workspace, nullptr);
|
||||
|
||||
/* Reorder to last position. */
|
||||
BKE_id_reorder(reinterpret_cast<const ListBaseT<ID> *>(&bmain->workspaces),
|
||||
&appended_workspace->id,
|
||||
nullptr,
|
||||
true);
|
||||
|
||||
/* Changing workspace changes context. Do delayed! */
|
||||
WM_event_add_notifier(C, NC_SCREEN | ND_WORKSPACE_SET, appended_workspace);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
static void WORKSPACE_OT_append_activate(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Append and Activate Workspace";
|
||||
ot->description = "Append a workspace and make it the active one in the current window";
|
||||
ot->idname = "WORKSPACE_OT_append_activate";
|
||||
|
||||
/* API callbacks. */
|
||||
ot->exec = workspace_append_activate_exec;
|
||||
|
||||
PropertyRNA *prop;
|
||||
RNA_def_string(ot->srna,
|
||||
"idname",
|
||||
nullptr,
|
||||
MAX_ID_NAME - 2,
|
||||
"Identifier",
|
||||
"Name of the workspace to append and activate");
|
||||
prop = RNA_def_string(
|
||||
ot->srna, "filepath", nullptr, FILE_MAX, "Filepath", "Path to the library");
|
||||
RNA_def_property_subtype(prop, PROP_FILEPATH);
|
||||
RNA_def_property_flag(prop, PROP_PATH_SUPPORTS_BLEND_RELATIVE);
|
||||
}
|
||||
|
||||
static WorkspaceConfigFileData *workspace_config_file_read(const char *app_template)
|
||||
{
|
||||
const std::optional<std::string> cfgdir = BKE_appdir_folder_id(BLENDER_USER_CONFIG,
|
||||
app_template);
|
||||
char startup_file_path[FILE_MAX] = {0};
|
||||
|
||||
if (cfgdir.has_value()) {
|
||||
BLI_path_join(
|
||||
startup_file_path, sizeof(startup_file_path), cfgdir->c_str(), BLENDER_STARTUP_FILE);
|
||||
}
|
||||
|
||||
bool has_path = BLI_exists(startup_file_path);
|
||||
return (has_path) ? BKE_blendfile_workspace_config_read(startup_file_path, nullptr, 0, nullptr) :
|
||||
nullptr;
|
||||
}
|
||||
|
||||
static WorkspaceConfigFileData *workspace_system_file_read(const char *app_template)
|
||||
{
|
||||
if (app_template == nullptr) {
|
||||
return BKE_blendfile_workspace_config_read(
|
||||
nullptr, datatoc_startup_blend, datatoc_startup_blend_size, nullptr);
|
||||
}
|
||||
|
||||
char template_dir[FILE_MAX];
|
||||
if (!BKE_appdir_app_template_id_search(app_template, template_dir, sizeof(template_dir))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
char startup_file_path[FILE_MAX];
|
||||
BLI_path_join(startup_file_path, sizeof(startup_file_path), template_dir, BLENDER_STARTUP_FILE);
|
||||
|
||||
bool has_path = BLI_exists(startup_file_path);
|
||||
return (has_path) ? BKE_blendfile_workspace_config_read(startup_file_path, nullptr, 0, nullptr) :
|
||||
nullptr;
|
||||
}
|
||||
|
||||
static void workspace_append_button(ui::Layout &layout,
|
||||
wmOperatorType *ot_append,
|
||||
const WorkSpace *workspace,
|
||||
const Main *from_main)
|
||||
{
|
||||
const ID *id = id_cast<ID *>(const_cast<WorkSpace *>(workspace));
|
||||
const char *filepath = from_main->filepath;
|
||||
|
||||
if (filepath[0] == '\0') {
|
||||
filepath = BLO_EMBEDDED_STARTUP_BLEND;
|
||||
}
|
||||
|
||||
BLI_assert(STREQ(ot_append->idname, "WORKSPACE_OT_append_activate"));
|
||||
|
||||
PointerRNA opptr;
|
||||
opptr = layout.op(ot_append,
|
||||
CTX_DATA_(BLT_I18NCONTEXT_ID_WORKSPACE, workspace->id.name + 2),
|
||||
ICON_NONE,
|
||||
wm::OpCallContext::ExecDefault,
|
||||
UI_ITEM_NONE);
|
||||
RNA_string_set(&opptr, "idname", id->name + 2);
|
||||
RNA_string_set(&opptr, "filepath", filepath);
|
||||
}
|
||||
|
||||
static void workspace_add_menu(bContext * /*C*/, ui::Layout *layout, void *template_v)
|
||||
{
|
||||
const char *app_template = static_cast<const char *>(template_v);
|
||||
bool has_startup_items = false;
|
||||
|
||||
wmOperatorType *ot_append = WM_operatortype_find("WORKSPACE_OT_append_activate", true);
|
||||
WorkspaceConfigFileData *startup_config = workspace_config_file_read(app_template);
|
||||
WorkspaceConfigFileData *builtin_config = workspace_system_file_read(app_template);
|
||||
|
||||
if (startup_config) {
|
||||
for (WorkSpace &workspace : startup_config->workspaces) {
|
||||
ui::Layout &row = layout->row(false);
|
||||
workspace_append_button(row, ot_append, &workspace, startup_config->main);
|
||||
has_startup_items = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (builtin_config) {
|
||||
bool has_title = false;
|
||||
|
||||
for (WorkSpace &workspace : builtin_config->workspaces) {
|
||||
if (startup_config &&
|
||||
BLI_findstring(&startup_config->workspaces, workspace.id.name, offsetof(ID, name)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!has_title) {
|
||||
if (has_startup_items) {
|
||||
layout->separator();
|
||||
}
|
||||
has_title = true;
|
||||
}
|
||||
|
||||
ui::Layout &row = layout->row(false);
|
||||
workspace_append_button(row, ot_append, &workspace, builtin_config->main);
|
||||
}
|
||||
}
|
||||
|
||||
if (startup_config) {
|
||||
BKE_blendfile_workspace_config_data_free(startup_config);
|
||||
}
|
||||
if (builtin_config) {
|
||||
BKE_blendfile_workspace_config_data_free(builtin_config);
|
||||
}
|
||||
}
|
||||
|
||||
static void workspace_add_menu_draw(ui::Layout &layout)
|
||||
{
|
||||
{
|
||||
PointerRNA props = layout.op("WM_OT_search_single_menu",
|
||||
"Search...",
|
||||
ICON_VIEWZOOM,
|
||||
wm::OpCallContext::InvokeDefault,
|
||||
UI_ITEM_NONE);
|
||||
RNA_string_set(&props, "menu_idname", "WORKSPACE_MT_add");
|
||||
}
|
||||
layout.separator();
|
||||
|
||||
layout.menu_fn(IFACE_("General"), ICON_NONE, workspace_add_menu, nullptr);
|
||||
|
||||
ListBaseT<LinkData> templates;
|
||||
BKE_appdir_app_templates(&templates);
|
||||
|
||||
for (LinkData &link : templates) {
|
||||
char *app_template = static_cast<char *>(link.data);
|
||||
char display_name[FILE_MAX];
|
||||
|
||||
BLI_path_to_display_name(display_name, sizeof(display_name), IFACE_(app_template));
|
||||
|
||||
/* Steals ownership of link data string. */
|
||||
layout.menu_fn_argN_free(display_name, ICON_NONE, workspace_add_menu, app_template);
|
||||
}
|
||||
|
||||
templates.free_no_destruct();
|
||||
|
||||
layout.separator();
|
||||
layout.op("WORKSPACE_OT_duplicate",
|
||||
CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Duplicate Current"),
|
||||
ICON_DUPLICATE);
|
||||
}
|
||||
|
||||
static void workspace_add_menu_register()
|
||||
{
|
||||
MenuType *mt = MEM_new_zeroed<MenuType>("workspace_add_invoke");
|
||||
STRNCPY_UTF8(mt->idname, "WORKSPACE_MT_add");
|
||||
STRNCPY_UTF8(mt->label, N_("Add Workspace"));
|
||||
mt->flag = MenuTypeFlag::SearchOnKeyPress;
|
||||
mt->draw = [](const bContext * /*C*/, Menu *menu) {
|
||||
ui::Layout &layout = *menu->layout;
|
||||
workspace_add_menu_draw(layout);
|
||||
};
|
||||
|
||||
WM_menutype_add(mt);
|
||||
}
|
||||
|
||||
static wmOperatorStatus workspace_add_invoke(bContext *C,
|
||||
wmOperator * /*op*/,
|
||||
const wmEvent * /*event*/)
|
||||
{
|
||||
WM_menu_name_call(C, "WORKSPACE_MT_add", wm::OpCallContext::InvokeDefault);
|
||||
return OPERATOR_INTERFACE;
|
||||
}
|
||||
|
||||
static void WORKSPACE_OT_add(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Add Workspace";
|
||||
ot->description =
|
||||
"Add a new workspace by duplicating the current one or appending one "
|
||||
"from the user configuration";
|
||||
ot->idname = "WORKSPACE_OT_add";
|
||||
|
||||
/* API callbacks. */
|
||||
ot->invoke = workspace_add_invoke;
|
||||
}
|
||||
|
||||
static wmOperatorStatus workspace_reorder_to_back_exec(bContext *C, wmOperator * /*op*/)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
WorkSpace *workspace = workspace_context_get(C);
|
||||
|
||||
BKE_id_reorder(
|
||||
reinterpret_cast<const ListBaseT<ID> *>(&bmain->workspaces), &workspace->id, nullptr, true);
|
||||
WM_event_add_notifier(C, NC_WINDOW, nullptr);
|
||||
|
||||
return OPERATOR_INTERFACE;
|
||||
}
|
||||
|
||||
static void WORKSPACE_OT_reorder_to_back(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Workspace Reorder to Back";
|
||||
ot->description = "Reorder workspace to be last in the list";
|
||||
ot->idname = "WORKSPACE_OT_reorder_to_back";
|
||||
|
||||
/* API callbacks. */
|
||||
ot->poll = workspace_context_poll;
|
||||
ot->exec = workspace_reorder_to_back_exec;
|
||||
}
|
||||
|
||||
static wmOperatorStatus workspace_reorder_to_front_exec(bContext *C, wmOperator * /*op*/)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
WorkSpace *workspace = workspace_context_get(C);
|
||||
|
||||
BKE_id_reorder(
|
||||
reinterpret_cast<const ListBaseT<ID> *>(&bmain->workspaces), &workspace->id, nullptr, false);
|
||||
WM_event_add_notifier(C, NC_WINDOW, nullptr);
|
||||
|
||||
return OPERATOR_INTERFACE;
|
||||
}
|
||||
|
||||
static void WORKSPACE_OT_reorder_to_front(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Workspace Reorder to Front";
|
||||
ot->description = "Reorder workspace to be first in the list";
|
||||
ot->idname = "WORKSPACE_OT_reorder_to_front";
|
||||
|
||||
/* API callbacks. */
|
||||
ot->poll = workspace_context_poll;
|
||||
ot->exec = workspace_reorder_to_front_exec;
|
||||
}
|
||||
|
||||
static wmOperatorStatus workspace_scene_pin_toggle_exec(bContext *C, wmOperator * /*op*/)
|
||||
{
|
||||
WorkSpace *workspace = workspace_context_get(C);
|
||||
|
||||
/* Trivial. The operator is only needed to display a superimposed extra icon, which
|
||||
* requires an operator. */
|
||||
workspace->flags ^= WORKSPACE_USE_PIN_SCENE;
|
||||
|
||||
WM_event_add_notifier(C, NC_WORKSPACE, nullptr);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static void WORKSPACE_OT_scene_pin_toggle(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Pin Scene to Workspace";
|
||||
ot->description =
|
||||
"Remember the last used scene for the current workspace and switch to it whenever this "
|
||||
"workspace is activated again";
|
||||
ot->idname = "WORKSPACE_OT_scene_pin_toggle";
|
||||
|
||||
/* API callbacks. */
|
||||
ot->poll = workspace_context_poll;
|
||||
ot->exec = workspace_scene_pin_toggle_exec;
|
||||
|
||||
ot->flag = OPTYPE_INTERNAL;
|
||||
}
|
||||
|
||||
void ED_operatortypes_workspace()
|
||||
{
|
||||
workspace_add_menu_register();
|
||||
|
||||
WM_operatortype_append(WORKSPACE_OT_duplicate);
|
||||
WM_operatortype_append(WORKSPACE_OT_delete);
|
||||
WM_operatortype_append(WORKSPACE_OT_delete_all_others);
|
||||
WM_operatortype_append(WORKSPACE_OT_add);
|
||||
WM_operatortype_append(WORKSPACE_OT_append_activate);
|
||||
WM_operatortype_append(WORKSPACE_OT_reorder_to_back);
|
||||
WM_operatortype_append(WORKSPACE_OT_reorder_to_front);
|
||||
WM_operatortype_append(WORKSPACE_OT_scene_pin_toggle);
|
||||
}
|
||||
|
||||
/** \} Workspace Operators */
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,219 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup edscr
|
||||
*/
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "DNA_screen_types.h"
|
||||
#include "DNA_workspace_types.h"
|
||||
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_lib_id.hh"
|
||||
#include "BKE_main.hh"
|
||||
#include "BKE_screen.hh"
|
||||
#include "BKE_workspace.hh"
|
||||
|
||||
#include "WM_api.hh"
|
||||
|
||||
#include "ED_screen.hh"
|
||||
|
||||
#include "screen_intern.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
WorkSpaceLayout *ED_workspace_layout_add(Main *bmain,
|
||||
WorkSpace *workspace,
|
||||
wmWindow *win,
|
||||
const char *name)
|
||||
{
|
||||
bScreen *screen;
|
||||
rcti screen_rect;
|
||||
|
||||
WM_window_screen_rect_calc(win, &screen_rect);
|
||||
screen = screen_add(bmain, name, &screen_rect);
|
||||
|
||||
return BKE_workspace_layout_add(bmain, *workspace, *screen, name);
|
||||
}
|
||||
|
||||
WorkSpaceLayout *ED_workspace_layout_duplicate(Main *bmain,
|
||||
WorkSpace *workspace,
|
||||
const WorkSpaceLayout *layout_old,
|
||||
wmWindow * /*win*/)
|
||||
{
|
||||
return BKE_workspace_layout_add_from_layout(bmain, *workspace, *layout_old, LIB_ID_COPY_DEFAULT);
|
||||
}
|
||||
|
||||
static bool workspace_layout_delete_doit(WorkSpace *workspace,
|
||||
WorkSpaceLayout *layout_old,
|
||||
WorkSpaceLayout *layout_new,
|
||||
bContext *C)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
wmWindow *win = CTX_wm_window(C);
|
||||
bScreen *screen_new = BKE_workspace_layout_screen_get(layout_new);
|
||||
|
||||
ED_screen_change(C, screen_new);
|
||||
|
||||
if (BKE_workspace_active_layout_get(win->workspace_hook) != layout_old) {
|
||||
BKE_workspace_layout_remove(bmain, workspace, layout_old);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool workspace_layout_set_poll(const WorkSpaceLayout *layout)
|
||||
{
|
||||
const bScreen *screen = BKE_workspace_layout_screen_get(layout);
|
||||
|
||||
return ((BKE_screen_is_used(screen) == false) &&
|
||||
/* in typical usage temp screens should have a nonzero winid
|
||||
* (all temp screens should be used, or closed & freed). */
|
||||
(screen->temp == false) && (BKE_screen_is_fullscreen_area(screen) == false) &&
|
||||
(screen->id.name[2] != '.' || !(U.uiflag & USER_HIDE_DOT)));
|
||||
}
|
||||
|
||||
static WorkSpaceLayout *workspace_layout_delete_find_new(const WorkSpaceLayout *layout_old)
|
||||
{
|
||||
for (WorkSpaceLayout *layout_new = layout_old->prev; layout_new; layout_new = layout_new->next) {
|
||||
if (workspace_layout_set_poll(layout_new)) {
|
||||
return layout_new;
|
||||
}
|
||||
}
|
||||
|
||||
for (WorkSpaceLayout *layout_new = layout_old->next; layout_new; layout_new = layout_new->next) {
|
||||
if (workspace_layout_set_poll(layout_new)) {
|
||||
return layout_new;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool ED_workspace_layout_delete(WorkSpace *workspace, WorkSpaceLayout *layout_old, bContext *C)
|
||||
{
|
||||
const bScreen *screen_old = BKE_workspace_layout_screen_get(layout_old);
|
||||
WorkSpaceLayout *layout_new;
|
||||
|
||||
BLI_assert(BLI_findindex(&workspace->layouts, layout_old) != -1);
|
||||
|
||||
/* Don't allow deleting temp full-screens for now. */
|
||||
if (BKE_screen_is_fullscreen_area(screen_old)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* A layout/screen can only be in use by one window at a time, so as
|
||||
* long as we are able to find a layout/screen that is unused, we
|
||||
* can safely assume ours is not in use anywhere an delete it. */
|
||||
|
||||
layout_new = workspace_layout_delete_find_new(layout_old);
|
||||
|
||||
if (layout_new) {
|
||||
return workspace_layout_delete_doit(workspace, layout_old, layout_new, C);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool workspace_change_find_new_layout_cb(const WorkSpaceLayout *layout, void * /*arg*/)
|
||||
{
|
||||
/* return false to stop the iterator if we've found a layout that can be activated */
|
||||
return workspace_layout_set_poll(layout) ? false : true;
|
||||
}
|
||||
|
||||
static bScreen *screen_fullscreen_find_associated_normal_screen(const Main *bmain, bScreen *screen)
|
||||
{
|
||||
for (bScreen &screen_iter : bmain->screens) {
|
||||
if ((&screen_iter != screen) && ELEM(screen_iter.state, SCREENMAXIMIZED, SCREENFULL)) {
|
||||
ScrArea *area = static_cast<ScrArea *>(screen_iter.areabase.first);
|
||||
if (area && area->full == screen) {
|
||||
return &screen_iter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return screen;
|
||||
}
|
||||
|
||||
static bool screen_is_used_by_other_window(const wmWindow *win, const bScreen *screen)
|
||||
{
|
||||
return BKE_screen_is_used(screen) && (screen->winid != win->winid);
|
||||
}
|
||||
|
||||
WorkSpaceLayout *ED_workspace_screen_change_ensure_unused_layout(
|
||||
Main *bmain,
|
||||
WorkSpace *workspace,
|
||||
WorkSpaceLayout *layout_new,
|
||||
const WorkSpaceLayout *layout_fallback_base,
|
||||
wmWindow *win)
|
||||
{
|
||||
WorkSpaceLayout *layout_temp = layout_new;
|
||||
bScreen *screen_temp = BKE_workspace_layout_screen_get(layout_new);
|
||||
|
||||
screen_temp = screen_fullscreen_find_associated_normal_screen(bmain, screen_temp);
|
||||
layout_temp = BKE_workspace_layout_find(workspace, screen_temp);
|
||||
|
||||
if (screen_is_used_by_other_window(win, screen_temp)) {
|
||||
/* Screen is already used, try to find a free one. */
|
||||
layout_temp = BKE_workspace_layout_iter_circular(
|
||||
workspace, layout_new, workspace_change_find_new_layout_cb, nullptr, false);
|
||||
screen_temp = layout_temp ? BKE_workspace_layout_screen_get(layout_temp) : nullptr;
|
||||
|
||||
if (!layout_temp || screen_is_used_by_other_window(win, screen_temp)) {
|
||||
/* Fallback solution: duplicate layout. */
|
||||
layout_temp = ED_workspace_layout_duplicate(bmain, workspace, layout_fallback_base, win);
|
||||
}
|
||||
}
|
||||
|
||||
return layout_temp;
|
||||
}
|
||||
|
||||
static bool workspace_layout_cycle_iter_cb(const WorkSpaceLayout *layout, void * /*arg*/)
|
||||
{
|
||||
/* return false to stop iterator when we have found a layout to activate */
|
||||
return !workspace_layout_set_poll(layout);
|
||||
}
|
||||
|
||||
bool ED_workspace_layout_cycle(WorkSpace *workspace, const short direction, bContext *C)
|
||||
{
|
||||
wmWindow *win = CTX_wm_window(C);
|
||||
WorkSpaceLayout *old_layout = BKE_workspace_active_layout_get(win->workspace_hook);
|
||||
const bScreen *old_screen = BKE_workspace_layout_screen_get(old_layout);
|
||||
ScrArea *area = CTX_wm_area(C);
|
||||
|
||||
if (old_screen->temp || (area && area->full && area->full->temp)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BLI_assert(ELEM(direction, 1, -1));
|
||||
WorkSpaceLayout *new_layout = BKE_workspace_layout_iter_circular(workspace,
|
||||
old_layout,
|
||||
workspace_layout_cycle_iter_cb,
|
||||
nullptr,
|
||||
(direction == -1) ? true :
|
||||
false);
|
||||
|
||||
if (new_layout && (old_layout != new_layout)) {
|
||||
bScreen *new_screen = BKE_workspace_layout_screen_get(new_layout);
|
||||
|
||||
if (area && area->full) {
|
||||
/* return to previous state before switching screens */
|
||||
ED_screen_full_restore(C, area); /* may free screen of old_layout */
|
||||
}
|
||||
|
||||
ED_screen_change(C, new_screen);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,51 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_viewer_path.hh"
|
||||
|
||||
#include "ED_screen.hh"
|
||||
#include "ED_viewer_path.hh"
|
||||
|
||||
#include "WM_api.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/**
|
||||
* Checks if the viewer path stored in the workspace is still active and resets it if not.
|
||||
* The viewer path stored in the workspace is the ground truth for other editors, so it should be
|
||||
* updated before other editors look at it.
|
||||
*/
|
||||
static void validate_viewer_paths(bContext &C, WorkSpace &workspace)
|
||||
{
|
||||
if (workspace.viewer_path.path.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
using namespace blender::ed::viewer_path;
|
||||
|
||||
const UpdateActiveGeometryNodesViewerResult result = update_active_geometry_nodes_viewer(
|
||||
C, workspace.viewer_path);
|
||||
switch (result) {
|
||||
case UpdateActiveGeometryNodesViewerResult::StillActive:
|
||||
return;
|
||||
case UpdateActiveGeometryNodesViewerResult::Updated:
|
||||
break;
|
||||
case UpdateActiveGeometryNodesViewerResult::NotActive:
|
||||
BKE_viewer_path_clear(&workspace.viewer_path);
|
||||
break;
|
||||
}
|
||||
|
||||
WM_event_add_notifier(&C, NC_VIEWER_PATH, nullptr);
|
||||
}
|
||||
|
||||
void ED_workspace_do_listen(bContext *C, const wmNotifier * /*note*/)
|
||||
{
|
||||
WorkSpace *workspace = CTX_wm_workspace(C);
|
||||
validate_viewer_paths(*C, *workspace);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
Reference in New Issue
Block a user