color_picker_shape.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771
  1. /**************************************************************************/
  2. /* color_picker_shape.cpp */
  3. /**************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /**************************************************************************/
  8. /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
  9. /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
  10. /* */
  11. /* Permission is hereby granted, free of charge, to any person obtaining */
  12. /* a copy of this software and associated documentation files (the */
  13. /* "Software"), to deal in the Software without restriction, including */
  14. /* without limitation the rights to use, copy, modify, merge, publish, */
  15. /* distribute, sublicense, and/or sell copies of the Software, and to */
  16. /* permit persons to whom the Software is furnished to do so, subject to */
  17. /* the following conditions: */
  18. /* */
  19. /* The above copyright notice and this permission notice shall be */
  20. /* included in all copies or substantial portions of the Software. */
  21. /* */
  22. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
  23. /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
  24. /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
  25. /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
  26. /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
  27. /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
  28. /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  29. /**************************************************************************/
  30. #include "color_picker_shape.h"
  31. #include "scene/gui/margin_container.h"
  32. void ColorPickerShape::_emit_color_changed() {
  33. color_picker->emit_signal(SNAME("color_changed"), color_picker->color);
  34. }
  35. bool ColorPickerShape::can_handle(const Ref<InputEvent> &p_event, Vector2 &r_position, bool *r_is_click) {
  36. Ref<InputEventMouseButton> mb = p_event;
  37. if (mb.is_valid()) {
  38. if (mb->get_button_index() != MouseButton::LEFT) {
  39. return false;
  40. }
  41. if (r_is_click) {
  42. *r_is_click = true;
  43. }
  44. if (mb->is_pressed()) {
  45. is_dragging = true;
  46. r_position = mb->get_position();
  47. return true;
  48. } else {
  49. _emit_color_changed();
  50. color_picker->add_recent_preset(color_picker->color);
  51. is_dragging = false;
  52. return false;
  53. }
  54. }
  55. Ref<InputEventMouseMotion> mm = p_event;
  56. if (is_dragging && mm.is_valid()) {
  57. r_position = mm->get_position();
  58. return true;
  59. }
  60. return false;
  61. }
  62. void ColorPickerShape::apply_color() {
  63. color_picker->_copy_hsv_to_color();
  64. color_picker->last_color = color_picker->color;
  65. color_picker->set_pick_color(color_picker->color);
  66. if (!color_picker->deferred_mode_enabled) {
  67. _emit_color_changed();
  68. }
  69. }
  70. void ColorPickerShape::cancel_event() {
  71. is_dragging = false;
  72. }
  73. void ColorPickerShape::draw_focus_rect(Control *p_control, const Rect2 &p_rect) {
  74. if (!p_control->has_focus()) {
  75. return;
  76. }
  77. Rect2 focus_rect;
  78. if (p_rect.has_area()) {
  79. focus_rect = p_rect;
  80. } else {
  81. focus_rect = Rect2(Vector2(), p_control->get_size());
  82. }
  83. const RID ci = p_control->get_canvas_item();
  84. if (!cursor_editing) {
  85. RenderingServer::get_singleton()->canvas_item_add_rect(ci, focus_rect, color_picker->theme_cache.focused_not_editing_cursor_color);
  86. }
  87. color_picker->theme_cache.picker_focus_rectangle->draw(ci, focus_rect);
  88. }
  89. void ColorPickerShape::draw_focus_circle(Control *p_control) {
  90. if (!p_control->has_focus()) {
  91. return;
  92. }
  93. const Rect2 focus_rect(Vector2(), p_control->get_size());
  94. const RID ci = p_control->get_canvas_item();
  95. if (!cursor_editing) {
  96. RenderingServer::get_singleton()->canvas_item_add_circle(ci, focus_rect.get_center(), focus_rect.get_size().y * 0.5, color_picker->theme_cache.focused_not_editing_cursor_color);
  97. }
  98. color_picker->theme_cache.picker_focus_circle->draw(ci, focus_rect);
  99. }
  100. void ColorPickerShape::draw_sv_square(Control *p_control, const Rect2 &p_square, bool p_draw_focus) {
  101. const Vector2 end = p_square.get_end();
  102. PackedVector2Array points = {
  103. p_square.position,
  104. Vector2(end.x, p_square.position.y),
  105. end,
  106. Vector2(p_square.position.x, end.y),
  107. };
  108. Color color1 = color_picker->color;
  109. color1.set_hsv(color_picker->h, 1, 1);
  110. Color color2 = color1;
  111. color2.set_hsv(color_picker->h, 1, 0);
  112. PackedColorArray colors = {
  113. Color(1, 1, 1, 1),
  114. Color(1, 1, 1, 1),
  115. Color(0, 0, 0, 1),
  116. Color(0, 0, 0, 1)
  117. };
  118. p_control->draw_polygon(points, colors);
  119. colors = {
  120. Color(color1, 0),
  121. Color(color1, 1),
  122. Color(color2, 1),
  123. Color(color2, 0)
  124. };
  125. p_control->draw_polygon(points, colors);
  126. Vector2 cursor_pos;
  127. cursor_pos.x = CLAMP(p_square.position.x + p_square.size.x * color_picker->s, p_square.position.x, end.x);
  128. cursor_pos.y = CLAMP(p_square.position.y + p_square.size.y * (1.0 - color_picker->v), p_square.position.y, end.y);
  129. if (p_draw_focus) {
  130. draw_focus_rect(p_control, p_square);
  131. }
  132. draw_cursor(p_control, cursor_pos);
  133. }
  134. void ColorPickerShape::draw_cursor(Control *p_control, const Vector2 &p_center, bool p_draw_bg) {
  135. const Vector2 position = p_center - color_picker->theme_cache.picker_cursor->get_size() * 0.5;
  136. if (p_draw_bg) {
  137. p_control->draw_texture(color_picker->theme_cache.picker_cursor_bg, position, Color(color_picker->color, 1.0));
  138. }
  139. p_control->draw_texture(color_picker->theme_cache.picker_cursor, position);
  140. }
  141. void ColorPickerShape::draw_circle_cursor(Control *p_control, float p_hue) {
  142. const Vector2 center = p_control->get_size() * 0.5;
  143. const Vector2 cursor_pos(
  144. center.x + (center.x * Math::cos(p_hue * Math::TAU) * color_picker->s),
  145. center.y + (center.y * Math::sin(p_hue * Math::TAU) * color_picker->s));
  146. draw_cursor(p_control, cursor_pos);
  147. }
  148. void ColorPickerShape::connect_shape_focus(Control *p_shape) {
  149. p_shape->set_focus_mode(Control::FOCUS_ALL);
  150. p_shape->connect(SceneStringName(focus_entered), callable_mp(this, &ColorPickerShape::shape_focus_entered));
  151. p_shape->connect(SceneStringName(focus_exited), callable_mp(this, &ColorPickerShape::shape_focus_exited));
  152. }
  153. void ColorPickerShape::shape_focus_entered() {
  154. Input *input = Input::get_singleton();
  155. if (!(input->is_action_pressed("ui_up") || input->is_action_pressed("ui_down") || input->is_action_pressed("ui_left") || input->is_action_pressed("ui_right"))) {
  156. cursor_editing = true;
  157. }
  158. }
  159. void ColorPickerShape::shape_focus_exited() {
  160. cursor_editing = false;
  161. }
  162. void ColorPickerShape::handle_cursor_editing(const Ref<InputEvent> &p_event, Control *p_control) {
  163. if (p_event->is_action_pressed("ui_accept", false, true)) {
  164. cursor_editing = !cursor_editing;
  165. p_control->queue_redraw();
  166. color_picker->accept_event();
  167. }
  168. if (cursor_editing && p_event->is_action_pressed("ui_cancel", false, true)) {
  169. cursor_editing = false;
  170. p_control->queue_redraw();
  171. color_picker->accept_event();
  172. }
  173. if (!cursor_editing) {
  174. return;
  175. }
  176. Input *input = Input::get_singleton();
  177. bool is_joypad_event = Object::cast_to<InputEventJoypadMotion>(p_event.ptr()) || Object::cast_to<InputEventJoypadButton>(p_event.ptr());
  178. if (p_event->is_action_pressed("ui_left", true) || p_event->is_action_pressed("ui_right", true) || p_event->is_action_pressed("ui_up", true) || p_event->is_action_pressed("ui_down", true)) {
  179. if (is_joypad_event) {
  180. if (color_picker->is_processing_internal()) {
  181. color_picker->accept_event();
  182. return;
  183. }
  184. color_picker->set_process_internal(true);
  185. }
  186. Vector2 color_change_vector = Vector2(
  187. input->is_action_pressed("ui_right") - input->is_action_pressed("ui_left"),
  188. input->is_action_pressed("ui_down") - input->is_action_pressed("ui_up"));
  189. update_cursor(color_change_vector, p_event->is_echo());
  190. color_picker->accept_event();
  191. }
  192. }
  193. int ColorPickerShape::get_edge_h_change(const Vector2 &p_color_change_vector) {
  194. int h_change = 0;
  195. if (color_picker->h > 0 && color_picker->h < 0.5) {
  196. h_change -= p_color_change_vector.x;
  197. } else if (color_picker->h > 0.5 && color_picker->h < 1) {
  198. h_change += p_color_change_vector.x;
  199. }
  200. if (color_picker->h > 0.25 && color_picker->h < 0.75) {
  201. h_change -= p_color_change_vector.y;
  202. } else if (color_picker->h < 0.25 || color_picker->h > 0.75) {
  203. h_change += p_color_change_vector.y;
  204. }
  205. return h_change;
  206. }
  207. float ColorPickerShape::get_h_on_circle_edge(const Vector2 &p_color_change_vector) {
  208. int h_change = get_edge_h_change(p_color_change_vector);
  209. float target_h = Math::wrapf(color_picker->h + h_change / 360.0, 0, 1);
  210. int current_quarter = color_picker->h * 4;
  211. int future_quarter = target_h * 4;
  212. if (p_color_change_vector.y > 0 && ((future_quarter == 0 && current_quarter == 1) || (future_quarter == 1 && current_quarter == 0))) {
  213. target_h = 0.25f;
  214. } else if (p_color_change_vector.y < 0 && ((future_quarter == 2 && current_quarter == 3) || (future_quarter == 3 && current_quarter == 2))) {
  215. target_h = 0.75f;
  216. } else if (p_color_change_vector.x < 0 && ((future_quarter == 1 && current_quarter == 2) || (future_quarter == 2 && current_quarter == 1))) {
  217. target_h = 0.5f;
  218. } else if (p_color_change_vector.x > 0 && ((future_quarter == 3 && current_quarter == 0) || (future_quarter == 0 && current_quarter == 3))) {
  219. target_h = 0;
  220. }
  221. return target_h;
  222. }
  223. void ColorPickerShape::initialize_controls() {
  224. _initialize_controls();
  225. update_theme();
  226. is_initialized = true;
  227. }
  228. void ColorPickerShape::update_cursor(const Vector2 &p_color_change_vector, bool p_is_echo) {
  229. if (p_color_change_vector.is_zero_approx()) {
  230. echo_multiplier = 1.0;
  231. } else {
  232. echo_multiplier = p_is_echo ? CLAMP(echo_multiplier * 1.1, 1, 25) : 1;
  233. _update_cursor(p_color_change_vector * echo_multiplier, p_is_echo);
  234. apply_color();
  235. }
  236. }
  237. ColorPickerShape::ColorPickerShape(ColorPicker *p_color_picker) {
  238. color_picker = p_color_picker;
  239. }
  240. void ColorPickerShapeRectangle::_sv_square_input(const Ref<InputEvent> &p_event) {
  241. handle_cursor_editing(p_event, sv_square);
  242. Vector2 event_position;
  243. if (!can_handle(p_event, event_position)) {
  244. return;
  245. }
  246. event_position = (event_position / sv_square->get_size()).clampf(0.0, 1.0);
  247. color_picker->s = event_position.x;
  248. color_picker->v = 1.0 - event_position.y;
  249. apply_color();
  250. }
  251. void ColorPickerShapeRectangle::_hue_slider_input(const Ref<InputEvent> &p_event) {
  252. handle_cursor_editing(p_event, hue_slider);
  253. Vector2 event_position;
  254. if (!can_handle(p_event, event_position)) {
  255. return;
  256. }
  257. color_picker->h = CLAMP(event_position.y / hue_slider->get_size().y, 0.0, 1.0);
  258. apply_color();
  259. }
  260. void ColorPickerShapeRectangle::_sv_square_draw() {
  261. draw_sv_square(sv_square, Rect2(Vector2(), sv_square->get_size()));
  262. }
  263. void ColorPickerShapeRectangle::_hue_slider_draw() {
  264. const Vector2 size = hue_slider->get_size();
  265. hue_slider->draw_texture_rect(color_picker->theme_cache.color_hue, Rect2(0, 0, -size.y, size.x), false, Color(1, 1, 1), true);
  266. draw_focus_rect(hue_slider);
  267. int y = size.y * color_picker->h;
  268. const Color color = Color::from_hsv(color_picker->h, 1, 1);
  269. hue_slider->draw_line(Vector2(0, y), Vector2(size.x, y), color.inverted());
  270. }
  271. void ColorPickerShapeRectangle::_initialize_controls() {
  272. sv_square = memnew(Control);
  273. color_picker->shape_container->add_child(sv_square);
  274. sv_square->connect(SceneStringName(gui_input), callable_mp(this, &ColorPickerShapeRectangle::_sv_square_input));
  275. sv_square->connect(SceneStringName(draw), callable_mp(this, &ColorPickerShapeRectangle::_sv_square_draw));
  276. connect_shape_focus(sv_square);
  277. hue_slider = memnew(Control);
  278. color_picker->shape_container->add_child(hue_slider);
  279. hue_slider->connect(SceneStringName(gui_input), callable_mp(this, &ColorPickerShapeRectangle::_hue_slider_input));
  280. hue_slider->connect(SceneStringName(draw), callable_mp(this, &ColorPickerShapeRectangle::_hue_slider_draw));
  281. connect_shape_focus(hue_slider);
  282. controls.append(sv_square);
  283. controls.append(hue_slider);
  284. }
  285. void ColorPickerShapeRectangle::_update_cursor(const Vector2 &p_color_change_vector, bool p_is_echo) {
  286. if (sv_square->has_focus()) {
  287. color_picker->s = CLAMP(color_picker->s + p_color_change_vector.x / 100.0, 0, 1);
  288. color_picker->v = CLAMP(color_picker->v - p_color_change_vector.y / 100.0, 0, 1);
  289. } else if (hue_slider->has_focus()) {
  290. color_picker->h = CLAMP(color_picker->h + p_color_change_vector.y * echo_multiplier / 360.0, 0, 1);
  291. }
  292. }
  293. void ColorPickerShapeRectangle::update_theme() {
  294. const ColorPicker::ThemeCache &theme_cache = color_picker->theme_cache;
  295. sv_square->set_custom_minimum_size(Size2(theme_cache.sv_width, theme_cache.sv_height));
  296. hue_slider->set_custom_minimum_size(Size2(theme_cache.h_width, 0));
  297. }
  298. void ColorPickerShapeRectangle::grab_focus() {
  299. hue_slider->grab_focus();
  300. }
  301. float ColorPickerShapeWheel::_get_h_on_wheel(const Vector2 &p_color_change_vector) {
  302. int h_change = get_edge_h_change(p_color_change_vector);
  303. float target_h = Math::wrapf(color_picker->h + h_change / 360.0, 0, 1);
  304. int current_quarter = color_picker->h * 4;
  305. int future_quarter = target_h * 4;
  306. if (p_color_change_vector.y > 0 && ((future_quarter == 0 && current_quarter == 1) || (future_quarter == 1 && current_quarter == 0))) {
  307. rotate_next_echo_event = !rotate_next_echo_event;
  308. } else if (p_color_change_vector.y < 0 && ((future_quarter == 2 && current_quarter == 3) || (future_quarter == 3 && current_quarter == 2))) {
  309. rotate_next_echo_event = !rotate_next_echo_event;
  310. } else if (p_color_change_vector.x < 0 && ((future_quarter == 1 && current_quarter == 2) || (future_quarter == 2 && current_quarter == 1))) {
  311. rotate_next_echo_event = !rotate_next_echo_event;
  312. } else if (p_color_change_vector.x > 0 && ((future_quarter == 3 && current_quarter == 0) || (future_quarter == 0 && current_quarter == 3))) {
  313. rotate_next_echo_event = !rotate_next_echo_event;
  314. }
  315. return target_h;
  316. }
  317. void ColorPickerShapeWheel::_reset_wheel_focus() {
  318. wheel_focused = true;
  319. }
  320. void ColorPickerShapeWheel::_wheel_input(const Ref<InputEvent> &p_event) {
  321. handle_cursor_editing(p_event, wheel_uv);
  322. if (!cursor_editing) {
  323. // Wheel and inner square are the same control, so focus has to be moved manually.
  324. if (!wheel_focused && p_event->is_action_pressed("ui_down", true)) {
  325. wheel_focused = true;
  326. wheel_uv->queue_redraw();
  327. color_picker->accept_event();
  328. return;
  329. } else if (wheel_focused && p_event->is_action_pressed("ui_up", true)) {
  330. wheel_focused = false;
  331. wheel_uv->queue_redraw();
  332. color_picker->accept_event();
  333. return;
  334. }
  335. }
  336. Vector2 event_position;
  337. bool is_click = false;
  338. if (!can_handle(p_event, event_position, &is_click)) {
  339. if (is_click) {
  340. // Released mouse button while dragging wheel.
  341. spinning = false;
  342. }
  343. return;
  344. }
  345. const Vector2 uv_size = wheel_uv->get_size();
  346. const Vector2 ring_radius = uv_size * Math::SQRT12 * WHEEL_RADIUS;
  347. const Vector2 center = uv_size * 0.5;
  348. if (is_click && !spinning) {
  349. real_t dist = center.distance_to(event_position);
  350. if (dist >= center.x * WHEEL_RADIUS * 2.0 && dist <= center.x) {
  351. spinning = true;
  352. if (!wheel_focused) {
  353. cursor_editing = true;
  354. wheel_focused = true;
  355. }
  356. } else if (dist > center.x) {
  357. // Clicked outside the wheel.
  358. cancel_event();
  359. return;
  360. }
  361. };
  362. if (spinning) {
  363. real_t rad = center.angle_to_point(event_position);
  364. color_picker->h = ((rad >= 0) ? rad : (Math::TAU + rad)) / Math::TAU;
  365. apply_color();
  366. return;
  367. }
  368. const Rect2 uv_rect(center - ring_radius, ring_radius * 2.0);
  369. event_position -= uv_rect.position;
  370. event_position /= uv_rect.size;
  371. if (is_click && (event_position.x < 0 || event_position.x > 1 || event_position.y < 0 || event_position.y > 1)) {
  372. // Clicked inside the wheel, but outside the square.
  373. cancel_event();
  374. return;
  375. }
  376. event_position = event_position.clampf(0.0, 1.0);
  377. color_picker->s = event_position.x;
  378. color_picker->v = 1.0 - event_position.y;
  379. if (wheel_focused) {
  380. cursor_editing = true;
  381. wheel_focused = false;
  382. }
  383. apply_color();
  384. }
  385. void ColorPickerShapeWheel::_wheel_draw() {
  386. wheel->draw_rect(Rect2(Point2(), wheel->get_size()), Color(1, 1, 1));
  387. }
  388. void ColorPickerShapeWheel::_wheel_uv_draw() {
  389. const Vector2 uv_size = wheel_uv->get_size();
  390. const Vector2 ring_radius = uv_size * Math::SQRT12 * WHEEL_RADIUS;
  391. const Vector2 center = uv_size * 0.5;
  392. const Rect2 uv_rect(center - ring_radius, ring_radius * 2.0);
  393. draw_sv_square(wheel_uv, uv_rect, !wheel_focused);
  394. if (wheel_focused) {
  395. draw_focus_circle(wheel_uv);
  396. }
  397. float radius = WHEEL_RADIUS * 2.0;
  398. radius += (1.0 - radius) * 0.5;
  399. const Vector2 cursor_pos = center +
  400. Vector2(center.x * Math::cos(color_picker->h * Math::TAU) * radius,
  401. center.y * Math::sin(color_picker->h * Math::TAU) * radius);
  402. draw_cursor(wheel_uv, cursor_pos, false);
  403. }
  404. void ColorPickerShapeWheel::_initialize_controls() {
  405. wheel_margin = memnew(MarginContainer);
  406. color_picker->shape_container->add_child(wheel_margin);
  407. Ref<ShaderMaterial> material;
  408. material.instantiate();
  409. material->set_shader(ColorPicker::wheel_shader);
  410. material->set_shader_parameter("wheel_radius", WHEEL_RADIUS);
  411. wheel = memnew(Control);
  412. wheel->set_material(material);
  413. wheel_margin->add_child(wheel);
  414. wheel->connect(SceneStringName(draw), callable_mp(this, &ColorPickerShapeWheel::_wheel_draw));
  415. wheel_uv = memnew(Control);
  416. wheel_margin->add_child(wheel_uv);
  417. wheel_uv->connect(SceneStringName(focus_entered), callable_mp(this, &ColorPickerShapeWheel::_reset_wheel_focus));
  418. wheel_uv->connect(SceneStringName(gui_input), callable_mp(this, &ColorPickerShapeWheel::_wheel_input));
  419. wheel_uv->connect(SceneStringName(draw), callable_mp(this, &ColorPickerShapeWheel::_wheel_uv_draw));
  420. connect_shape_focus(wheel_uv);
  421. controls.append(wheel_margin);
  422. controls.append(wheel);
  423. controls.append(wheel_uv);
  424. }
  425. void ColorPickerShapeWheel::_update_cursor(const Vector2 &p_color_change_vector, bool p_is_echo) {
  426. if (wheel_focused) {
  427. if (p_is_echo && rotate_next_echo_event) {
  428. color_picker->h = _get_h_on_wheel(-p_color_change_vector);
  429. } else {
  430. rotate_next_echo_event = false;
  431. color_picker->h = _get_h_on_wheel(p_color_change_vector);
  432. }
  433. } else {
  434. color_picker->s = CLAMP(color_picker->s + p_color_change_vector.x / 100.0, 0, 1);
  435. color_picker->v = CLAMP(color_picker->v - p_color_change_vector.y / 100.0, 0, 1);
  436. }
  437. }
  438. void ColorPickerShapeWheel::update_theme() {
  439. const ColorPicker::ThemeCache &theme_cache = color_picker->theme_cache;
  440. wheel_margin->set_custom_minimum_size(Size2(theme_cache.sv_width, theme_cache.sv_height));
  441. wheel_margin->add_theme_constant_override(SNAME("margin_bottom"), 8 * theme_cache.base_scale);
  442. }
  443. void ColorPickerShapeWheel::grab_focus() {
  444. wheel_uv->grab_focus();
  445. }
  446. void ColorPickerShapeCircle::update_circle_cursor(const Vector2 &p_color_change_vector, const Vector2 &p_center, const Vector2 &p_hue_offset) {
  447. if (circle_keyboard_joypad_picker_cursor_position == Vector2i()) {
  448. circle_keyboard_joypad_picker_cursor_position = p_center + p_hue_offset;
  449. }
  450. Vector2i potential_cursor_position = circle_keyboard_joypad_picker_cursor_position + p_color_change_vector;
  451. real_t potential_new_cursor_distance = p_center.distance_to(potential_cursor_position);
  452. real_t dist_pre = p_center.distance_to(circle_keyboard_joypad_picker_cursor_position);
  453. if (color_picker->s < 1 || potential_new_cursor_distance < dist_pre) {
  454. circle_keyboard_joypad_picker_cursor_position += p_color_change_vector;
  455. real_t dist = p_center.distance_to(circle_keyboard_joypad_picker_cursor_position);
  456. real_t rad = p_center.angle_to_point(circle_keyboard_joypad_picker_cursor_position);
  457. color_picker->h = ((rad >= 0) ? rad : (Math::TAU + rad)) / Math::TAU;
  458. color_picker->s = CLAMP(dist / p_center.x, 0, 1);
  459. } else {
  460. color_picker->h = get_h_on_circle_edge(p_color_change_vector);
  461. circle_keyboard_joypad_picker_cursor_position = Vector2i();
  462. }
  463. }
  464. void ColorPickerShapeCircle::_initialize_controls() {
  465. circle_margin = memnew(MarginContainer);
  466. color_picker->shape_container->add_child(circle_margin);
  467. Ref<ShaderMaterial> material;
  468. material.instantiate();
  469. material->set_shader(_get_shader());
  470. circle = memnew(Control);
  471. circle->set_material(material);
  472. circle_margin->add_child(circle);
  473. circle->connect(SceneStringName(draw), callable_mp(this, &ColorPickerShapeCircle::_circle_draw));
  474. circle_overlay = memnew(Control);
  475. circle_overlay->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
  476. circle->add_child(circle_overlay);
  477. circle_overlay->connect(SceneStringName(gui_input), callable_mp(this, &ColorPickerShapeCircle::_circle_input));
  478. circle_overlay->connect(SceneStringName(draw), callable_mp(this, &ColorPickerShapeCircle::_circle_overlay_draw));
  479. connect_shape_focus(circle_overlay);
  480. value_slider = memnew(Control);
  481. color_picker->shape_container->add_child(value_slider);
  482. value_slider->connect(SceneStringName(gui_input), callable_mp(this, &ColorPickerShapeCircle::_value_slider_input));
  483. value_slider->connect(SceneStringName(draw), callable_mp(this, &ColorPickerShapeCircle::_value_slider_draw));
  484. connect_shape_focus(value_slider);
  485. controls.append(circle_margin);
  486. controls.append(circle);
  487. controls.append(circle_overlay);
  488. controls.append(value_slider);
  489. }
  490. void ColorPickerShapeCircle::update_theme() {
  491. const ColorPicker::ThemeCache &theme_cache = color_picker->theme_cache;
  492. circle_margin->set_custom_minimum_size(Size2(theme_cache.sv_width, theme_cache.sv_height));
  493. circle_margin->add_theme_constant_override(SNAME("margin_bottom"), 8 * theme_cache.base_scale);
  494. value_slider->set_custom_minimum_size(Size2(theme_cache.h_width, 0));
  495. }
  496. void ColorPickerShapeCircle::grab_focus() {
  497. circle_overlay->grab_focus();
  498. }
  499. void ColorPickerShapeVHSCircle::_circle_input(const Ref<InputEvent> &p_event) {
  500. handle_cursor_editing(p_event, circle_overlay);
  501. Vector2 event_position;
  502. bool is_click = false;
  503. if (!can_handle(p_event, event_position, &is_click)) {
  504. return;
  505. }
  506. Vector2 center = circle->get_size() * 0.5;
  507. real_t dist = center.distance_to(event_position);
  508. if (is_click && dist > center.x) {
  509. // Clicked outside the circle.
  510. cancel_event();
  511. return;
  512. }
  513. real_t rad = center.angle_to_point(event_position);
  514. color_picker->h = ((rad >= 0) ? rad : (Math::TAU + rad)) / Math::TAU;
  515. color_picker->s = CLAMP(dist / center.x, 0, 1);
  516. color_picker->ok_hsl_h = color_picker->h;
  517. color_picker->ok_hsl_s = color_picker->s;
  518. circle_keyboard_joypad_picker_cursor_position = Vector2i();
  519. apply_color();
  520. }
  521. void ColorPickerShapeVHSCircle::_value_slider_input(const Ref<InputEvent> &p_event) {
  522. handle_cursor_editing(p_event, value_slider);
  523. Vector2 event_position;
  524. if (!can_handle(p_event, event_position)) {
  525. return;
  526. }
  527. color_picker->v = 1.0 - CLAMP(event_position.y / value_slider->get_size().y, 0.0, 1.0);
  528. apply_color();
  529. }
  530. void ColorPickerShapeVHSCircle::_circle_draw() {
  531. Ref<ShaderMaterial> material = circle->get_material();
  532. material->set_shader_parameter(SNAME("v"), color_picker->v);
  533. circle->draw_rect(Rect2(Point2(), circle->get_size()), Color(1, 1, 1));
  534. }
  535. void ColorPickerShapeVHSCircle::_circle_overlay_draw() {
  536. draw_focus_circle(circle_overlay);
  537. draw_circle_cursor(circle_overlay, color_picker->h);
  538. }
  539. void ColorPickerShapeVHSCircle::_value_slider_draw() {
  540. const Vector2 size = value_slider->get_size();
  541. PackedVector2Array points{
  542. Vector2(),
  543. Vector2(size.x, 0),
  544. size,
  545. Vector2(0, size.y)
  546. };
  547. Color color = Color::from_hsv(color_picker->h, color_picker->s, 1);
  548. PackedColorArray colors = {
  549. color,
  550. color,
  551. Color(),
  552. Color()
  553. };
  554. value_slider->draw_polygon(points, colors);
  555. draw_focus_rect(value_slider);
  556. int y = size.y * (1 - CLAMP(color_picker->v, 0, 1));
  557. color.set_hsv(color_picker->h, 1, color_picker->v);
  558. value_slider->draw_line(Vector2(0, y), Vector2(size.x, y), color.inverted());
  559. }
  560. void ColorPickerShapeVHSCircle::_update_cursor(const Vector2 &p_color_change_vector, bool p_is_echo) {
  561. if (circle_overlay->has_focus()) {
  562. const Vector2 center = circle_overlay->get_size() / 2.0;
  563. const Vector2 hue_offset = center * Vector2(Math::cos(color_picker->h * Math::TAU), Math::sin(color_picker->h * Math::TAU)) * color_picker->s;
  564. update_circle_cursor(p_color_change_vector, center, hue_offset);
  565. } else if (value_slider->has_focus()) {
  566. color_picker->v = CLAMP(color_picker->v - p_color_change_vector.y * echo_multiplier / 100.0, 0, 1);
  567. }
  568. }
  569. void ColorPickerShapeOKHSLCircle::_circle_input(const Ref<InputEvent> &p_event) {
  570. handle_cursor_editing(p_event, circle_overlay);
  571. Vector2 event_position;
  572. bool is_click = false;
  573. if (!can_handle(p_event, event_position, &is_click)) {
  574. return;
  575. }
  576. const Vector2 center = circle->get_size() * 0.5;
  577. real_t dist = center.distance_to(event_position);
  578. if (is_click && dist > center.x) {
  579. // Clicked outside the circle.
  580. cancel_event();
  581. return;
  582. }
  583. real_t rad = center.angle_to_point(event_position);
  584. color_picker->h = ((rad >= 0) ? rad : (Math::TAU + rad)) / Math::TAU;
  585. color_picker->s = CLAMP(dist / center.x, 0, 1);
  586. color_picker->ok_hsl_h = color_picker->h;
  587. color_picker->ok_hsl_s = color_picker->s;
  588. circle_keyboard_joypad_picker_cursor_position = Vector2i();
  589. apply_color();
  590. }
  591. void ColorPickerShapeOKHSLCircle::_value_slider_input(const Ref<InputEvent> &p_event) {
  592. handle_cursor_editing(p_event, value_slider);
  593. Vector2 event_position;
  594. if (!can_handle(p_event, event_position)) {
  595. return;
  596. }
  597. color_picker->ok_hsl_l = 1.0 - CLAMP(event_position.y / value_slider->get_size().y, 0.0, 1.0);
  598. apply_color();
  599. }
  600. void ColorPickerShapeOKHSLCircle::_circle_draw() {
  601. Ref<ShaderMaterial> material = circle->get_material();
  602. material->set_shader_parameter(SNAME("ok_hsl_l"), color_picker->ok_hsl_l);
  603. circle->draw_rect(Rect2(Point2(), circle->get_size()), Color(1, 1, 1));
  604. }
  605. void ColorPickerShapeOKHSLCircle::_circle_overlay_draw() {
  606. draw_focus_circle(circle_overlay);
  607. draw_circle_cursor(circle_overlay, color_picker->ok_hsl_h);
  608. }
  609. void ColorPickerShapeOKHSLCircle::_value_slider_draw() {
  610. const float ok_hsl_h = color_picker->ok_hsl_h;
  611. const float ok_hsl_s = color_picker->ok_hsl_s;
  612. const float ok_hsl_l = color_picker->ok_hsl_l;
  613. const Vector2 size = value_slider->get_size();
  614. PackedVector2Array points{
  615. Vector2(size.x, 0),
  616. Vector2(size.x, size.y * 0.5),
  617. size,
  618. Vector2(0, size.y),
  619. Vector2(0, size.y * 0.5),
  620. Vector2()
  621. };
  622. Color color1 = Color::from_ok_hsl(ok_hsl_h, ok_hsl_s, 1);
  623. Color color2 = Color::from_ok_hsl(ok_hsl_h, ok_hsl_s, 0.5);
  624. Color color3 = Color::from_ok_hsl(ok_hsl_h, ok_hsl_s, 0);
  625. PackedColorArray colors = {
  626. color1,
  627. color2,
  628. color3,
  629. color3,
  630. color2,
  631. color1,
  632. };
  633. value_slider->draw_polygon(points, colors);
  634. draw_focus_rect(value_slider);
  635. int y = size.y * (1 - CLAMP(ok_hsl_l, 0, 1));
  636. value_slider->draw_line(Vector2(0, y), Vector2(size.x, y), Color::from_hsv(ok_hsl_h, 1, ok_hsl_l).inverted());
  637. }
  638. void ColorPickerShapeOKHSLCircle::_update_cursor(const Vector2 &p_color_change_vector, bool p_is_echo) {
  639. if (circle_overlay->has_focus()) {
  640. const Vector2 center = circle_overlay->get_size() / 2.0;
  641. const Vector2 hue_offset = center * Vector2(Math::cos(color_picker->ok_hsl_h * Math::TAU), Math::sin(color_picker->ok_hsl_h * Math::TAU)) * color_picker->ok_hsl_s;
  642. update_circle_cursor(p_color_change_vector, center, hue_offset);
  643. color_picker->ok_hsl_h = color_picker->h;
  644. color_picker->ok_hsl_s = color_picker->s;
  645. } else if (value_slider->has_focus()) {
  646. color_picker->ok_hsl_l = CLAMP(color_picker->ok_hsl_l - p_color_change_vector.y * echo_multiplier / 100.0, 0, 1);
  647. }
  648. }