Blender V5.0
overlay_text.hh
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2024 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#pragma once
10
11#include "BLI_math_color.h"
12#include "BLI_math_matrix.hh"
13
14#include "BKE_vfont.hh"
15
16#include "overlay_base.hh"
17
18namespace blender::draw::overlay {
19
24class Text : Overlay {
25
26 private:
27 PassSimple ps_ = {"TextEdit"};
28 PassSimple::Sub *selection_ps_ = nullptr;
29 PassSimple::Sub *selection_highlight_ps_ = nullptr;
30 PassSimple::Sub *cursor_ps_ = nullptr;
31
32 View view_edit_text = {"view_edit_text"};
33
34 LinePrimitiveBuf box_line_buf_;
35
37 gpu::Batch *quad = nullptr;
39 gpu::Batch *quad_wire = nullptr;
40
41 public:
42 Text(SelectionType selection_type) : box_line_buf_(selection_type, "box_line_buf_") {}
43
44 void begin_sync(Resources &res, const State &state) final
45 {
46 enabled_ = state.is_space_v3d();
47 box_line_buf_.clear();
48
49 if (!enabled_) {
50 return;
51 }
52
53 quad = res.shapes.quad_solid.get();
54 quad_wire = res.shapes.quad_wire.get();
55
56 ps_.init();
57 ps_.bind_ubo(OVERLAY_GLOBALS_SLOT, &res.globals_buf);
58 ps_.bind_ubo(DRW_CLIPPING_UBO_SLOT, &res.clip_planes_buf);
59 res.select_bind(ps_);
60 {
63
64 /* Selection Boxes. */
65 {
66 auto &sub = ps_.sub("text_selection");
67 sub.state_set(default_state, state.clipping_plane_count);
68 sub.shader_set(res.shaders->uniform_color.get());
71 sub.push_constant("ucolor", color);
72 selection_ps_ = ⊂
73 }
74
75 /* Highlight text within selection boxes. */
76 {
77 auto &sub = ps_.sub("highlight_text_selection");
80 state.clipping_plane_count);
81 sub.shader_set(res.shaders->uniform_color.get());
84 sub.push_constant("ucolor", color);
85 selection_highlight_ps_ = ⊂
86 }
87
88 /* Cursor (text caret). */
89 {
90 auto &sub = ps_.sub("text_cursor");
91 sub.state_set(default_state, state.clipping_plane_count);
92 sub.shader_set(res.shaders->uniform_color.get());
93 sub.state_set(default_state, state.clipping_plane_count);
96 sub.push_constant("ucolor", color);
97 cursor_ps_ = ⊂
98 }
99 }
100 }
101
103 const ObjectRef &ob_ref,
104 Resources &res,
105 const State & /*state*/) final
106 {
107 if (!enabled_) {
108 return;
109 }
110
111 const Curve &cu = DRW_object_get_data_for_drawing<Curve>(*ob_ref.object);
112 add_select(manager, cu, ob_ref.object->object_to_world());
113 add_cursor(manager, cu, ob_ref.object->object_to_world());
114 add_boxes(res, cu, ob_ref.object->object_to_world());
115 }
116
117 void end_sync(Resources &res, const State &state) final
118 {
119 if (!enabled_) {
120 return;
121 }
122
123 /* Text boxes. */
124 {
125 auto &sub_pass = ps_.sub("text_boxes");
126 sub_pass.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH |
128 state.clipping_plane_count);
129 sub_pass.shader_set(res.shaders->extra_wire.get());
130 box_line_buf_.end_sync(sub_pass);
131 }
132 }
133
134 void draw(Framebuffer &framebuffer, Manager &manager, View &view) final
135 {
136 if (!enabled_) {
137 return;
138 }
139
140 View::OffsetData offset;
141 view_edit_text.sync(view.viewmat(), offset.winmat_polygon_offset(view.winmat(), -5.0f));
142
143 GPU_framebuffer_bind(framebuffer);
144 manager.submit(ps_, view_edit_text);
145 }
146
147 private:
148 /* Use 2D quad corners to create a matrix that set
149 * a [-1..1] quad at the right position. */
150 static void v2_quad_corners_to_mat4(const float4x2 &corners, float4x4 &r_mat)
151 {
152 const float2 &origin = corners[0];
153 const float2 half_size_x = (float2(corners[1]) - float2(corners[0])) * 0.5f;
154 const float2 half_size_y = (float2(corners[3]) - float2(corners[0])) * 0.5f;
155
156 r_mat = float4x4(float4(half_size_x, 0.0f, 0.0f),
157 float4(half_size_y, 0.0f, 0.0f),
158 float4(0.0f, 0.0f, 1.0f, 0.0f),
159 float4(origin + half_size_x + half_size_y, 0.0f, 1.0f));
160 }
161
162 void add_select(Manager &manager, const Curve &cu, const float4x4 &ob_to_world)
163 {
164 EditFont *ef = cu.editfont;
165 for (const int i : IndexRange(ef->selboxes_len)) {
166 EditFontSelBox *sb = &ef->selboxes[i];
167
168 float selboxw;
169 if (i + 1 != ef->selboxes_len) {
170 if (ef->selboxes[i + 1].y == sb->y) {
171 selboxw = ef->selboxes[i + 1].x - sb->x;
172 }
173 else {
174 selboxw = sb->w;
175 }
176 }
177 else {
178 selboxw = sb->w;
179 }
180 float4x2 box;
181 /* NOTE: v2_quad_corners_to_mat4 don't need the 3rd corner. */
182 if (sb->rotate == 0.0f) {
183 box[0] = float2(sb->x, sb->y);
184 box[1] = float2(sb->x + selboxw, sb->y);
185 box[3] = float2(sb->x, sb->y + sb->h);
186 }
187 else {
189 box[0] = float2(sb->x, sb->y);
190 box[1] = mat[0] * selboxw + float2(&sb->x);
191 box[3] = mat[1] * sb->h + float2(&sb->x);
192 }
193 float4x4 mat;
194 v2_quad_corners_to_mat4(box, mat);
195 ResourceHandleRange res_handle = manager.resource_handle(ob_to_world * mat);
196 selection_ps_->draw(quad, res_handle);
197 selection_highlight_ps_->draw(quad, res_handle);
198 }
199 }
200
201 void add_cursor(Manager &manager, const Curve &cu, const float4x4 &ob_to_world)
202 {
203 EditFont *edit_font = cu.editfont;
204 float4x2 cursor = float4x2(&edit_font->textcurs[0][0]);
205 float4x4 mat;
206 v2_quad_corners_to_mat4(cursor, mat);
207 ResourceHandleRange res_handle = manager.resource_handle(ob_to_world * mat);
208
209 cursor_ps_->draw(quad, res_handle);
210
211 /* Draw both wire and solid so the cursor is always at least with width of a line,
212 * otherwise it may become invisible, see: #137940. */
213 cursor_ps_->draw(quad_wire, res_handle);
214 }
215
216 void add_boxes(const Resources &res, const Curve &cu, const float4x4 &ob_to_world)
217 {
218 const EditFont *edit_font = cu.editfont;
219 for (const int i : IndexRange(cu.totbox)) {
220 const TextBox &tb = cu.tb[i];
221 const bool is_active = (i == (cu.actbox - 1));
222 const float4 &color = is_active ? res.theme.colors.active_object : res.theme.colors.wire;
223
224 if ((tb.w != 0.0f) || (tb.h != 0.0f)) {
225 const float3 top_left = float3(
226 cu.xof + tb.x, cu.yof + tb.y + edit_font->font_size_eval, 0.001);
227 const float3 w = float3(tb.w, 0.0f, 0.0f);
228 const float3 h = float3(0.0f, tb.h, 0.0f);
229 float4x3 vecs = float4x3(top_left, top_left + w, top_left + w - h, top_left - h);
230
231 for (const int j : IndexRange(4)) {
232 vecs[j] = math::transform_point(ob_to_world, vecs[j]);
233 }
234 for (const int j : IndexRange(4)) {
235 box_line_buf_.append(vecs[j], vecs[(j + 1) % 4], color);
236 }
237 }
238 }
239 }
240};
241} // namespace blender::draw::overlay
MINLINE void srgb_to_linearrgb_v4(float linear[4], const float srgb[4])
struct Curve Curve
struct TextBox TextBox
T & DRW_object_get_data_for_drawing(const Object &object)
static AppView * view
void GPU_framebuffer_bind(blender::gpu::FrameBuffer *fb)
@ TH_WIDGET_TEXT_HIGHLIGHT
@ TH_WIDGET_TEXT_SELECTION
@ TH_WIDGET_TEXT_CURSOR
void UI_GetThemeColor4fv(int colorid, float col[4])
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
ResourceHandleRange resource_handle(const ObjectRef &ref, float inflate_bounds=0.0f)
void draw(gpu::Batch *batch, uint instance_len=-1, uint vertex_len=-1, uint vertex_first=-1, ResourceIndexRange res_index={}, uint custom_id=0)
Definition draw_pass.hh:893
detail::PassBase< command::DrawCommandBuf > Sub
Definition draw_pass.hh:499
Text(SelectionType selection_type)
void begin_sync(Resources &res, const State &state) final
void draw(Framebuffer &framebuffer, Manager &manager, View &view) final
void edit_object_sync(Manager &manager, const ObjectRef &ob_ref, Resources &res, const State &) final
void end_sync(Resources &res, const State &state) final
#define DRW_CLIPPING_UBO_SLOT
#define OVERLAY_GLOBALS_SLOT
DRWState
Definition draw_state.hh:25
@ DRW_STATE_BLEND_ALPHA
Definition draw_state.hh:55
@ DRW_STATE_DEPTH_GREATER_EQUAL
Definition draw_state.hh:41
@ DRW_STATE_WRITE_DEPTH
Definition draw_state.hh:29
@ DRW_STATE_WRITE_COLOR
Definition draw_state.hh:30
@ DRW_STATE_DEPTH_LESS_EQUAL
Definition draw_state.hh:38
blender::gpu::Batch * quad
static ulong state[N]
select::SelectionType SelectionType
detail::Pass< command::DrawCommandBuf > PassSimple
MatT from_rotation(const RotationT &rotation)
VecBase< T, 3 > transform_point(const CartesianBasis &basis, const VecBase< T, 3 > &v)
MatBase< float, 2, 2 > float2x2
MatBase< float, 4, 4 > float4x4
VecBase< float, 4 > float4
VecBase< float, 2 > float2
MatBase< float, 4, 2 > float4x2
MatBase< float, 4, 3 > float4x3
VecBase< float, 3 > float3
struct TextBox * tb
struct EditFont * editfont
float font_size_eval
Definition BKE_vfont.hh:46
EditFontSelBox * selboxes
Definition BKE_vfont.hh:50
int selboxes_len
Definition BKE_vfont.hh:51
blender::float2 textcurs[4]
Definition BKE_vfont.hh:49
float4x4 winmat_polygon_offset(const float4x4 &winmat, float offset)
Definition draw_view.hh:197
void append(const float3 &start, const float3 &end, const float4 &color, select::ID select_id=select::SelectMap::select_invalid_id())
i
Definition text_draw.cc:230