color_picker_shape.cpp 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985
  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_okhsl_to_normalized();
  64. color_picker->_normalized_apply_intensity_to_color();
  65. color_picker->hsv_cached = true;
  66. color_picker->okhsl_cached = true;
  67. color_picker->_set_pick_color(color_picker->color, true, false);
  68. if (!color_picker->deferred_mode_enabled) {
  69. _emit_color_changed();
  70. }
  71. }
  72. void ColorPickerShape::cancel_event() {
  73. is_dragging = false;
  74. }
  75. void ColorPickerShape::draw_focus_rect(Control *p_control, const Rect2 &p_rect) {
  76. if (!p_control->has_focus()) {
  77. return;
  78. }
  79. Rect2 focus_rect;
  80. if (p_rect.has_area()) {
  81. focus_rect = p_rect;
  82. } else {
  83. focus_rect = Rect2(Vector2(), p_control->get_size());
  84. }
  85. const RID ci = p_control->get_canvas_item();
  86. if (!cursor_editing) {
  87. RenderingServer::get_singleton()->canvas_item_add_rect(ci, focus_rect, color_picker->theme_cache.focused_not_editing_cursor_color);
  88. }
  89. color_picker->theme_cache.picker_focus_rectangle->draw(ci, focus_rect);
  90. }
  91. void ColorPickerShape::draw_focus_circle(Control *p_control) {
  92. if (!p_control->has_focus()) {
  93. return;
  94. }
  95. const Rect2 focus_rect(Vector2(), p_control->get_size());
  96. const RID ci = p_control->get_canvas_item();
  97. if (!cursor_editing) {
  98. 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);
  99. }
  100. color_picker->theme_cache.picker_focus_circle->draw(ci, focus_rect);
  101. }
  102. void ColorPickerShape::draw_sv_square(Control *p_control, const Rect2 &p_square, bool p_draw_focus) {
  103. const Vector2 end = p_square.get_end();
  104. PackedVector2Array points = {
  105. p_square.position,
  106. Vector2(end.x, p_square.position.y),
  107. end,
  108. Vector2(p_square.position.x, end.y),
  109. };
  110. Color color1 = Color::from_hsv(color_picker->h, 1, 1);
  111. Color color2 = Color::from_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, float p_saturation) {
  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) * p_saturation),
  145. center.y + (center.y * Math::sin(p_hue * Math::TAU) * p_saturation));
  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. void ColorPickerShapeOKHSRectangle::_initialize_controls() {
  302. rectangle_margin = memnew(MarginContainer);
  303. color_picker->shape_container->add_child(rectangle_margin);
  304. Ref<ShaderMaterial> material;
  305. material.instantiate();
  306. material->set_shader(_get_shader());
  307. square = memnew(Control);
  308. rectangle_margin->add_child(square);
  309. square->connect(SceneStringName(draw), callable_mp(this, &ColorPickerShapeOKHSRectangle::_square_draw));
  310. square->set_material(material);
  311. square_overlay = memnew(Control);
  312. rectangle_margin->add_child(square_overlay);
  313. square_overlay->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
  314. square_overlay->connect(SceneStringName(gui_input), callable_mp(this, &ColorPickerShapeOKHSRectangle::_square_overlay_input));
  315. square_overlay->connect(SceneStringName(draw), callable_mp(this, &ColorPickerShapeOKHSRectangle::_square_overlay_draw));
  316. connect_shape_focus(square_overlay);
  317. value_slider = memnew(Control);
  318. color_picker->shape_container->add_child(value_slider);
  319. value_slider->connect(SceneStringName(gui_input), callable_mp(this, &ColorPickerShapeOKHSRectangle::_value_slider_input));
  320. value_slider->connect(SceneStringName(draw), callable_mp(this, &ColorPickerShapeOKHSRectangle::_value_slider_draw));
  321. connect_shape_focus(value_slider);
  322. controls.append(rectangle_margin);
  323. controls.append(square);
  324. controls.append(square_overlay);
  325. controls.append(value_slider);
  326. }
  327. void ColorPickerShapeOKHSRectangle::update_theme() {
  328. const ColorPicker::ThemeCache &theme_cache = color_picker->theme_cache;
  329. rectangle_margin->set_custom_minimum_size(Size2(theme_cache.sv_width, theme_cache.sv_height));
  330. value_slider->set_custom_minimum_size(Size2(theme_cache.h_width, 0));
  331. }
  332. void ColorPickerShapeOKHSRectangle::grab_focus() {
  333. square_overlay->grab_focus();
  334. }
  335. void ColorPickerShapeOKHSRectangle::_update_cursor(const Vector2 &p_color_change_vector, bool p_is_echo) {
  336. if (square_overlay->has_focus()) {
  337. color_picker->ok_hsl_h = CLAMP(color_picker->ok_hsl_h + p_color_change_vector.x / 100.0, 0, 1);
  338. color_picker->ok_hsl_s = CLAMP(color_picker->ok_hsl_s - p_color_change_vector.y / 100.0, 0, 1);
  339. } else if (value_slider->has_focus()) {
  340. color_picker->ok_hsl_l = CLAMP(color_picker->ok_hsl_l + p_color_change_vector.y * echo_multiplier / 360.0, 0, 1);
  341. }
  342. }
  343. void ColorPickerShapeOKHSRectangle::_square_draw() {
  344. Ref<ShaderMaterial> material = square->get_material();
  345. material->set_shader_parameter(SNAME("ok_hsl_l"), color_picker->ok_hsl_l);
  346. square->draw_rect(Rect2(Point2(), square->get_size()), Color(1, 1, 1));
  347. }
  348. void ColorPickerShapeOKHSRectangle::_square_overlay_input(const Ref<InputEvent> &p_event) {
  349. handle_cursor_editing(p_event, square_overlay);
  350. Vector2 event_position;
  351. if (!can_handle(p_event, event_position)) {
  352. return;
  353. }
  354. event_position = (event_position / square_overlay->get_size()).clampf(0.0, 1.0);
  355. color_picker->ok_hsl_h = event_position.x;
  356. color_picker->ok_hsl_s = 1.0 - event_position.y;
  357. apply_color();
  358. }
  359. void ColorPickerShapeOKHSRectangle::_square_overlay_draw() {
  360. const Rect2 rect = Rect2(Vector2(), square_overlay->get_size());
  361. const Vector2 end = rect.get_end();
  362. Vector2 cursor_pos;
  363. cursor_pos.x = CLAMP(rect.position.x + rect.size.x * color_picker->ok_hsl_h, rect.position.x, end.x);
  364. cursor_pos.y = CLAMP(rect.position.y + rect.size.y * (1.0 - color_picker->ok_hsl_s), rect.position.y, end.y);
  365. draw_focus_rect(square_overlay);
  366. draw_cursor(square_overlay, cursor_pos);
  367. }
  368. void ColorPickerShapeOKHSRectangle::_value_slider_input(const Ref<InputEvent> &p_event) {
  369. handle_cursor_editing(p_event, value_slider);
  370. Vector2 event_position;
  371. if (!can_handle(p_event, event_position)) {
  372. return;
  373. }
  374. color_picker->ok_hsl_l = 1 - CLAMP(event_position.y / value_slider->get_size().y, 0.0, 1.0);
  375. apply_color();
  376. }
  377. void ColorPickerShapeOKHSRectangle::_value_slider_draw() {
  378. const float ok_hsl_h = color_picker->ok_hsl_h;
  379. const float ok_hsl_s = color_picker->ok_hsl_s;
  380. const Vector2 size = value_slider->get_size();
  381. PackedVector2Array points{
  382. Vector2(size.x, 0),
  383. Vector2(size.x, size.y * 0.5),
  384. size,
  385. Vector2(0, size.y),
  386. Vector2(0, size.y * 0.5),
  387. Vector2(),
  388. };
  389. Color color1 = Color::from_ok_hsl(ok_hsl_h, ok_hsl_s, 1);
  390. Color color2 = Color::from_ok_hsl(ok_hsl_h, ok_hsl_s, 0.5);
  391. Color color3 = Color::from_ok_hsl(ok_hsl_h, ok_hsl_s, 0);
  392. PackedColorArray colors = {
  393. color1,
  394. color2,
  395. color3,
  396. color3,
  397. color2,
  398. color1,
  399. };
  400. value_slider->draw_polygon(points, colors);
  401. draw_focus_rect(value_slider);
  402. int y = size.y * (1 - CLAMP(color_picker->ok_hsl_l, 0, 1));
  403. const Color color = Color::from_ok_hsl(ok_hsl_h, 1, color_picker->ok_hsl_l);
  404. value_slider->draw_line(Vector2(0, y), Vector2(size.x, y), color.inverted());
  405. }
  406. void ColorPickerShapeOKHLRectangle::_update_cursor(const Vector2 &p_color_change_vector, bool p_is_echo) {
  407. if (square_overlay->has_focus()) {
  408. color_picker->ok_hsl_h = CLAMP(color_picker->ok_hsl_h + p_color_change_vector.x / 100.0, 0, 1);
  409. color_picker->ok_hsl_l = CLAMP(color_picker->ok_hsl_l - p_color_change_vector.y / 100.0, 0, 1);
  410. } else if (value_slider->has_focus()) {
  411. color_picker->ok_hsl_s = CLAMP(color_picker->ok_hsl_s + p_color_change_vector.y * echo_multiplier / 360.0, 0, 1);
  412. }
  413. }
  414. void ColorPickerShapeOKHLRectangle::_square_overlay_input(const Ref<InputEvent> &p_event) {
  415. handle_cursor_editing(p_event, square_overlay);
  416. Vector2 event_position;
  417. if (!can_handle(p_event, event_position)) {
  418. return;
  419. }
  420. event_position = (event_position / square_overlay->get_size()).clampf(0.0, 1.0);
  421. color_picker->ok_hsl_h = event_position.x;
  422. color_picker->ok_hsl_l = 1.0 - event_position.y;
  423. apply_color();
  424. }
  425. void ColorPickerShapeOKHLRectangle::_square_overlay_draw() {
  426. const Rect2 rect = Rect2(Vector2(), square_overlay->get_size());
  427. const Vector2 end = rect.get_end();
  428. Vector2 cursor_pos;
  429. cursor_pos.x = CLAMP(rect.position.x + rect.size.x * color_picker->ok_hsl_h, rect.position.x, end.x);
  430. cursor_pos.y = CLAMP(rect.position.y + rect.size.y * (1.0 - color_picker->ok_hsl_l), rect.position.y, end.y);
  431. draw_focus_rect(square_overlay);
  432. draw_cursor(square_overlay, cursor_pos);
  433. }
  434. void ColorPickerShapeOKHLRectangle::_square_draw() {
  435. Ref<ShaderMaterial> material = square->get_material();
  436. material->set_shader_parameter(SNAME("ok_hsl_s"), color_picker->ok_hsl_s);
  437. square->draw_rect(Rect2(Point2(), square->get_size()), Color(1, 1, 1));
  438. }
  439. void ColorPickerShapeOKHLRectangle::_value_slider_input(const Ref<InputEvent> &p_event) {
  440. handle_cursor_editing(p_event, value_slider);
  441. Vector2 event_position;
  442. if (!can_handle(p_event, event_position)) {
  443. return;
  444. }
  445. color_picker->ok_hsl_s = 1 - CLAMP(event_position.y / value_slider->get_size().y, 0.0, 1.0);
  446. apply_color();
  447. }
  448. void ColorPickerShapeOKHLRectangle::_value_slider_draw() {
  449. const float ok_hsl_h = color_picker->ok_hsl_h;
  450. const float ok_hsl_l = color_picker->ok_hsl_l;
  451. const Vector2 size = value_slider->get_size();
  452. PackedVector2Array points{
  453. Vector2(size.x, 0),
  454. Vector2(size.x, size.y * 0.5),
  455. size,
  456. Vector2(0, size.y),
  457. Vector2(0, size.y * 0.5),
  458. Vector2(),
  459. };
  460. Color color1 = Color::from_ok_hsl(ok_hsl_h, 1, ok_hsl_l);
  461. Color color2 = Color::from_ok_hsl(ok_hsl_h, 0.5, ok_hsl_l);
  462. Color color3 = Color::from_ok_hsl(ok_hsl_h, 0, ok_hsl_l);
  463. PackedColorArray colors = {
  464. color1,
  465. color2,
  466. color3,
  467. color3,
  468. color2,
  469. color1,
  470. };
  471. value_slider->draw_polygon(points, colors);
  472. draw_focus_rect(value_slider);
  473. int y = size.y * (1 - CLAMP(color_picker->ok_hsl_s, 0, 1));
  474. const Color color = Color::from_ok_hsl(ok_hsl_h, 1, ok_hsl_l);
  475. value_slider->draw_line(Vector2(0, y), Vector2(size.x, y), color.inverted());
  476. }
  477. float ColorPickerShapeWheel::_get_h_on_wheel(const Vector2 &p_color_change_vector) {
  478. int h_change = get_edge_h_change(p_color_change_vector);
  479. float target_h = Math::wrapf(color_picker->h + h_change / 360.0, 0, 1);
  480. int current_quarter = color_picker->h * 4;
  481. int future_quarter = target_h * 4;
  482. if (p_color_change_vector.y > 0 && ((future_quarter == 0 && current_quarter == 1) || (future_quarter == 1 && current_quarter == 0))) {
  483. rotate_next_echo_event = !rotate_next_echo_event;
  484. } else if (p_color_change_vector.y < 0 && ((future_quarter == 2 && current_quarter == 3) || (future_quarter == 3 && current_quarter == 2))) {
  485. rotate_next_echo_event = !rotate_next_echo_event;
  486. } else if (p_color_change_vector.x < 0 && ((future_quarter == 1 && current_quarter == 2) || (future_quarter == 2 && current_quarter == 1))) {
  487. rotate_next_echo_event = !rotate_next_echo_event;
  488. } else if (p_color_change_vector.x > 0 && ((future_quarter == 3 && current_quarter == 0) || (future_quarter == 0 && current_quarter == 3))) {
  489. rotate_next_echo_event = !rotate_next_echo_event;
  490. }
  491. return target_h;
  492. }
  493. void ColorPickerShapeWheel::_reset_wheel_focus() {
  494. wheel_focused = true;
  495. }
  496. void ColorPickerShapeWheel::_wheel_input(const Ref<InputEvent> &p_event) {
  497. handle_cursor_editing(p_event, wheel_uv);
  498. if (!cursor_editing) {
  499. // Wheel and inner square are the same control, so focus has to be moved manually.
  500. if (!wheel_focused && p_event->is_action_pressed("ui_down", true)) {
  501. wheel_focused = true;
  502. wheel_uv->queue_redraw();
  503. color_picker->accept_event();
  504. return;
  505. } else if (wheel_focused && p_event->is_action_pressed("ui_up", true)) {
  506. wheel_focused = false;
  507. wheel_uv->queue_redraw();
  508. color_picker->accept_event();
  509. return;
  510. }
  511. }
  512. Vector2 event_position;
  513. bool is_click = false;
  514. if (!can_handle(p_event, event_position, &is_click)) {
  515. if (is_click) {
  516. // Released mouse button while dragging wheel.
  517. spinning = false;
  518. }
  519. return;
  520. }
  521. const Vector2 uv_size = wheel_uv->get_size();
  522. const Vector2 ring_radius = uv_size * Math::SQRT12 * WHEEL_RADIUS;
  523. const Vector2 center = uv_size * 0.5;
  524. if (is_click && !spinning) {
  525. real_t dist = center.distance_to(event_position);
  526. if (dist >= center.x * WHEEL_RADIUS * 2.0 && dist <= center.x) {
  527. spinning = true;
  528. if (!wheel_focused) {
  529. cursor_editing = true;
  530. wheel_focused = true;
  531. }
  532. } else if (dist > center.x) {
  533. // Clicked outside the wheel.
  534. cancel_event();
  535. return;
  536. }
  537. };
  538. if (spinning) {
  539. real_t rad = center.angle_to_point(event_position);
  540. color_picker->h = ((rad >= 0) ? rad : (Math::TAU + rad)) / Math::TAU;
  541. apply_color();
  542. return;
  543. }
  544. const Rect2 uv_rect(center - ring_radius, ring_radius * 2.0);
  545. event_position -= uv_rect.position;
  546. event_position /= uv_rect.size;
  547. if (is_click && (event_position.x < 0 || event_position.x > 1 || event_position.y < 0 || event_position.y > 1)) {
  548. // Clicked inside the wheel, but outside the square.
  549. cancel_event();
  550. return;
  551. }
  552. event_position = event_position.clampf(0.0, 1.0);
  553. color_picker->s = event_position.x;
  554. color_picker->v = 1.0 - event_position.y;
  555. if (wheel_focused) {
  556. cursor_editing = true;
  557. wheel_focused = false;
  558. }
  559. apply_color();
  560. }
  561. void ColorPickerShapeWheel::_wheel_draw() {
  562. wheel->draw_rect(Rect2(Point2(), wheel->get_size()), Color(1, 1, 1));
  563. }
  564. void ColorPickerShapeWheel::_wheel_uv_draw() {
  565. const Vector2 uv_size = wheel_uv->get_size();
  566. const Vector2 ring_radius = uv_size * Math::SQRT12 * WHEEL_RADIUS;
  567. const Vector2 center = uv_size * 0.5;
  568. const Rect2 uv_rect(center - ring_radius, ring_radius * 2.0);
  569. draw_sv_square(wheel_uv, uv_rect, !wheel_focused);
  570. if (wheel_focused) {
  571. draw_focus_circle(wheel_uv);
  572. }
  573. float radius = WHEEL_RADIUS * 2.0;
  574. radius += (1.0 - radius) * 0.5;
  575. const Vector2 cursor_pos = center +
  576. Vector2(center.x * Math::cos(color_picker->h * Math::TAU) * radius,
  577. center.y * Math::sin(color_picker->h * Math::TAU) * radius);
  578. draw_cursor(wheel_uv, cursor_pos, false);
  579. }
  580. void ColorPickerShapeWheel::_initialize_controls() {
  581. wheel_margin = memnew(MarginContainer);
  582. color_picker->shape_container->add_child(wheel_margin);
  583. Ref<ShaderMaterial> material;
  584. material.instantiate();
  585. material->set_shader(ColorPicker::wheel_shader);
  586. material->set_shader_parameter("wheel_radius", WHEEL_RADIUS);
  587. wheel = memnew(Control);
  588. wheel->set_material(material);
  589. wheel_margin->add_child(wheel);
  590. wheel->connect(SceneStringName(draw), callable_mp(this, &ColorPickerShapeWheel::_wheel_draw));
  591. wheel_uv = memnew(Control);
  592. wheel_margin->add_child(wheel_uv);
  593. wheel_uv->connect(SceneStringName(focus_entered), callable_mp(this, &ColorPickerShapeWheel::_reset_wheel_focus));
  594. wheel_uv->connect(SceneStringName(gui_input), callable_mp(this, &ColorPickerShapeWheel::_wheel_input));
  595. wheel_uv->connect(SceneStringName(draw), callable_mp(this, &ColorPickerShapeWheel::_wheel_uv_draw));
  596. connect_shape_focus(wheel_uv);
  597. controls.append(wheel_margin);
  598. controls.append(wheel);
  599. controls.append(wheel_uv);
  600. }
  601. void ColorPickerShapeWheel::_update_cursor(const Vector2 &p_color_change_vector, bool p_is_echo) {
  602. if (wheel_focused) {
  603. if (p_is_echo && rotate_next_echo_event) {
  604. color_picker->h = _get_h_on_wheel(-p_color_change_vector);
  605. } else {
  606. rotate_next_echo_event = false;
  607. color_picker->h = _get_h_on_wheel(p_color_change_vector);
  608. }
  609. } else {
  610. color_picker->s = CLAMP(color_picker->s + p_color_change_vector.x / 100.0, 0, 1);
  611. color_picker->v = CLAMP(color_picker->v - p_color_change_vector.y / 100.0, 0, 1);
  612. }
  613. }
  614. void ColorPickerShapeWheel::update_theme() {
  615. const ColorPicker::ThemeCache &theme_cache = color_picker->theme_cache;
  616. wheel_margin->set_custom_minimum_size(Size2(theme_cache.sv_width, theme_cache.sv_height));
  617. wheel_margin->add_theme_constant_override(SNAME("margin_bottom"), 8 * theme_cache.base_scale);
  618. }
  619. void ColorPickerShapeWheel::grab_focus() {
  620. wheel_uv->grab_focus();
  621. }
  622. void ColorPickerShapeCircle::update_circle_cursor(const Vector2 &p_color_change_vector, const Vector2 &p_center, const Vector2 &p_hue_offset) {
  623. if (circle_keyboard_joypad_picker_cursor_position == Vector2i()) {
  624. circle_keyboard_joypad_picker_cursor_position = p_center + p_hue_offset;
  625. }
  626. Vector2i potential_cursor_position = circle_keyboard_joypad_picker_cursor_position + p_color_change_vector;
  627. real_t potential_new_cursor_distance = p_center.distance_to(potential_cursor_position);
  628. real_t dist_pre = p_center.distance_to(circle_keyboard_joypad_picker_cursor_position);
  629. if (color_picker->s < 1 || potential_new_cursor_distance < dist_pre) {
  630. circle_keyboard_joypad_picker_cursor_position += p_color_change_vector;
  631. real_t dist = p_center.distance_to(circle_keyboard_joypad_picker_cursor_position);
  632. real_t rad = p_center.angle_to_point(circle_keyboard_joypad_picker_cursor_position);
  633. color_picker->h = ((rad >= 0) ? rad : (Math::TAU + rad)) / Math::TAU;
  634. color_picker->s = CLAMP(dist / p_center.x, 0, 1);
  635. } else {
  636. color_picker->h = get_h_on_circle_edge(p_color_change_vector);
  637. circle_keyboard_joypad_picker_cursor_position = Vector2i();
  638. }
  639. }
  640. void ColorPickerShapeCircle::_initialize_controls() {
  641. circle_margin = memnew(MarginContainer);
  642. color_picker->shape_container->add_child(circle_margin);
  643. Ref<ShaderMaterial> material;
  644. material.instantiate();
  645. material->set_shader(_get_shader());
  646. circle = memnew(Control);
  647. circle->set_material(material);
  648. circle_margin->add_child(circle);
  649. circle->connect(SceneStringName(draw), callable_mp(this, &ColorPickerShapeCircle::_circle_draw));
  650. circle_overlay = memnew(Control);
  651. circle_overlay->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
  652. circle->add_child(circle_overlay);
  653. circle_overlay->connect(SceneStringName(gui_input), callable_mp(this, &ColorPickerShapeCircle::_circle_input));
  654. circle_overlay->connect(SceneStringName(draw), callable_mp(this, &ColorPickerShapeCircle::_circle_overlay_draw));
  655. connect_shape_focus(circle_overlay);
  656. value_slider = memnew(Control);
  657. color_picker->shape_container->add_child(value_slider);
  658. value_slider->connect(SceneStringName(gui_input), callable_mp(this, &ColorPickerShapeCircle::_value_slider_input));
  659. value_slider->connect(SceneStringName(draw), callable_mp(this, &ColorPickerShapeCircle::_value_slider_draw));
  660. connect_shape_focus(value_slider);
  661. controls.append(circle_margin);
  662. controls.append(circle);
  663. controls.append(circle_overlay);
  664. controls.append(value_slider);
  665. }
  666. void ColorPickerShapeCircle::update_theme() {
  667. const ColorPicker::ThemeCache &theme_cache = color_picker->theme_cache;
  668. circle_margin->set_custom_minimum_size(Size2(theme_cache.sv_width, theme_cache.sv_height));
  669. circle_margin->add_theme_constant_override(SNAME("margin_bottom"), 8 * theme_cache.base_scale);
  670. value_slider->set_custom_minimum_size(Size2(theme_cache.h_width, 0));
  671. }
  672. void ColorPickerShapeCircle::grab_focus() {
  673. circle_overlay->grab_focus();
  674. }
  675. void ColorPickerShapeVHSCircle::_circle_input(const Ref<InputEvent> &p_event) {
  676. handle_cursor_editing(p_event, circle_overlay);
  677. Vector2 event_position;
  678. bool is_click = false;
  679. if (!can_handle(p_event, event_position, &is_click)) {
  680. return;
  681. }
  682. Vector2 center = circle->get_size() * 0.5;
  683. real_t dist = center.distance_to(event_position);
  684. if (is_click && dist > center.x) {
  685. // Clicked outside the circle.
  686. cancel_event();
  687. return;
  688. }
  689. real_t rad = center.angle_to_point(event_position);
  690. color_picker->h = ((rad >= 0) ? rad : (Math::TAU + rad)) / Math::TAU;
  691. color_picker->s = CLAMP(dist / center.x, 0, 1);
  692. color_picker->ok_hsl_h = color_picker->h;
  693. color_picker->ok_hsl_s = color_picker->s;
  694. circle_keyboard_joypad_picker_cursor_position = Vector2i();
  695. apply_color();
  696. }
  697. void ColorPickerShapeVHSCircle::_value_slider_input(const Ref<InputEvent> &p_event) {
  698. handle_cursor_editing(p_event, value_slider);
  699. Vector2 event_position;
  700. if (!can_handle(p_event, event_position)) {
  701. return;
  702. }
  703. color_picker->v = 1.0 - CLAMP(event_position.y / value_slider->get_size().y, 0.0, 1.0);
  704. apply_color();
  705. }
  706. void ColorPickerShapeVHSCircle::_circle_draw() {
  707. Ref<ShaderMaterial> material = circle->get_material();
  708. material->set_shader_parameter(SNAME("v"), color_picker->v);
  709. circle->draw_rect(Rect2(Point2(), circle->get_size()), Color(1, 1, 1));
  710. }
  711. void ColorPickerShapeVHSCircle::_circle_overlay_draw() {
  712. draw_focus_circle(circle_overlay);
  713. draw_circle_cursor(circle_overlay, color_picker->h, color_picker->s);
  714. }
  715. void ColorPickerShapeVHSCircle::_value_slider_draw() {
  716. const Vector2 size = value_slider->get_size();
  717. PackedVector2Array points{
  718. Vector2(),
  719. Vector2(size.x, 0),
  720. size,
  721. Vector2(0, size.y),
  722. };
  723. Color color = Color::from_hsv(color_picker->h, color_picker->s, 1);
  724. PackedColorArray colors = {
  725. color,
  726. color,
  727. Color(),
  728. Color(),
  729. };
  730. value_slider->draw_polygon(points, colors);
  731. draw_focus_rect(value_slider);
  732. int y = size.y * (1 - CLAMP(color_picker->v, 0, 1));
  733. color.set_hsv(color_picker->h, 1, color_picker->v);
  734. value_slider->draw_line(Vector2(0, y), Vector2(size.x, y), color.inverted());
  735. }
  736. void ColorPickerShapeVHSCircle::_update_cursor(const Vector2 &p_color_change_vector, bool p_is_echo) {
  737. if (circle_overlay->has_focus()) {
  738. const Vector2 center = circle_overlay->get_size() / 2.0;
  739. const Vector2 hue_offset = center * Vector2(Math::cos(color_picker->h * Math::TAU), Math::sin(color_picker->h * Math::TAU)) * color_picker->s;
  740. update_circle_cursor(p_color_change_vector, center, hue_offset);
  741. } else if (value_slider->has_focus()) {
  742. color_picker->v = CLAMP(color_picker->v - p_color_change_vector.y * echo_multiplier / 100.0, 0, 1);
  743. }
  744. }
  745. void ColorPickerShapeOKHSLCircle::_circle_input(const Ref<InputEvent> &p_event) {
  746. handle_cursor_editing(p_event, circle_overlay);
  747. Vector2 event_position;
  748. bool is_click = false;
  749. if (!can_handle(p_event, event_position, &is_click)) {
  750. return;
  751. }
  752. const Vector2 center = circle->get_size() * 0.5;
  753. real_t dist = center.distance_to(event_position);
  754. if (is_click && dist > center.x) {
  755. // Clicked outside the circle.
  756. cancel_event();
  757. return;
  758. }
  759. real_t rad = center.angle_to_point(event_position);
  760. color_picker->h = ((rad >= 0) ? rad : (Math::TAU + rad)) / Math::TAU;
  761. color_picker->s = CLAMP(dist / center.x, 0, 1);
  762. color_picker->ok_hsl_h = color_picker->h;
  763. color_picker->ok_hsl_s = color_picker->s;
  764. circle_keyboard_joypad_picker_cursor_position = Vector2i();
  765. apply_color();
  766. }
  767. void ColorPickerShapeOKHSLCircle::_value_slider_input(const Ref<InputEvent> &p_event) {
  768. handle_cursor_editing(p_event, value_slider);
  769. Vector2 event_position;
  770. if (!can_handle(p_event, event_position)) {
  771. return;
  772. }
  773. color_picker->ok_hsl_l = 1.0 - CLAMP(event_position.y / value_slider->get_size().y, 0.0, 1.0);
  774. apply_color();
  775. }
  776. void ColorPickerShapeOKHSLCircle::_circle_draw() {
  777. Ref<ShaderMaterial> material = circle->get_material();
  778. material->set_shader_parameter(SNAME("ok_hsl_l"), color_picker->ok_hsl_l);
  779. circle->draw_rect(Rect2(Point2(), circle->get_size()), Color(1, 1, 1));
  780. }
  781. void ColorPickerShapeOKHSLCircle::_circle_overlay_draw() {
  782. draw_focus_circle(circle_overlay);
  783. draw_circle_cursor(circle_overlay, color_picker->ok_hsl_h, color_picker->ok_hsl_s);
  784. }
  785. void ColorPickerShapeOKHSLCircle::_value_slider_draw() {
  786. const float ok_hsl_h = color_picker->ok_hsl_h;
  787. const float ok_hsl_s = color_picker->ok_hsl_s;
  788. const float ok_hsl_l = color_picker->ok_hsl_l;
  789. const Vector2 size = value_slider->get_size();
  790. PackedVector2Array points{
  791. Vector2(size.x, 0),
  792. Vector2(size.x, size.y * 0.5),
  793. size,
  794. Vector2(0, size.y),
  795. Vector2(0, size.y * 0.5),
  796. Vector2(),
  797. };
  798. Color color1 = Color::from_ok_hsl(ok_hsl_h, ok_hsl_s, 1);
  799. Color color2 = Color::from_ok_hsl(ok_hsl_h, ok_hsl_s, 0.5);
  800. Color color3 = Color::from_ok_hsl(ok_hsl_h, ok_hsl_s, 0);
  801. PackedColorArray colors = {
  802. color1,
  803. color2,
  804. color3,
  805. color3,
  806. color2,
  807. color1,
  808. };
  809. value_slider->draw_polygon(points, colors);
  810. draw_focus_rect(value_slider);
  811. int y = size.y * (1 - CLAMP(ok_hsl_l, 0, 1));
  812. value_slider->draw_line(Vector2(0, y), Vector2(size.x, y), Color::from_ok_hsl(ok_hsl_h, 1, ok_hsl_l).inverted());
  813. }
  814. void ColorPickerShapeOKHSLCircle::_update_cursor(const Vector2 &p_color_change_vector, bool p_is_echo) {
  815. if (circle_overlay->has_focus()) {
  816. const Vector2 center = circle_overlay->get_size() / 2.0;
  817. 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;
  818. update_circle_cursor(p_color_change_vector, center, hue_offset);
  819. color_picker->ok_hsl_h = color_picker->h;
  820. color_picker->ok_hsl_s = color_picker->s;
  821. } else if (value_slider->has_focus()) {
  822. color_picker->ok_hsl_l = CLAMP(color_picker->ok_hsl_l - p_color_change_vector.y * echo_multiplier / 100.0, 0, 1);
  823. }
  824. }