@@ -396,8 +396,237 @@ bool ScriptTextEditor::show_members_overview() {
396396 return true ;
397397}
398398
399+ bool ScriptTextEditor::_is_valid_color_info (const Dictionary &p_info) {
400+ if (p_info.get_valid (" color" ).get_type () != Variant::COLOR) {
401+ return false ;
402+ }
403+ if (!p_info.get_valid (" color_end" ).is_num () || !p_info.get_valid (" color_mode" ).is_num ()) {
404+ return false ;
405+ }
406+ return true ;
407+ }
408+
409+ Array ScriptTextEditor::_inline_object_parse (const String &p_text, int p_line) {
410+ Array result;
411+ int i_end_previous = 0 ;
412+ int i_start = p_text.find (" Color" );
413+
414+ while (i_start != -1 ) {
415+ // Ignore words that just have "Color" in them.
416+ if (i_start == 0 || !(" _" + p_text.substr (i_start - 1 , 1 )).is_valid_ascii_identifier ()) {
417+ int i_par_start = p_text.find_char (' (' , i_start + 5 );
418+ if (i_par_start != -1 ) {
419+ int i_par_end = p_text.find_char (' )' , i_start + 5 );
420+ if (i_par_end != -1 ) {
421+ Dictionary color_info;
422+ color_info[" line" ] = p_line;
423+ color_info[" column" ] = i_start;
424+ color_info[" width_ratio" ] = 1.0 ;
425+ color_info[" color_end" ] = i_par_end;
426+
427+ String fn_name = p_text.substr (i_start + 5 , i_par_start - i_start - 5 );
428+ String s_params = p_text.substr (i_par_start + 1 , i_par_end - i_par_start - 1 );
429+ bool has_added_color = false ;
430+
431+ if (fn_name.is_empty ()) {
432+ String stripped = s_params.strip_edges (true , true );
433+ // String constructor.
434+ if (stripped.length () > 0 && (stripped[0 ] == ' \" ' )) {
435+ String color_string = stripped.substr (1 , stripped.length () - 2 );
436+ color_info[" color" ] = Color (color_string);
437+ color_info[" color_mode" ] = MODE_STRING;
438+ has_added_color = true ;
439+ }
440+ // Hex constructor.
441+ else if (stripped.length () == 10 && stripped.substr (0 , 2 ) == " 0x" ) {
442+ color_info[" color" ] = Color (stripped.substr (2 , stripped.length () - 2 ));
443+ color_info[" color_mode" ] = MODE_HEX;
444+ has_added_color = true ;
445+ }
446+ // Empty Color() constructor.
447+ else if (stripped.is_empty ()) {
448+ color_info[" color" ] = Color ();
449+ color_info[" color_mode" ] = MODE_RGB;
450+ has_added_color = true ;
451+ }
452+ }
453+ // Float & int parameters.
454+ if (!has_added_color && s_params.size () > 0 ) {
455+ PackedFloat64Array params = s_params.split_floats (" ," , false );
456+ if (params.size () == 3 ) {
457+ params.resize (4 );
458+ params.set (3 , 1.0 );
459+ }
460+ if (params.size () == 4 ) {
461+ has_added_color = true ;
462+ if (fn_name == " .from_ok_hsl" ) {
463+ color_info[" color" ] = Color::from_ok_hsl (params[0 ], params[1 ], params[2 ], params[3 ]);
464+ color_info[" color_mode" ] = MODE_OKHSL;
465+ } else if (fn_name == " .from_hsv" ) {
466+ color_info[" color" ] = Color::from_hsv (params[0 ], params[1 ], params[2 ], params[3 ]);
467+ color_info[" color_mode" ] = MODE_HSV;
468+ } else if (fn_name == " .from_rgba8" ) {
469+ color_info[" color" ] = Color::from_rgba8 (int (params[0 ]), int (params[1 ]), int (params[2 ]), int (params[3 ]));
470+ color_info[" color_mode" ] = MODE_RGB8;
471+ } else if (fn_name.is_empty ()) {
472+ color_info[" color" ] = Color (params[0 ], params[1 ], params[2 ], params[3 ]);
473+ color_info[" color_mode" ] = MODE_RGB;
474+ } else {
475+ has_added_color = false ;
476+ }
477+ }
478+ }
479+
480+ if (has_added_color) {
481+ result.push_back (color_info);
482+ i_end_previous = i_par_end + 1 ;
483+ }
484+ }
485+ }
486+ }
487+ i_end_previous = MAX (i_end_previous, i_start);
488+ i_start = p_text.find (" Color" , i_start + 1 );
489+ }
490+ return result;
491+ }
492+
493+ void ScriptTextEditor::_inline_object_draw (const Dictionary &p_info, const Rect2 &p_rect) {
494+ if (_is_valid_color_info (p_info)) {
495+ Rect2 col_rect = p_rect.grow (-4 );
496+ if (color_alpha_texture.is_null ()) {
497+ color_alpha_texture = inline_color_picker->get_theme_icon (" sample_bg" , " ColorPicker" );
498+ }
499+ code_editor->get_text_editor ()->draw_texture_rect (color_alpha_texture, col_rect, false );
500+ code_editor->get_text_editor ()->draw_rect (col_rect, Color (p_info[" color" ]));
501+ code_editor->get_text_editor ()->draw_rect (col_rect, Color (1 , 1 , 1 ), false , 1 );
502+ }
503+ }
504+
505+ void ScriptTextEditor::_inline_object_handle_click (const Dictionary &p_info, const Rect2 &p_rect) {
506+ if (_is_valid_color_info (p_info)) {
507+ inline_color_picker->set_pick_color (p_info[" color" ]);
508+ inline_color_line = p_info[" line" ];
509+ inline_color_start = p_info[" column" ];
510+ inline_color_end = p_info[" color_end" ];
511+
512+ // Reset tooltip hover timer.
513+ code_editor->get_text_editor ()->set_symbol_tooltip_on_hover_enabled (false );
514+ code_editor->get_text_editor ()->set_symbol_tooltip_on_hover_enabled (true );
515+
516+ _update_color_constructor_options ();
517+ inline_color_options->select (p_info[" color_mode" ]);
518+ EditorNode::get_singleton ()->setup_color_picker (inline_color_picker);
519+
520+ // Move popup above the line if it's too low.
521+ float_t view_h = get_viewport_rect ().size .y ;
522+ float_t pop_h = inline_color_popup->get_contents_minimum_size ().y ;
523+ float_t pop_y = p_rect.get_end ().y ;
524+ float_t pop_x = p_rect.position .x ;
525+ if (pop_y + pop_h > view_h) {
526+ pop_y = p_rect.position .y - pop_h;
527+ }
528+ // Move popup to the right if it's too high.
529+ if (pop_y < 0 ) {
530+ pop_x = p_rect.get_end ().x ;
531+ }
532+
533+ inline_color_popup->popup (Rect2 (pop_x, pop_y, 0 , 0 ));
534+ }
535+ }
536+
537+ String ScriptTextEditor::_picker_color_stringify (const Color &p_color, COLOR_MODE p_mode) {
538+ String result;
539+ String fname;
540+ Vector<String> str_params;
541+ switch (p_mode) {
542+ case ScriptTextEditor::MODE_STRING: {
543+ str_params.push_back (" \" " + p_color.to_html () + " \" " );
544+ } break ;
545+ case ScriptTextEditor::MODE_HEX: {
546+ str_params.push_back (" 0x" + p_color.to_html ());
547+ } break ;
548+ case ScriptTextEditor::MODE_RGB: {
549+ str_params = {
550+ String::num (p_color.r , 3 ),
551+ String::num (p_color.g , 3 ),
552+ String::num (p_color.b , 3 ),
553+ String::num (p_color.a , 3 )
554+ };
555+ } break ;
556+ case ScriptTextEditor::MODE_HSV: {
557+ str_params = {
558+ String::num (p_color.get_h (), 3 ),
559+ String::num (p_color.get_s (), 3 ),
560+ String::num (p_color.get_v (), 3 ),
561+ String::num (p_color.a , 3 )
562+ };
563+ fname = " .from_hsv" ;
564+ } break ;
565+ case ScriptTextEditor::MODE_OKHSL: {
566+ str_params = {
567+ String::num (p_color.get_ok_hsl_h (), 3 ),
568+ String::num (p_color.get_ok_hsl_s (), 3 ),
569+ String::num (p_color.get_ok_hsl_l (), 3 ),
570+ String::num (p_color.a , 3 )
571+ };
572+ fname = " .from_ok_hsl" ;
573+ } break ;
574+ case ScriptTextEditor::MODE_RGB8: {
575+ str_params = {
576+ itos (p_color.get_r8 ()),
577+ itos (p_color.get_g8 ()),
578+ itos (p_color.get_b8 ()),
579+ itos (p_color.get_a8 ())
580+ };
581+ fname = " .from_rgba8" ;
582+ } break ;
583+ default : {
584+ } break ;
585+ }
586+ result = " Color" + fname + " (" + String (" , " ).join (str_params) + " )" ;
587+ return result;
588+ }
589+
590+ void ScriptTextEditor::_picker_color_changed (const Color &p_color) {
591+ _update_color_constructor_options ();
592+ _update_color_text ();
593+ }
594+
595+ void ScriptTextEditor::_update_color_constructor_options () {
596+ int item_count = inline_color_options->get_item_count ();
597+ // Update or add each constructor as an option.
598+ for (int i = 0 ; i < MODE_MAX; i++) {
599+ String option_text = _picker_color_stringify (inline_color_picker->get_pick_color (), (COLOR_MODE)i);
600+ if (i >= item_count) {
601+ inline_color_options->add_item (option_text);
602+ } else {
603+ inline_color_options->set_item_text (i, option_text);
604+ }
605+ }
606+ }
607+
608+ void ScriptTextEditor::_update_color_text () {
609+ if (inline_color_line < 0 ) {
610+ return ;
611+ }
612+ String result = inline_color_options->get_item_text (inline_color_options->get_selected_id ());
613+ code_editor->get_text_editor ()->begin_complex_operation ();
614+ code_editor->get_text_editor ()->remove_text (inline_color_line, inline_color_start, inline_color_line, inline_color_end + 1 );
615+ inline_color_end = inline_color_start + result.size () - 2 ;
616+ code_editor->get_text_editor ()->insert_text (result, inline_color_line, inline_color_start);
617+ code_editor->get_text_editor ()->end_complex_operation ();
618+ }
619+
399620void ScriptTextEditor::update_settings () {
400621 code_editor->get_text_editor ()->set_gutter_draw (connection_gutter, EDITOR_GET (" text_editor/appearance/gutters/show_info_gutter" ));
622+ if (EDITOR_GET (" text_editor/appearance/enable_inline_color_picker" )) {
623+ code_editor->get_text_editor ()->set_inline_object_handlers (
624+ callable_mp (this , &ScriptTextEditor::_inline_object_parse),
625+ callable_mp (this , &ScriptTextEditor::_inline_object_draw),
626+ callable_mp (this , &ScriptTextEditor::_inline_object_handle_click));
627+ } else {
628+ code_editor->get_text_editor ()->set_inline_object_handlers (Callable (), Callable (), Callable ());
629+ }
401630 code_editor->update_editor_settings ();
402631}
403632
@@ -1812,6 +2041,9 @@ void ScriptTextEditor::_notification(int p_what) {
18122041 [[fallthrough]];
18132042 case NOTIFICATION_ENTER_TREE: {
18142043 code_editor->get_text_editor ()->set_gutter_width (connection_gutter, code_editor->get_text_editor ()->get_line_height ());
2044+ Ref<Font> code_font = get_theme_font (" font" , " CodeEdit" );
2045+ inline_color_options->add_theme_font_override (" font" , code_font);
2046+ inline_color_options->get_popup ()->add_theme_font_override (" font" , code_font);
18152047 } break ;
18162048 }
18172049}
@@ -2594,6 +2826,23 @@ ScriptTextEditor::ScriptTextEditor() {
25942826 bookmarks_menu = memnew (PopupMenu);
25952827 breakpoints_menu = memnew (PopupMenu);
25962828
2829+ inline_color_popup = memnew (PopupPanel);
2830+ add_child (inline_color_popup);
2831+
2832+ inline_color_picker = memnew (ColorPicker);
2833+ inline_color_picker->set_mouse_filter (MOUSE_FILTER_STOP);
2834+ inline_color_picker->set_deferred_mode (true );
2835+ inline_color_picker->set_hex_visible (false );
2836+ inline_color_picker->connect (" color_changed" , callable_mp (this , &ScriptTextEditor::_picker_color_changed));
2837+ inline_color_popup->add_child (inline_color_picker);
2838+
2839+ inline_color_options = memnew (OptionButton);
2840+ inline_color_options->set_h_size_flags (SIZE_FILL);
2841+ inline_color_options->set_text_overrun_behavior (TextServer::OVERRUN_TRIM_ELLIPSIS);
2842+ inline_color_options->set_fit_to_longest_item (false );
2843+ inline_color_options->connect (" item_selected" , callable_mp (this , &ScriptTextEditor::_update_color_text).unbind (1 ));
2844+ inline_color_picker->get_slider (ColorPicker::SLIDER_COUNT)->get_parent ()->add_sibling (inline_color_options);
2845+
25972846 connection_info_dialog = memnew (ConnectionInfoDialog);
25982847
25992848 SET_DRAG_FORWARDING_GCD (code_editor->get_text_editor (), ScriptTextEditor);
0 commit comments