238 lines
7.3 KiB
C++
238 lines
7.3 KiB
C++
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup overlay
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "BKE_vfont.hh"
|
|
#include "BLI_math_matrix.hh"
|
|
|
|
#include "overlay_base.hh"
|
|
|
|
namespace blender::draw::overlay {
|
|
|
|
/**
|
|
* Text objects related overlays.
|
|
* Currently only display cursor and selection of text edit mode.
|
|
*/
|
|
class Text : Overlay {
|
|
|
|
private:
|
|
PassSimple ps_ = {"TextEdit"};
|
|
PassSimple::Sub *selection_ps_ = nullptr;
|
|
PassSimple::Sub *selection_highlight_ps_ = nullptr;
|
|
PassSimple::Sub *cursor_ps_ = nullptr;
|
|
|
|
View view_edit_text = {"view_edit_text"};
|
|
|
|
LinePrimitiveBuf box_line_buf_;
|
|
|
|
/** A solid quad. */
|
|
gpu::Batch *quad = nullptr;
|
|
/** A wire quad. */
|
|
gpu::Batch *quad_wire = nullptr;
|
|
|
|
public:
|
|
Text(SelectionType selection_type) : box_line_buf_(selection_type, "box_line_buf_") {}
|
|
|
|
void begin_sync(Resources &res, const State &state) final
|
|
{
|
|
enabled_ = state.is_space_v3d();
|
|
box_line_buf_.clear();
|
|
|
|
if (!enabled_) {
|
|
return;
|
|
}
|
|
|
|
quad = res.shapes.quad_solid.get();
|
|
quad_wire = res.shapes.quad_wire.get();
|
|
|
|
ps_.init();
|
|
ps_.bind_ubo(OVERLAY_GLOBALS_SLOT, &res.globals_buf);
|
|
ps_.bind_ubo(DRW_CLIPPING_UBO_SLOT, &res.clip_planes_buf);
|
|
res.select_bind(ps_);
|
|
{
|
|
DRWState default_state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA;
|
|
float4 color;
|
|
|
|
/* Selection Boxes. */
|
|
{
|
|
auto &sub = ps_.sub("text_selection");
|
|
sub.state_set(default_state, state.clipping_plane_count);
|
|
sub.shader_set(res.shaders->uniform_color.get());
|
|
UI_GetThemeColor4fv(TH_WIDGET_TEXT_SELECTION, color);
|
|
srgb_to_linearrgb_v4(color, color);
|
|
sub.push_constant("ucolor", color);
|
|
selection_ps_ = ⊂
|
|
}
|
|
|
|
/* Highlight text within selection boxes. */
|
|
{
|
|
auto &sub = ps_.sub("highlight_text_selection");
|
|
sub.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA |
|
|
DRW_STATE_DEPTH_GREATER_EQUAL,
|
|
state.clipping_plane_count);
|
|
sub.shader_set(res.shaders->uniform_color.get());
|
|
UI_GetThemeColor4fv(TH_WIDGET_TEXT_HIGHLIGHT, color);
|
|
srgb_to_linearrgb_v4(color, color);
|
|
sub.push_constant("ucolor", color);
|
|
selection_highlight_ps_ = ⊂
|
|
}
|
|
|
|
/* Cursor (text caret). */
|
|
{
|
|
auto &sub = ps_.sub("text_cursor");
|
|
sub.state_set(default_state, state.clipping_plane_count);
|
|
sub.shader_set(res.shaders->uniform_color.get());
|
|
sub.state_set(default_state, state.clipping_plane_count);
|
|
UI_GetThemeColor4fv(TH_WIDGET_TEXT_CURSOR, color);
|
|
srgb_to_linearrgb_v4(color, color);
|
|
sub.push_constant("ucolor", color);
|
|
cursor_ps_ = ⊂
|
|
}
|
|
}
|
|
}
|
|
|
|
void edit_object_sync(Manager &manager,
|
|
const ObjectRef &ob_ref,
|
|
Resources &res,
|
|
const State & /*state*/) final
|
|
{
|
|
if (!enabled_) {
|
|
return;
|
|
}
|
|
|
|
const Curve &cu = DRW_object_get_data_for_drawing<Curve>(*ob_ref.object);
|
|
add_select(manager, cu, ob_ref.object->object_to_world());
|
|
add_cursor(manager, cu, ob_ref.object->object_to_world());
|
|
add_boxes(res, cu, ob_ref.object->object_to_world());
|
|
}
|
|
|
|
void end_sync(Resources &res, const State &state) final
|
|
{
|
|
if (!enabled_) {
|
|
return;
|
|
}
|
|
|
|
/* Text boxes. */
|
|
{
|
|
auto &sub_pass = ps_.sub("text_boxes");
|
|
sub_pass.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH |
|
|
DRW_STATE_DEPTH_LESS_EQUAL,
|
|
state.clipping_plane_count);
|
|
sub_pass.shader_set(res.shaders->extra_wire.get());
|
|
box_line_buf_.end_sync(sub_pass);
|
|
}
|
|
}
|
|
|
|
void draw(Framebuffer &framebuffer, Manager &manager, View &view) final
|
|
{
|
|
if (!enabled_) {
|
|
return;
|
|
}
|
|
|
|
View::OffsetData offset;
|
|
view_edit_text.sync(view.viewmat(), offset.winmat_polygon_offset(view.winmat(), -5.0f));
|
|
|
|
GPU_framebuffer_bind(framebuffer);
|
|
manager.submit(ps_, view_edit_text);
|
|
}
|
|
|
|
private:
|
|
/* Use 2D quad corners to create a matrix that set
|
|
* a [-1..1] quad at the right position. */
|
|
static void v2_quad_corners_to_mat4(const float4x2 &corners, float4x4 &r_mat)
|
|
{
|
|
const float2 &origin = corners[0];
|
|
const float2 half_size_x = (float2(corners[1]) - float2(corners[0])) * 0.5f;
|
|
const float2 half_size_y = (float2(corners[3]) - float2(corners[0])) * 0.5f;
|
|
|
|
r_mat = float4x4(float4(half_size_x, 0.0f, 0.0f),
|
|
float4(half_size_y, 0.0f, 0.0f),
|
|
float4(0.0f, 0.0f, 1.0f, 0.0f),
|
|
float4(origin + half_size_x + half_size_y, 0.0f, 1.0f));
|
|
}
|
|
|
|
void add_select(Manager &manager, const Curve &cu, const float4x4 &ob_to_world)
|
|
{
|
|
EditFont *ef = cu.editfont;
|
|
for (const int i : IndexRange(ef->selboxes_len)) {
|
|
EditFontSelBox *sb = &ef->selboxes[i];
|
|
|
|
float selboxw;
|
|
if (i + 1 != ef->selboxes_len) {
|
|
if (ef->selboxes[i + 1].y == sb->y) {
|
|
selboxw = ef->selboxes[i + 1].x - sb->x;
|
|
}
|
|
else {
|
|
selboxw = sb->w;
|
|
}
|
|
}
|
|
else {
|
|
selboxw = sb->w;
|
|
}
|
|
float4x2 box;
|
|
/* NOTE: v2_quad_corners_to_mat4 don't need the 3rd corner. */
|
|
if (sb->rot == 0.0f) {
|
|
box[0] = float2(sb->x, sb->y);
|
|
box[1] = float2(sb->x + selboxw, sb->y);
|
|
box[3] = float2(sb->x, sb->y + sb->h);
|
|
}
|
|
else {
|
|
float2x2 mat = math::from_rotation<float2x2>(sb->rot);
|
|
box[0] = float2(sb->x, sb->y);
|
|
box[1] = mat[0] * selboxw + float2(&sb->x);
|
|
box[3] = mat[1] * sb->h + float2(&sb->x);
|
|
}
|
|
float4x4 mat;
|
|
v2_quad_corners_to_mat4(box, mat);
|
|
ResourceHandle res_handle = manager.resource_handle(ob_to_world * mat);
|
|
selection_ps_->draw(quad, res_handle);
|
|
selection_highlight_ps_->draw(quad, res_handle);
|
|
}
|
|
}
|
|
|
|
void add_cursor(Manager &manager, const Curve &cu, const float4x4 &ob_to_world)
|
|
{
|
|
EditFont *edit_font = cu.editfont;
|
|
float4x2 cursor = float4x2(&edit_font->textcurs[0][0]);
|
|
float4x4 mat;
|
|
v2_quad_corners_to_mat4(cursor, mat);
|
|
ResourceHandle res_handle = manager.resource_handle(ob_to_world * mat);
|
|
|
|
cursor_ps_->draw(quad, res_handle);
|
|
|
|
/* Draw both wire and solid so the cursor is always at least with width of a line,
|
|
* otherwise it may become invisible, see: #137940. */
|
|
cursor_ps_->draw(quad_wire, res_handle);
|
|
}
|
|
|
|
void add_boxes(const Resources &res, const Curve &cu, const float4x4 &ob_to_world)
|
|
{
|
|
for (const int i : IndexRange(cu.totbox)) {
|
|
const TextBox &tb = cu.tb[i];
|
|
const bool is_active = (i == (cu.actbox - 1));
|
|
const float4 &color = is_active ? res.theme.colors.active_object : res.theme.colors.wire;
|
|
|
|
if ((tb.w != 0.0f) || (tb.h != 0.0f)) {
|
|
const float3 top_left = float3(cu.xof + tb.x, cu.yof + tb.y + cu.fsize_realtime, 0.001);
|
|
const float3 w = float3(tb.w, 0.0f, 0.0f);
|
|
const float3 h = float3(0.0f, tb.h, 0.0f);
|
|
float4x3 vecs = float4x3(top_left, top_left + w, top_left + w - h, top_left - h);
|
|
|
|
for (const int j : IndexRange(4)) {
|
|
vecs[j] = math::transform_point(ob_to_world, vecs[j]);
|
|
}
|
|
for (const int j : IndexRange(4)) {
|
|
box_line_buf_.append(vecs[j], vecs[(j + 1) % 4], color);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
} // namespace blender::draw::overlay
|