2014-02-10 01:10:30 +00:00
/*************************************************************************/
/* text_edit.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
2017-08-27 12:16:55 +00:00
/* https://godotengine.org */
2014-02-10 01:10:30 +00:00
/*************************************************************************/
2019-01-01 11:53:14 +00:00
/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
2014-02-10 01:10:30 +00:00
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
2018-01-04 23:50:27 +00:00
2014-02-10 01:10:30 +00:00
# include "text_edit.h"
2017-08-27 19:07:15 +00:00
2018-09-11 16:13:45 +00:00
# include "core/message_queue.h"
# include "core/os/input.h"
# include "core/os/keyboard.h"
# include "core/os/os.h"
# include "core/project_settings.h"
2019-06-13 09:32:03 +00:00
# include "core/script_language.h"
2016-09-11 14:28:01 +00:00
# include "scene/main/viewport.h"
2014-02-10 01:10:30 +00:00
2017-09-17 04:33:44 +00:00
# ifdef TOOLS_ENABLED
# include "editor/editor_scale.h"
# endif
2014-04-27 10:34:37 +00:00
# define TAB_PIXELS
2014-02-10 01:10:30 +00:00
2017-12-23 08:59:54 +00:00
inline bool _is_symbol ( CharType c ) {
2016-03-08 23:00:52 +00:00
2017-12-23 08:59:54 +00:00
return is_symbol ( c ) ;
2014-02-10 01:10:30 +00:00
}
2017-12-23 08:59:54 +00:00
static bool _is_text_char ( CharType c ) {
2016-03-08 23:00:52 +00:00
2019-03-09 18:45:06 +00:00
return ! is_symbol ( c ) ;
2014-02-10 01:10:30 +00:00
}
2017-08-01 17:40:43 +00:00
static bool _is_whitespace ( CharType c ) {
return c = = ' \t ' | | c = = ' ' ;
}
2016-03-21 15:45:38 +00:00
static bool _is_char ( CharType c ) {
2017-03-05 15:44:50 +00:00
return ( c > = ' a ' & & c < = ' z ' ) | | ( c > = ' A ' & & c < = ' Z ' ) | | c = = ' _ ' ;
2016-03-21 15:45:38 +00:00
}
static bool _is_number ( CharType c ) {
return ( c > = ' 0 ' & & c < = ' 9 ' ) ;
}
2016-05-15 00:32:43 +00:00
static bool _is_hex_symbol ( CharType c ) {
return ( ( c > = ' a ' & & c < = ' f ' ) | | ( c > = ' A ' & & c < = ' F ' ) ) ;
}
2014-04-27 10:34:37 +00:00
static bool _is_pair_right_symbol ( CharType c ) {
2017-03-05 15:44:50 +00:00
return c = = ' " ' | |
c = = ' \' ' | |
c = = ' ) ' | |
c = = ' ] ' | |
c = = ' } ' ;
2014-04-27 10:34:37 +00:00
}
static bool _is_pair_left_symbol ( CharType c ) {
2017-03-05 15:44:50 +00:00
return c = = ' " ' | |
c = = ' \' ' | |
c = = ' ( ' | |
c = = ' [ ' | |
c = = ' { ' ;
2014-04-27 10:34:37 +00:00
}
static bool _is_pair_symbol ( CharType c ) {
2015-01-02 18:08:40 +00:00
return _is_pair_left_symbol ( c ) | | _is_pair_right_symbol ( c ) ;
2014-04-27 10:34:37 +00:00
}
static CharType _get_right_pair_symbol ( CharType c ) {
2017-03-05 15:44:50 +00:00
if ( c = = ' " ' )
2015-01-02 18:08:40 +00:00
return ' " ' ;
2017-03-05 15:44:50 +00:00
if ( c = = ' \' ' )
2015-01-02 18:08:40 +00:00
return ' \' ' ;
2017-03-05 15:44:50 +00:00
if ( c = = ' ( ' )
2015-01-02 18:08:40 +00:00
return ' ) ' ;
2017-03-05 15:44:50 +00:00
if ( c = = ' [ ' )
2015-01-02 18:08:40 +00:00
return ' ] ' ;
2017-03-05 15:44:50 +00:00
if ( c = = ' { ' )
2015-01-02 18:08:40 +00:00
return ' } ' ;
return 0 ;
2014-04-27 10:34:37 +00:00
}
2019-05-08 21:56:00 +00:00
static int _find_first_non_whitespace_column_of_line ( const String & line ) {
int left = 0 ;
while ( left < line . length ( ) & & _is_whitespace ( line [ left ] ) )
left + + ;
return left ;
}
2017-03-05 15:44:50 +00:00
void TextEdit : : Text : : set_font ( const Ref < Font > & p_font ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
font = p_font ;
2014-02-10 01:10:30 +00:00
}
2017-04-17 13:24:30 +00:00
void TextEdit : : Text : : set_indent_size ( int p_indent_size ) {
2016-03-08 23:00:52 +00:00
2017-04-17 13:24:30 +00:00
indent_size = p_indent_size ;
2014-02-10 01:10:30 +00:00
}
void TextEdit : : Text : : _update_line_cache ( int p_line ) const {
2016-03-08 23:00:52 +00:00
2015-09-09 16:44:31 +00:00
int w = 0 ;
2015-01-02 18:08:40 +00:00
int len = text [ p_line ] . data . length ( ) ;
const CharType * str = text [ p_line ] . data . c_str ( ) ;
2016-03-08 23:00:52 +00:00
2019-08-18 15:56:24 +00:00
// Update width.
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
for ( int i = 0 ; i < len ; i + + ) {
2018-01-26 01:41:17 +00:00
w + = get_char_width ( str [ i ] , str [ i + 1 ] , w ) ;
2015-01-02 18:08:40 +00:00
}
2016-03-08 23:00:52 +00:00
2018-07-25 01:11:03 +00:00
text . write [ p_line ] . width_cache = w ;
2016-03-08 23:00:52 +00:00
2018-07-25 01:11:03 +00:00
text . write [ p_line ] . wrap_amount_cache = - 1 ;
2018-01-26 01:41:17 +00:00
2019-08-18 15:56:24 +00:00
// Update regions.
2016-03-08 23:00:52 +00:00
2018-07-25 01:11:03 +00:00
text . write [ p_line ] . region_info . clear ( ) ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
for ( int i = 0 ; i < len ; i + + ) {
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
if ( ! _is_symbol ( str [ i ] ) )
continue ;
2017-03-05 15:44:50 +00:00
if ( str [ i ] = = ' \\ ' ) {
2019-08-18 15:56:24 +00:00
i + + ; // Skip quoted anything.
2015-01-02 18:08:40 +00:00
continue ;
}
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
int left = len - i ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
for ( int j = 0 ; j < color_regions - > size ( ) ; j + + ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
const ColorRegion & cr = color_regions - > operator [ ] ( j ) ;
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
/* BEGIN */
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
int lr = cr . begin_key . length ( ) ;
if ( lr = = 0 | | lr > left )
2015-01-02 18:08:40 +00:00
continue ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
const CharType * kc = cr . begin_key . c_str ( ) ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
bool match = true ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
for ( int k = 0 ; k < lr ; k + + ) {
if ( kc [ k ] ! = str [ i + k ] ) {
match = false ;
2015-01-02 18:08:40 +00:00
break ;
}
}
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
if ( match ) {
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
ColorRegionInfo cri ;
2017-03-05 15:44:50 +00:00
cri . end = false ;
cri . region = j ;
2018-07-25 01:11:03 +00:00
text . write [ p_line ] . region_info [ i ] = cri ;
2017-03-05 15:44:50 +00:00
i + = lr - 1 ;
2018-04-02 11:41:44 +00:00
2015-01-02 18:08:40 +00:00
break ;
}
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
/* END */
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
lr = cr . end_key . length ( ) ;
if ( lr = = 0 | | lr > left )
2015-01-02 18:08:40 +00:00
continue ;
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
kc = cr . end_key . c_str ( ) ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
match = true ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
for ( int k = 0 ; k < lr ; k + + ) {
if ( kc [ k ] ! = str [ i + k ] ) {
match = false ;
2015-01-02 18:08:40 +00:00
break ;
}
}
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
if ( match ) {
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
ColorRegionInfo cri ;
2017-03-05 15:44:50 +00:00
cri . end = true ;
cri . region = j ;
2018-07-25 01:11:03 +00:00
text . write [ p_line ] . region_info [ i ] = cri ;
2017-03-05 15:44:50 +00:00
i + = lr - 1 ;
2018-04-02 11:41:44 +00:00
2015-01-02 18:08:40 +00:00
break ;
}
}
}
2014-02-10 01:10:30 +00:00
}
2018-01-12 05:25:04 +00:00
const Map < int , TextEdit : : Text : : ColorRegionInfo > & TextEdit : : Text : : get_color_region_info ( int p_line ) const {
2016-03-08 23:00:52 +00:00
2017-08-25 15:14:33 +00:00
static Map < int , ColorRegionInfo > cri ;
ERR_FAIL_INDEX_V ( p_line , text . size ( ) , cri ) ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
if ( text [ p_line ] . width_cache = = - 1 ) {
2015-01-02 18:08:40 +00:00
_update_line_cache ( p_line ) ;
}
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
return text [ p_line ] . region_info ;
2014-02-10 01:10:30 +00:00
}
int TextEdit : : Text : : get_line_width ( int p_line ) const {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
ERR_FAIL_INDEX_V ( p_line , text . size ( ) , - 1 ) ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
if ( text [ p_line ] . width_cache = = - 1 ) {
2015-01-02 18:08:40 +00:00
_update_line_cache ( p_line ) ;
}
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
return text [ p_line ] . width_cache ;
2014-02-10 01:10:30 +00:00
}
2018-01-26 01:41:17 +00:00
void TextEdit : : Text : : set_line_wrap_amount ( int p_line , int p_wrap_amount ) const {
ERR_FAIL_INDEX ( p_line , text . size ( ) ) ;
2018-07-25 01:11:03 +00:00
text . write [ p_line ] . wrap_amount_cache = p_wrap_amount ;
2018-01-26 01:41:17 +00:00
}
int TextEdit : : Text : : get_line_wrap_amount ( int p_line ) const {
ERR_FAIL_INDEX_V ( p_line , text . size ( ) , - 1 ) ;
return text [ p_line ] . wrap_amount_cache ;
}
void TextEdit : : Text : : clear_width_cache ( ) {
2016-03-08 23:00:52 +00:00
2018-01-26 01:41:17 +00:00
for ( int i = 0 ; i < text . size ( ) ; i + + ) {
2018-07-25 01:11:03 +00:00
text . write [ i ] . width_cache = - 1 ;
2018-01-26 01:41:17 +00:00
}
}
void TextEdit : : Text : : clear_wrap_cache ( ) {
for ( int i = 0 ; i < text . size ( ) ; i + + ) {
2018-07-25 01:11:03 +00:00
text . write [ i ] . wrap_amount_cache = - 1 ;
2018-01-26 01:41:17 +00:00
}
2014-02-10 01:10:30 +00:00
}
void TextEdit : : Text : : clear ( ) {
2016-03-08 23:00:52 +00:00
2017-01-14 17:03:38 +00:00
text . clear ( ) ;
2017-03-05 15:44:50 +00:00
insert ( 0 , " " ) ;
2014-02-10 01:10:30 +00:00
}
2017-12-06 23:31:09 +00:00
int TextEdit : : Text : : get_max_width ( bool p_exclude_hidden ) const {
2019-08-18 15:56:24 +00:00
// Quite some work, but should be fast enough.
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
int max = 0 ;
2017-12-06 23:31:09 +00:00
for ( int i = 0 ; i < text . size ( ) ; i + + ) {
if ( ! p_exclude_hidden | | ! is_hidden ( i ) )
max = MAX ( max , get_line_width ( i ) ) ;
}
2015-01-02 18:08:40 +00:00
return max ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
void TextEdit : : Text : : set ( int p_line , const String & p_text ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
ERR_FAIL_INDEX ( p_line , text . size ( ) ) ;
2016-03-08 23:00:52 +00:00
2018-07-25 01:11:03 +00:00
text . write [ p_line ] . width_cache = - 1 ;
text . write [ p_line ] . wrap_amount_cache = - 1 ;
text . write [ p_line ] . data = p_text ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
void TextEdit : : Text : : insert ( int p_at , const String & p_text ) {
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
Line line ;
2017-03-05 15:44:50 +00:00
line . marked = false ;
2018-06-05 16:50:21 +00:00
line . safe = false ;
2017-03-05 15:44:50 +00:00
line . breakpoint = false ;
2019-04-19 23:51:25 +00:00
line . bookmark = false ;
2017-11-12 23:12:17 +00:00
line . hidden = false ;
2017-03-05 15:44:50 +00:00
line . width_cache = - 1 ;
2018-01-26 01:41:17 +00:00
line . wrap_amount_cache = - 1 ;
2017-03-05 15:44:50 +00:00
line . data = p_text ;
text . insert ( p_at , line ) ;
2014-02-10 01:10:30 +00:00
}
void TextEdit : : Text : : remove ( int p_at ) {
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
text . remove ( p_at ) ;
2014-02-10 01:10:30 +00:00
}
2018-05-15 19:32:09 +00:00
int TextEdit : : Text : : get_char_width ( CharType c , CharType next_c , int px ) const {
2018-01-26 01:41:17 +00:00
int tab_w = font - > get_char_size ( ' ' ) . width * indent_size ;
int w = 0 ;
if ( c = = ' \t ' ) {
int left = px % tab_w ;
if ( left = = 0 )
w = tab_w ;
else
2019-08-18 15:56:24 +00:00
w = tab_w - px % tab_w ; // Is right.
2018-01-26 01:41:17 +00:00
} else {
w = font - > get_char_size ( c , next_c ) . width ;
}
return w ;
}
2014-02-10 01:10:30 +00:00
void TextEdit : : _update_scrollbars ( ) {
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
Size2 size = get_size ( ) ;
Size2 hmin = h_scroll - > get_combined_minimum_size ( ) ;
Size2 vmin = v_scroll - > get_combined_minimum_size ( ) ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
v_scroll - > set_begin ( Point2 ( size . width - vmin . width , cache . style_normal - > get_margin ( MARGIN_TOP ) ) ) ;
v_scroll - > set_end ( Point2 ( size . width , size . height - cache . style_normal - > get_margin ( MARGIN_TOP ) - cache . style_normal - > get_margin ( MARGIN_BOTTOM ) ) ) ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
h_scroll - > set_begin ( Point2 ( 0 , size . height - hmin . height ) ) ;
h_scroll - > set_end ( Point2 ( size . width - vmin . width , size . height ) ) ;
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
int visible_rows = get_visible_rows ( ) ;
2018-01-26 01:41:17 +00:00
int total_rows = get_total_visible_rows ( ) ;
2016-03-07 02:32:51 +00:00
if ( scroll_past_end_of_file_enabled ) {
2017-11-16 04:00:27 +00:00
total_rows + = visible_rows - 1 ;
2016-03-07 02:32:51 +00:00
}
2015-01-02 18:08:40 +00:00
int visible_width = size . width - cache . style_normal - > get_minimum_size ( ) . width ;
2017-12-06 23:31:09 +00:00
int total_width = text . get_max_width ( true ) + vmin . x ;
2016-03-08 23:00:52 +00:00
2015-09-09 16:44:31 +00:00
if ( line_numbers )
total_width + = cache . line_number_w ;
2016-03-08 23:00:52 +00:00
2019-04-19 23:51:25 +00:00
if ( draw_breakpoint_gutter | | draw_bookmark_gutter ) {
2016-05-26 13:17:14 +00:00
total_width + = cache . breakpoint_gutter_width ;
}
2019-04-20 11:51:25 +00:00
if ( draw_info_gutter ) {
total_width + = cache . info_gutter_width ;
}
2017-11-12 23:12:17 +00:00
if ( draw_fold_gutter ) {
total_width + = cache . fold_gutter_width ;
}
2019-08-11 18:31:19 +00:00
if ( draw_minimap ) {
total_width + = cache . minimap_width ;
}
2017-03-05 15:44:50 +00:00
bool use_hscroll = true ;
bool use_vscroll = true ;
2016-03-08 23:00:52 +00:00
2019-08-18 15:56:24 +00:00
// Thanks yessopie for this clever bit of logic.
2015-01-02 18:08:40 +00:00
if ( total_rows < = visible_rows & & total_width < = visible_width ) {
2019-08-18 15:56:24 +00:00
2017-03-05 15:44:50 +00:00
use_hscroll = false ;
use_vscroll = false ;
2015-01-02 18:08:40 +00:00
} else {
2016-03-08 23:00:52 +00:00
2018-10-04 22:38:08 +00:00
if ( total_rows > visible_rows & & total_width < = visible_width ) {
2017-03-05 15:44:50 +00:00
use_hscroll = false ;
2015-01-02 18:08:40 +00:00
}
2016-03-08 23:00:52 +00:00
2018-10-04 22:38:08 +00:00
if ( total_rows < = visible_rows & & total_width > visible_width ) {
2017-03-05 15:44:50 +00:00
use_vscroll = false ;
2015-01-02 18:08:40 +00:00
}
}
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
updating_scrolls = true ;
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
if ( use_vscroll ) {
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
v_scroll - > show ( ) ;
2018-01-26 01:41:17 +00:00
v_scroll - > set_max ( total_rows + get_visible_rows_offset ( ) ) ;
v_scroll - > set_page ( visible_rows + get_visible_rows_offset ( ) ) ;
2017-08-19 14:23:45 +00:00
if ( smooth_scroll_enabled ) {
v_scroll - > set_step ( 0.25 ) ;
} else {
v_scroll - > set_step ( 1 ) ;
}
2018-01-26 01:41:17 +00:00
set_v_scroll ( get_v_scroll ( ) ) ;
2017-11-21 14:34:04 +00:00
2017-03-05 15:44:50 +00:00
} else {
2017-12-06 23:31:09 +00:00
2015-01-02 18:08:40 +00:00
cursor . line_ofs = 0 ;
2018-01-26 01:41:17 +00:00
cursor . wrap_ofs = 0 ;
2017-11-16 04:00:27 +00:00
v_scroll - > set_value ( 0 ) ;
2019-05-26 18:51:08 +00:00
v_scroll - > set_max ( 0 ) ;
2015-01-02 18:08:40 +00:00
v_scroll - > hide ( ) ;
}
2016-03-08 23:00:52 +00:00
2018-01-26 01:41:17 +00:00
if ( use_hscroll & & ! is_wrap_enabled ( ) ) {
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
h_scroll - > show ( ) ;
h_scroll - > set_max ( total_width ) ;
h_scroll - > set_page ( visible_width ) ;
2017-12-06 23:31:09 +00:00
if ( cursor . x_ofs > ( total_width - visible_width ) )
cursor . x_ofs = ( total_width - visible_width ) ;
2017-02-21 22:45:31 +00:00
if ( fabs ( h_scroll - > get_value ( ) - ( double ) cursor . x_ofs ) > = 1 ) {
h_scroll - > set_value ( cursor . x_ofs ) ;
}
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
} else {
2016-03-08 23:00:52 +00:00
2017-12-06 23:31:09 +00:00
cursor . x_ofs = 0 ;
h_scroll - > set_value ( 0 ) ;
2019-05-26 18:51:08 +00:00
h_scroll - > set_max ( 0 ) ;
2015-01-02 18:08:40 +00:00
h_scroll - > hide ( ) ;
}
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
updating_scrolls = false ;
2014-02-10 01:10:30 +00:00
}
2015-12-09 18:56:41 +00:00
void TextEdit : : _click_selection_held ( ) {
2018-06-14 07:10:30 +00:00
// Warning: is_mouse_button_pressed(BUTTON_LEFT) returns false for double+ clicks, so this doesn't work for MODE_WORD
// and MODE_LINE. However, moving the mouse triggers _gui_input, which calls these functions too, so that's not a huge problem.
// I'm unsure if there's an actual fix that doesn't have a ton of side effects.
2017-03-05 15:44:50 +00:00
if ( Input : : get_singleton ( ) - > is_mouse_button_pressed ( BUTTON_LEFT ) & & selection . selecting_mode ! = Selection : : MODE_NONE ) {
2017-11-05 15:54:00 +00:00
switch ( selection . selecting_mode ) {
case Selection : : MODE_POINTER : {
_update_selection_mode_pointer ( ) ;
} break ;
case Selection : : MODE_WORD : {
_update_selection_mode_word ( ) ;
} break ;
case Selection : : MODE_LINE : {
_update_selection_mode_line ( ) ;
} break ;
default : {
break ;
}
}
} else {
click_select_held - > stop ( ) ;
}
}
2015-12-09 18:56:41 +00:00
2017-11-05 15:54:00 +00:00
void TextEdit : : _update_selection_mode_pointer ( ) {
2019-08-11 18:31:19 +00:00
dragging_selection = true ;
2018-04-19 18:05:18 +00:00
Point2 mp = get_local_mouse_position ( ) ;
2015-12-09 18:56:41 +00:00
2017-11-05 15:54:00 +00:00
int row , col ;
_get_mouse_pos ( Point2i ( mp . x , mp . y ) , row , col ) ;
2015-12-09 18:56:41 +00:00
2017-11-05 15:54:00 +00:00
select ( selection . selecting_line , selection . selecting_column , row , col ) ;
2015-12-09 18:56:41 +00:00
2018-01-26 01:41:17 +00:00
cursor_set_line ( row , false ) ;
2018-06-14 07:10:30 +00:00
cursor_set_column ( col ) ;
2017-11-05 15:54:00 +00:00
update ( ) ;
click_select_held - > start ( ) ;
}
void TextEdit : : _update_selection_mode_word ( ) {
2019-08-11 18:31:19 +00:00
dragging_selection = true ;
2018-04-19 18:05:18 +00:00
Point2 mp = get_local_mouse_position ( ) ;
2017-11-05 15:54:00 +00:00
int row , col ;
_get_mouse_pos ( Point2i ( mp . x , mp . y ) , row , col ) ;
2015-12-09 18:56:41 +00:00
2017-11-05 15:54:00 +00:00
String line = text [ row ] ;
int beg = CLAMP ( col , 0 , line . length ( ) ) ;
2019-08-18 15:56:24 +00:00
// If its the first selection and on whitespace make sure we grab the word instead.
2017-11-05 15:54:00 +00:00
if ( ! selection . active ) {
while ( beg > 0 & & line [ beg ] < = 32 ) {
beg - - ;
}
}
int end = beg ;
bool symbol = beg < line . length ( ) & & _is_symbol ( line [ beg ] ) ;
2019-08-18 15:56:24 +00:00
// Get the word end and begin points.
2017-11-05 15:54:00 +00:00
while ( beg > 0 & & line [ beg - 1 ] > 32 & & ( symbol = = _is_symbol ( line [ beg - 1 ] ) ) ) {
beg - - ;
}
while ( end < line . length ( ) & & line [ end + 1 ] > 32 & & ( symbol = = _is_symbol ( line [ end + 1 ] ) ) ) {
end + + ;
}
if ( end < line . length ( ) ) {
end + = 1 ;
}
2015-12-09 18:56:41 +00:00
2019-08-18 15:56:24 +00:00
// Initial selection.
2017-11-05 15:54:00 +00:00
if ( ! selection . active ) {
select ( row , beg , row , end ) ;
selection . selecting_column = beg ;
selection . selected_word_beg = beg ;
selection . selected_word_end = end ;
selection . selected_word_origin = beg ;
2018-06-14 07:10:30 +00:00
cursor_set_line ( selection . to_line , false ) ;
2017-11-05 15:54:00 +00:00
cursor_set_column ( selection . to_column ) ;
2015-12-09 18:56:41 +00:00
} else {
2017-11-05 15:54:00 +00:00
if ( ( col < = selection . selected_word_origin & & row = = selection . selecting_line ) | | row < selection . selecting_line ) {
selection . selecting_column = selection . selected_word_end ;
select ( row , beg , selection . selecting_line , selection . selected_word_end ) ;
2018-06-14 07:10:30 +00:00
cursor_set_line ( selection . from_line , false ) ;
2017-11-05 15:54:00 +00:00
cursor_set_column ( selection . from_column ) ;
} else {
selection . selecting_column = selection . selected_word_beg ;
select ( selection . selecting_line , selection . selected_word_beg , row , end ) ;
2018-06-14 07:10:30 +00:00
cursor_set_line ( selection . to_line , false ) ;
2017-11-05 15:54:00 +00:00
cursor_set_column ( selection . to_column ) ;
}
}
2015-12-09 18:56:41 +00:00
2017-11-05 15:54:00 +00:00
update ( ) ;
2018-06-14 07:10:30 +00:00
2017-11-05 15:54:00 +00:00
click_select_held - > start ( ) ;
}
void TextEdit : : _update_selection_mode_line ( ) {
2019-08-11 18:31:19 +00:00
dragging_selection = true ;
2018-04-19 18:05:18 +00:00
Point2 mp = get_local_mouse_position ( ) ;
2017-11-05 15:54:00 +00:00
int row , col ;
_get_mouse_pos ( Point2i ( mp . x , mp . y ) , row , col ) ;
col = 0 ;
if ( row < selection . selecting_line ) {
2019-08-18 15:56:24 +00:00
// Cursor is above us.
2018-01-26 01:41:17 +00:00
cursor_set_line ( row - 1 , false ) ;
2017-11-05 15:54:00 +00:00
selection . selecting_column = text [ selection . selecting_line ] . length ( ) ;
} else {
2019-08-18 15:56:24 +00:00
// Cursor is below us.
2018-01-26 01:41:17 +00:00
cursor_set_line ( row + 1 , false ) ;
2017-11-05 15:54:00 +00:00
selection . selecting_column = 0 ;
col = text [ row ] . length ( ) ;
2015-12-09 18:56:41 +00:00
}
2018-06-14 07:10:30 +00:00
cursor_set_column ( 0 ) ;
2017-11-05 15:54:00 +00:00
select ( selection . selecting_line , selection . selecting_column , row , col ) ;
update ( ) ;
click_select_held - > start ( ) ;
2015-12-09 18:56:41 +00:00
}
2019-08-25 13:38:14 +00:00
void TextEdit : : _update_minimap_click ( ) {
2019-08-11 18:31:19 +00:00
Point2 mp = get_local_mouse_position ( ) ;
int xmargin_end = get_size ( ) . width - cache . style_normal - > get_margin ( MARGIN_RIGHT ) ;
if ( ! dragging_minimap & & ( mp . x < xmargin_end - minimap_width | | mp . y > xmargin_end ) ) {
minimap_clicked = false ;
return ;
}
minimap_clicked = true ;
dragging_minimap = true ;
int row ;
_get_minimap_mouse_row ( Point2i ( mp . x , mp . y ) , row ) ;
2019-08-25 13:38:14 +00:00
if ( row > = get_first_visible_line ( ) & & ( row < get_last_visible_line ( ) | | row > = ( text . size ( ) - 1 ) ) ) {
minimap_scroll_ratio = v_scroll - > get_as_ratio ( ) ;
minimap_scroll_click_pos = mp . y ;
can_drag_minimap = true ;
return ;
}
2019-08-11 18:31:19 +00:00
int wi ;
int first_line = row - num_lines_from_rows ( row , 0 , - get_visible_rows ( ) / 2 , wi ) + 1 ;
double delta = get_scroll_pos_for_line ( first_line , wi ) - get_v_scroll ( ) ;
if ( delta < 0 ) {
_scroll_up ( - delta ) ;
} else {
_scroll_down ( delta ) ;
}
}
2019-08-25 13:38:14 +00:00
void TextEdit : : _update_minimap_drag ( ) {
if ( ! can_drag_minimap ) {
return ;
}
Point2 mp = get_local_mouse_position ( ) ;
2019-08-25 13:49:13 +00:00
double diff = ( mp . y - minimap_scroll_click_pos ) / _get_control_height ( ) ;
2019-08-25 13:38:14 +00:00
v_scroll - > set_as_ratio ( minimap_scroll_ratio + diff ) ;
}
2014-02-10 01:10:30 +00:00
void TextEdit : : _notification ( int p_what ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
switch ( p_what ) {
2014-12-17 01:31:57 +00:00
case NOTIFICATION_ENTER_TREE : {
2016-03-08 23:00:52 +00:00
2014-12-17 01:31:57 +00:00
_update_caches ( ) ;
if ( cursor_changed_dirty )
2017-03-05 15:44:50 +00:00
MessageQueue : : get_singleton ( ) - > push_call ( this , " _cursor_changed_emit " ) ;
2014-12-17 01:31:57 +00:00
if ( text_changed_dirty )
2017-03-05 15:44:50 +00:00
MessageQueue : : get_singleton ( ) - > push_call ( this , " _text_changed_emit " ) ;
2019-01-05 16:58:54 +00:00
_update_wrap_at ( ) ;
2014-12-17 01:31:57 +00:00
} break ;
case NOTIFICATION_RESIZED : {
2016-03-08 23:00:52 +00:00
2018-01-26 01:41:17 +00:00
_update_scrollbars ( ) ;
2019-08-26 16:47:33 +00:00
_update_wrap_at ( ) ;
2014-12-17 01:31:57 +00:00
} break ;
case NOTIFICATION_THEME_CHANGED : {
2016-03-08 23:00:52 +00:00
2014-12-17 01:31:57 +00:00
_update_caches ( ) ;
2019-01-05 16:58:54 +00:00
_update_wrap_at ( ) ;
2019-08-11 18:29:09 +00:00
syntax_highlighting_cache . clear ( ) ;
2016-05-29 14:37:26 +00:00
} break ;
2016-06-19 15:11:16 +00:00
case MainLoop : : NOTIFICATION_WM_FOCUS_IN : {
window_has_focus = true ;
draw_caret = true ;
update ( ) ;
} break ;
case MainLoop : : NOTIFICATION_WM_FOCUS_OUT : {
window_has_focus = false ;
draw_caret = false ;
update ( ) ;
} break ;
2018-04-11 07:28:14 +00:00
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS : {
2018-01-26 01:41:17 +00:00
if ( scrolling & & get_v_scroll ( ) ! = target_v_scroll ) {
double target_y = target_v_scroll - get_v_scroll ( ) ;
2017-08-19 14:23:45 +00:00
double dist = sqrt ( target_y * target_y ) ;
2019-08-11 18:31:19 +00:00
// To ensure minimap is responsive overide the speed setting.
double vel = ( ( target_y / dist ) * ( ( minimap_clicked ) ? 3000 : v_scroll_speed ) ) * get_physics_process_delta_time ( ) ;
2017-08-19 14:23:45 +00:00
2017-09-02 12:17:13 +00:00
if ( Math : : abs ( vel ) > = dist ) {
2018-01-26 01:41:17 +00:00
set_v_scroll ( target_v_scroll ) ;
2017-08-19 14:23:45 +00:00
scrolling = false ;
2019-08-11 18:31:19 +00:00
minimap_clicked = false ;
2018-04-11 07:28:14 +00:00
set_physics_process_internal ( false ) ;
2017-08-19 14:23:45 +00:00
} else {
2018-01-26 01:41:17 +00:00
set_v_scroll ( get_v_scroll ( ) + vel ) ;
2017-08-19 14:23:45 +00:00
}
} else {
scrolling = false ;
2019-08-11 18:31:19 +00:00
minimap_clicked = false ;
2018-04-11 07:28:14 +00:00
set_physics_process_internal ( false ) ;
2017-08-19 14:23:45 +00:00
}
} break ;
2014-12-17 01:31:57 +00:00
case NOTIFICATION_DRAW : {
2019-04-12 02:21:48 +00:00
if ( first_draw ) {
2019-08-18 15:56:24 +00:00
// Size may not be the final one, so attempts to ensure cursor was visible may have failed.
2019-04-12 02:21:48 +00:00
adjust_viewport_to_cursor ( ) ;
first_draw = false ;
}
2018-08-20 11:36:34 +00:00
Size2 size = get_size ( ) ;
2016-06-19 15:11:16 +00:00
if ( ( ! has_focus ( ) & & ! menu - > has_focus ( ) ) | | ! window_has_focus ) {
draw_caret = false ;
}
2019-04-19 23:51:25 +00:00
if ( draw_breakpoint_gutter | | draw_bookmark_gutter ) {
2016-06-07 15:59:16 +00:00
breakpoint_gutter_width = ( get_row_height ( ) * 55 ) / 100 ;
2016-05-26 13:17:14 +00:00
cache . breakpoint_gutter_width = breakpoint_gutter_width ;
} else {
cache . breakpoint_gutter_width = 0 ;
}
2019-04-20 11:51:25 +00:00
if ( draw_info_gutter ) {
info_gutter_width = ( get_row_height ( ) ) ;
cache . info_gutter_width = info_gutter_width ;
} else {
cache . info_gutter_width = 0 ;
}
2017-11-12 23:12:17 +00:00
if ( draw_fold_gutter ) {
fold_gutter_width = ( get_row_height ( ) * 55 ) / 100 ;
cache . fold_gutter_width = fold_gutter_width ;
} else {
cache . fold_gutter_width = 0 ;
}
2019-08-11 18:31:19 +00:00
cache . minimap_width = 0 ;
if ( draw_minimap ) {
cache . minimap_width = minimap_width ;
}
2017-03-05 15:44:50 +00:00
int line_number_char_count = 0 ;
2016-03-08 23:00:52 +00:00
2014-12-17 01:31:57 +00:00
{
2017-08-18 20:11:45 +00:00
int lc = text . size ( ) ;
2017-03-05 15:44:50 +00:00
cache . line_number_w = 0 ;
while ( lc ) {
cache . line_number_w + = 1 ;
lc / = 10 ;
2014-12-17 01:31:57 +00:00
} ;
2016-03-08 23:00:52 +00:00
2014-12-17 01:31:57 +00:00
if ( line_numbers ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
line_number_char_count = cache . line_number_w ;
cache . line_number_w = ( cache . line_number_w + 1 ) * cache . font - > get_char_size ( ' 0 ' ) . width ;
2014-12-17 01:31:57 +00:00
} else {
2017-03-05 15:44:50 +00:00
cache . line_number_w = 0 ;
2014-12-17 01:31:57 +00:00
}
}
_update_scrollbars ( ) ;
2016-03-08 23:00:52 +00:00
2014-12-17 01:31:57 +00:00
RID ci = get_canvas_item ( ) ;
2017-08-19 14:23:45 +00:00
VisualServer : : get_singleton ( ) - > canvas_item_set_clip ( get_canvas_item ( ) , true ) ;
2019-04-20 11:51:25 +00:00
int xmargin_beg = cache . style_normal - > get_margin ( MARGIN_LEFT ) + cache . line_number_w + cache . breakpoint_gutter_width + cache . fold_gutter_width + cache . info_gutter_width ;
2019-08-11 18:31:19 +00:00
int xmargin_end = size . width - cache . style_normal - > get_margin ( MARGIN_RIGHT ) - cache . minimap_width ;
2019-08-18 15:56:24 +00:00
// Let's do it easy for now.
2018-08-20 11:36:34 +00:00
cache . style_normal - > draw ( ci , Rect2 ( Point2 ( ) , size ) ) ;
2017-11-30 03:53:15 +00:00
if ( readonly ) {
2018-08-20 11:36:34 +00:00
cache . style_readonly - > draw ( ci , Rect2 ( Point2 ( ) , size ) ) ;
2017-11-30 03:53:15 +00:00
draw_caret = false ;
}
2014-12-17 01:31:57 +00:00
if ( has_focus ( ) )
2018-08-20 11:36:34 +00:00
cache . style_focus - > draw ( ci , Rect2 ( Point2 ( ) , size ) ) ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
int ascent = cache . font - > get_ascent ( ) ;
2016-03-08 23:00:52 +00:00
2017-08-20 13:07:54 +00:00
int visible_rows = get_visible_rows ( ) + 1 ;
2016-03-08 23:00:52 +00:00
2019-06-24 03:43:52 +00:00
Color color = readonly ? cache . font_color_readonly : cache . font_color ;
2017-11-30 03:53:15 +00:00
2014-12-17 01:31:57 +00:00
if ( syntax_coloring ) {
2017-03-05 15:44:50 +00:00
if ( cache . background_color . a > 0.01 ) {
2017-09-29 14:59:38 +00:00
VisualServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( Point2i ( ) , get_size ( ) ) , cache . background_color ) ;
2014-12-17 01:31:57 +00:00
}
}
2016-03-08 23:00:52 +00:00
2019-06-30 21:21:11 +00:00
if ( line_length_guideline ) {
2019-07-10 09:29:43 +00:00
int x = xmargin_beg + ( int ) cache . font - > get_char_size ( ' 0 ' ) . width * line_length_guideline_col - cursor . x_ofs ;
2019-06-30 21:21:11 +00:00
if ( x > xmargin_beg & & x < xmargin_end ) {
VisualServer : : get_singleton ( ) - > canvas_item_add_line ( ci , Point2 ( x , 0 ) , Point2 ( x , size . height ) , cache . line_length_guideline_color ) ;
}
}
2017-03-05 15:44:50 +00:00
int brace_open_match_line = - 1 ;
int brace_open_match_column = - 1 ;
bool brace_open_matching = false ;
bool brace_open_mismatch = false ;
int brace_close_match_line = - 1 ;
int brace_close_match_column = - 1 ;
bool brace_close_matching = false ;
bool brace_close_mismatch = false ;
2016-03-08 23:00:52 +00:00
2019-03-05 15:23:25 +00:00
if ( brace_matching_enabled & & cursor . line > = 0 & & cursor . line < text . size ( ) & & cursor . column > = 0 ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
if ( cursor . column < text [ cursor . line ] . length ( ) ) {
2019-08-18 15:56:24 +00:00
// Check for open.
2014-12-17 04:53:34 +00:00
CharType c = text [ cursor . line ] [ cursor . column ] ;
2017-03-05 15:44:50 +00:00
CharType closec = 0 ;
if ( c = = ' [ ' ) {
closec = ' ] ' ;
} else if ( c = = ' { ' ) {
closec = ' } ' ;
} else if ( c = = ' ( ' ) {
closec = ' ) ' ;
2014-12-17 04:53:34 +00:00
}
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
if ( closec ! = 0 ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
int stack = 1 ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
for ( int i = cursor . line ; i < text . size ( ) ; i + + ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
int from = i = = cursor . line ? cursor . column + 1 : 0 ;
for ( int j = from ; j < text [ i ] . length ( ) ; j + + ) {
2016-03-08 23:00:52 +00:00
2014-12-17 04:53:34 +00:00
CharType cc = text [ i ] [ j ] ;
2019-08-18 15:56:24 +00:00
// Ignore any brackets inside a string.
2017-03-05 15:44:50 +00:00
if ( cc = = ' " ' | | cc = = ' \' ' ) {
2015-02-20 00:34:04 +00:00
CharType quotation = cc ;
do {
j + + ;
2017-03-05 15:44:50 +00:00
if ( ! ( j < text [ i ] . length ( ) ) ) {
2015-02-20 00:34:04 +00:00
break ;
}
2017-03-05 15:44:50 +00:00
cc = text [ i ] [ j ] ;
2019-08-18 15:56:24 +00:00
// Skip over escaped quotation marks inside strings.
2017-03-05 15:44:50 +00:00
if ( cc = = ' \\ ' ) {
2015-02-20 00:34:04 +00:00
bool escaped = true ;
2017-03-05 15:44:50 +00:00
while ( j + 1 < text [ i ] . length ( ) & & text [ i ] [ j + 1 ] = = ' \\ ' ) {
escaped = ! escaped ;
2015-02-20 00:34:04 +00:00
j + + ;
}
if ( escaped ) {
j + + ;
continue ;
}
}
2017-03-05 15:44:50 +00:00
} while ( cc ! = quotation ) ;
} else if ( cc = = c )
2014-12-17 04:53:34 +00:00
stack + + ;
2017-03-05 15:44:50 +00:00
else if ( cc = = closec )
2014-12-17 04:53:34 +00:00
stack - - ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
if ( stack = = 0 ) {
brace_open_match_line = i ;
brace_open_match_column = j ;
brace_open_matching = true ;
2016-03-08 23:00:52 +00:00
2014-12-17 04:53:34 +00:00
break ;
}
}
2017-03-05 15:44:50 +00:00
if ( brace_open_match_line ! = - 1 )
2014-12-17 04:53:34 +00:00
break ;
}
2016-03-08 23:00:52 +00:00
2014-12-17 04:53:34 +00:00
if ( ! brace_open_matching )
2017-03-05 15:44:50 +00:00
brace_open_mismatch = true ;
2014-12-17 04:53:34 +00:00
}
}
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
if ( cursor . column > 0 ) {
CharType c = text [ cursor . line ] [ cursor . column - 1 ] ;
CharType closec = 0 ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
if ( c = = ' ] ' ) {
closec = ' [ ' ;
} else if ( c = = ' } ' ) {
closec = ' { ' ;
} else if ( c = = ' ) ' ) {
closec = ' ( ' ;
2014-12-17 04:53:34 +00:00
}
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
if ( closec ! = 0 ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
int stack = 1 ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
for ( int i = cursor . line ; i > = 0 ; i - - ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
int from = i = = cursor . line ? cursor . column - 2 : text [ i ] . length ( ) - 1 ;
for ( int j = from ; j > = 0 ; j - - ) {
2016-03-08 23:00:52 +00:00
2014-12-17 04:53:34 +00:00
CharType cc = text [ i ] [ j ] ;
2019-08-18 15:56:24 +00:00
// Ignore any brackets inside a string.
2017-03-05 15:44:50 +00:00
if ( cc = = ' " ' | | cc = = ' \' ' ) {
2015-02-20 00:34:04 +00:00
CharType quotation = cc ;
do {
j - - ;
2017-03-05 15:44:50 +00:00
if ( ! ( j > = 0 ) ) {
2015-02-20 00:34:04 +00:00
break ;
}
2017-03-05 15:44:50 +00:00
cc = text [ i ] [ j ] ;
2019-08-18 15:56:24 +00:00
// Skip over escaped quotation marks inside strings.
2017-03-05 15:44:50 +00:00
if ( cc = = quotation ) {
2015-02-20 00:34:04 +00:00
bool escaped = false ;
2017-03-05 15:44:50 +00:00
while ( j - 1 > = 0 & & text [ i ] [ j - 1 ] = = ' \\ ' ) {
escaped = ! escaped ;
2015-02-20 00:34:04 +00:00
j - - ;
}
if ( escaped ) {
2017-03-05 15:44:50 +00:00
cc = ' \\ ' ;
2015-02-20 00:34:04 +00:00
continue ;
}
}
2017-03-05 15:44:50 +00:00
} while ( cc ! = quotation ) ;
} else if ( cc = = c )
2014-12-17 04:53:34 +00:00
stack + + ;
2017-03-05 15:44:50 +00:00
else if ( cc = = closec )
2014-12-17 04:53:34 +00:00
stack - - ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
if ( stack = = 0 ) {
brace_close_match_line = i ;
brace_close_match_column = j ;
brace_close_matching = true ;
2016-03-08 23:00:52 +00:00
2014-12-17 04:53:34 +00:00
break ;
}
}
2017-03-05 15:44:50 +00:00
if ( brace_close_match_line ! = - 1 )
2014-12-17 04:53:34 +00:00
break ;
}
2016-03-08 23:00:52 +00:00
2014-12-17 04:53:34 +00:00
if ( ! brace_close_matching )
2017-03-05 15:44:50 +00:00
brace_close_mismatch = true ;
2014-12-17 04:53:34 +00:00
}
}
}
2016-03-08 23:00:52 +00:00
2014-12-17 01:31:57 +00:00
Point2 cursor_pos ;
2018-12-31 11:38:42 +00:00
int cursor_insert_offset_y = 0 ;
2016-03-08 23:00:52 +00:00
2019-08-18 15:56:24 +00:00
// Get the highlighted words.
2016-03-17 19:37:19 +00:00
String highlighted_text = get_selection_text ( ) ;
2016-03-16 21:20:42 +00:00
2019-08-18 15:56:24 +00:00
// Check if highlighted words contains only whitespaces (tabs or spaces).
2019-04-27 16:02:09 +00:00
bool only_whitespaces_highlighted = highlighted_text . strip_edges ( ) = = String ( ) ;
2016-10-10 11:43:09 +00:00
String line_num_padding = line_numbers_zero_padded ? " 0 " : " " ;
2018-01-26 01:41:17 +00:00
int cursor_wrap_index = get_cursor_wrap_index ( ) ;
2018-03-17 08:44:34 +00:00
FontDrawer drawer ( cache . font , Color ( 1 , 1 , 1 ) ) ;
2018-01-26 01:41:17 +00:00
2019-08-11 18:31:19 +00:00
int first_visible_line = get_first_visible_line ( ) - 1 ;
2018-01-26 01:41:17 +00:00
int draw_amount = visible_rows + ( smooth_scroll_enabled ? 1 : 0 ) ;
2019-08-11 18:31:19 +00:00
draw_amount + = times_line_wraps ( first_visible_line + 1 ) ;
// minimap
if ( draw_minimap ) {
int minimap_visible_lines = _get_minimap_visible_rows ( ) ;
int minimap_line_height = ( minimap_char_size . y + minimap_line_spacing ) ;
int minimap_tab_size = minimap_char_size . x * indent_size ;
// calculate viewport size and y offset
int viewport_height = ( draw_amount - 1 ) * minimap_line_height ;
2019-08-25 13:49:13 +00:00
int control_height = _get_control_height ( ) - viewport_height ;
2019-08-11 18:31:19 +00:00
int viewport_offset_y = round ( get_scroll_pos_for_line ( first_visible_line ) * control_height ) / ( ( v_scroll - > get_max ( ) < = minimap_visible_lines ) ? ( minimap_visible_lines - draw_amount ) : ( v_scroll - > get_max ( ) - draw_amount ) ) ;
// calculate the first line.
int num_lines_before = round ( ( viewport_offset_y ) / minimap_line_height ) ;
int wi ;
int minimap_line = ( v_scroll - > get_max ( ) < = minimap_visible_lines ) ? - 1 : first_visible_line ;
if ( minimap_line > = 0 ) {
minimap_line - = num_lines_from_rows ( first_visible_line , 0 , - num_lines_before , wi ) ;
minimap_line - = ( smooth_scroll_enabled ? 1 : 0 ) ;
}
int minimap_draw_amount = minimap_visible_lines + times_line_wraps ( minimap_line + 1 ) ;
// draw the minimap
2019-08-24 13:33:55 +00:00
Color viewport_color = ( cache . background_color . get_v ( ) < 0.5 ) ? Color ( 1 , 1 , 1 , 0.1 ) : Color ( 0 , 0 , 0 , 0.1 ) ;
2019-08-11 18:31:19 +00:00
VisualServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( ( xmargin_end + 2 ) , viewport_offset_y , cache . minimap_width , viewport_height ) , viewport_color ) ;
for ( int i = 0 ; i < minimap_draw_amount ; i + + ) {
minimap_line + + ;
if ( minimap_line < 0 | | minimap_line > = ( int ) text . size ( ) ) {
break ;
}
while ( is_line_hidden ( minimap_line ) ) {
minimap_line + + ;
if ( minimap_line < 0 | | minimap_line > = ( int ) text . size ( ) ) {
break ;
}
}
Map < int , HighlighterInfo > color_map ;
if ( syntax_coloring ) {
color_map = _get_line_syntax_highlighting ( minimap_line ) ;
}
Color current_color = cache . font_color ;
if ( readonly ) {
current_color = cache . font_color_readonly ;
}
Vector < String > wrap_rows = get_wrap_rows_text ( minimap_line ) ;
int line_wrap_amount = times_line_wraps ( minimap_line ) ;
int last_wrap_column = 0 ;
for ( int line_wrap_index = 0 ; line_wrap_index < line_wrap_amount + 1 ; line_wrap_index + + ) {
if ( line_wrap_index ! = 0 ) {
i + + ;
if ( i > = minimap_draw_amount )
break ;
}
const String & str = wrap_rows [ line_wrap_index ] ;
int indent_px = line_wrap_index ! = 0 ? get_indent_level ( minimap_line ) : 0 ;
if ( indent_px > = wrap_at ) {
indent_px = 0 ;
}
indent_px = minimap_char_size . x * indent_px ;
if ( line_wrap_index > 0 ) {
last_wrap_column + = wrap_rows [ line_wrap_index - 1 ] . length ( ) ;
}
if ( minimap_line = = cursor . line & & cursor_wrap_index = = line_wrap_index & & highlight_current_line ) {
VisualServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( ( xmargin_end + 2 ) , i * 3 , cache . minimap_width , 2 ) , cache . current_line_color ) ;
}
Color previous_color ;
int characters = 0 ;
int tabs = 0 ;
for ( int j = 0 ; j < str . length ( ) ; j + + ) {
if ( syntax_coloring ) {
if ( color_map . has ( last_wrap_column + j ) ) {
current_color = color_map [ last_wrap_column + j ] . color ;
if ( readonly ) {
current_color . a = cache . font_color_readonly . a ;
}
}
color = current_color ;
}
if ( j = = 0 ) {
previous_color = color ;
}
int xpos = indent_px + ( ( xmargin_end + minimap_char_size . x ) + ( minimap_char_size . x * j ) ) + tabs ;
bool out_of_bounds = ( xpos > = xmargin_end + cache . minimap_width ) ;
bool is_whitespace = _is_whitespace ( str [ j ] ) ;
if ( ! is_whitespace ) {
characters + + ;
if ( j < str . length ( ) - 1 & & color = = previous_color & & ! out_of_bounds ) {
continue ;
}
// If we've changed colour we are at the start of a new section, therefore we need to go back to the end
// of the previous section to draw it, we'll also add the character back on.
if ( color ! = previous_color ) {
characters - - ;
j - - ;
if ( str [ j ] = = ' \t ' ) {
tabs - = minimap_tab_size ;
}
}
}
if ( characters > 0 ) {
previous_color . a * = 0.6 ;
// take one for zero indexing, and if we hit whitespace / the end of a word.
int chars = MAX ( 0 , ( j - ( characters - 1 ) ) - ( is_whitespace ? 1 : 0 ) ) + 1 ;
int char_x_ofs = indent_px + ( ( xmargin_end + minimap_char_size . x ) + ( minimap_char_size . x * chars ) ) + tabs ;
VisualServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( Point2 ( char_x_ofs , minimap_line_height * i ) , Point2 ( minimap_char_size . x * characters , minimap_char_size . y ) ) , previous_color ) ;
}
if ( out_of_bounds ) {
break ;
}
// re-adjust if we went backwards.
if ( color ! = previous_color & & ! is_whitespace ) {
characters + + ;
}
if ( str [ j ] = = ' \t ' ) {
tabs + = minimap_tab_size ;
}
previous_color = color ;
characters = 0 ;
}
}
}
}
// draw main text
int line = first_visible_line ;
2017-11-21 14:34:04 +00:00
for ( int i = 0 ; i < draw_amount ; i + + ) {
2016-03-08 23:00:52 +00:00
2017-11-12 23:12:17 +00:00
line + + ;
if ( line < 0 | | line > = ( int ) text . size ( ) )
continue ;
while ( is_line_hidden ( line ) ) {
line + + ;
if ( line < 0 | | line > = ( int ) text . size ( ) ) {
break ;
}
}
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
if ( line < 0 | | line > = ( int ) text . size ( ) )
2014-12-17 01:31:57 +00:00
continue ;
2016-03-08 23:00:52 +00:00
2018-01-26 01:41:17 +00:00
const String & fullstr = text [ line ] ;
2016-03-08 23:00:52 +00:00
2018-01-26 01:41:17 +00:00
Map < int , HighlighterInfo > color_map ;
if ( syntax_coloring ) {
color_map = _get_line_syntax_highlighting ( line ) ;
}
2019-08-18 15:56:24 +00:00
// Ensure we at least use the font color.
2019-06-24 03:43:52 +00:00
Color current_color = readonly ? cache . font_color_readonly : cache . font_color ;
2017-08-19 14:23:45 +00:00
2017-03-05 15:44:50 +00:00
bool underlined = false ;
2016-03-08 23:00:52 +00:00
2018-12-18 13:01:15 +00:00
Vector < String > wrap_rows = get_wrap_rows_text ( line ) ;
2018-01-26 01:41:17 +00:00
int line_wrap_amount = times_line_wraps ( line ) ;
int last_wrap_column = 0 ;
2016-05-28 16:25:45 +00:00
2018-01-26 01:41:17 +00:00
for ( int line_wrap_index = 0 ; line_wrap_index < line_wrap_amount + 1 ; line_wrap_index + + ) {
if ( line_wrap_index ! = 0 ) {
i + + ;
if ( i > = draw_amount )
break ;
}
2016-05-28 16:25:45 +00:00
2018-01-26 01:41:17 +00:00
const String & str = wrap_rows [ line_wrap_index ] ;
int indent_px = line_wrap_index ! = 0 ? get_indent_level ( line ) * cache . font - > get_char_size ( ' ' ) . width : 0 ;
2019-02-20 13:52:33 +00:00
if ( indent_px > = wrap_at ) {
indent_px = 0 ;
}
2016-03-08 23:00:52 +00:00
2018-01-26 01:41:17 +00:00
if ( line_wrap_index > 0 )
last_wrap_column + = wrap_rows [ line_wrap_index - 1 ] . length ( ) ;
int char_margin = xmargin_beg - cursor . x_ofs ;
char_margin + = indent_px ;
int char_ofs = 0 ;
int ofs_readonly = 0 ;
int ofs_x = 0 ;
if ( readonly ) {
ofs_readonly = cache . style_readonly - > get_offset ( ) . y / 2 ;
ofs_x = cache . style_readonly - > get_offset ( ) . x / 2 ;
2018-04-02 11:41:44 +00:00
}
2016-03-08 23:00:52 +00:00
2018-01-26 01:41:17 +00:00
int ofs_y = ( i * get_row_height ( ) + cache . line_spacing / 2 ) + ofs_readonly ;
ofs_y - = cursor . wrap_ofs * get_row_height ( ) ;
if ( smooth_scroll_enabled )
ofs_y + = ( - get_v_scroll_offset ( ) ) * get_row_height ( ) ;
2016-03-08 23:00:52 +00:00
2019-08-18 15:56:24 +00:00
// Check if line contains highlighted word.
2018-01-26 01:41:17 +00:00
int highlighted_text_col = - 1 ;
int search_text_col = - 1 ;
int highlighted_word_col = - 1 ;
if ( ! search_text . empty ( ) )
search_text_col = _get_column_pos_of_word ( search_text , str , search_flags , 0 ) ;
2016-03-08 23:00:52 +00:00
2018-01-26 01:41:17 +00:00
if ( highlighted_text . length ( ) ! = 0 & & highlighted_text ! = search_text )
highlighted_text_col = _get_column_pos_of_word ( highlighted_text , str , SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS , 0 ) ;
if ( select_identifiers_enabled & & highlighted_word . length ( ) ! = 0 ) {
if ( _is_char ( highlighted_word [ 0 ] ) ) {
highlighted_word_col = _get_column_pos_of_word ( highlighted_word , fullstr , SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS , 0 ) ;
}
2017-10-21 20:35:50 +00:00
}
2018-01-26 01:41:17 +00:00
if ( text . is_marked ( line ) ) {
VisualServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( xmargin_beg + ofs_x , ofs_y , xmargin_end - xmargin_beg , get_row_height ( ) ) , cache . mark_color ) ;
2017-10-21 20:35:50 +00:00
}
2018-01-26 01:41:17 +00:00
if ( str . length ( ) = = 0 ) {
2019-08-18 15:56:24 +00:00
// Draw line background if empty as we won't loop at at all.
2018-01-26 01:41:17 +00:00
if ( line = = cursor . line & & cursor_wrap_index = = line_wrap_index & & highlight_current_line ) {
VisualServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( ofs_x , ofs_y , xmargin_end , get_row_height ( ) ) , cache . current_line_color ) ;
}
2019-08-18 15:56:24 +00:00
// Give visual indication of empty selected line.
2018-01-26 01:41:17 +00:00
if ( selection . active & & line > = selection . from_line & & line < = selection . to_line & & char_margin > = xmargin_beg ) {
int char_w = cache . font - > get_char_size ( ' ' ) . width ;
VisualServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( xmargin_beg + ofs_x , ofs_y , char_w , get_row_height ( ) ) , cache . selection_color ) ;
}
} else {
2019-08-18 15:56:24 +00:00
// If it has text, then draw current line marker in the margin, as line number etc will draw over it, draw the rest of line marker later.
2018-01-26 01:41:17 +00:00
if ( line = = cursor . line & & cursor_wrap_index = = line_wrap_index & & highlight_current_line ) {
2019-08-18 15:56:24 +00:00
VisualServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( 0 , ofs_y , xmargin_beg + ofs_x , get_row_height ( ) ) , cache . current_line_color ) ;
2018-01-26 01:41:17 +00:00
}
2017-12-04 21:48:20 +00:00
}
2017-10-21 20:35:50 +00:00
2018-01-26 01:41:17 +00:00
if ( line_wrap_index = = 0 ) {
2019-08-18 15:56:24 +00:00
// Only do these if we are on the first wrapped part of a line.
2018-01-26 01:41:17 +00:00
if ( text . is_breakpoint ( line ) & & ! draw_breakpoint_gutter ) {
2017-09-17 04:33:44 +00:00
# ifdef TOOLS_ENABLED
2018-01-26 01:41:17 +00:00
VisualServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( xmargin_beg + ofs_x , ofs_y + get_row_height ( ) - EDSCALE , xmargin_end - xmargin_beg , EDSCALE ) , cache . breakpoint_color ) ;
2017-09-17 04:33:44 +00:00
# else
2018-01-26 01:41:17 +00:00
VisualServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( xmargin_beg + ofs_x , ofs_y , xmargin_end - xmargin_beg , get_row_height ( ) ) , cache . breakpoint_color ) ;
2017-09-17 04:33:44 +00:00
# endif
2018-01-26 01:41:17 +00:00
}
2017-09-15 01:02:05 +00:00
2019-08-18 15:56:24 +00:00
// Draw bookmark marker.
2019-04-19 23:51:25 +00:00
if ( text . is_bookmark ( line ) ) {
if ( draw_bookmark_gutter ) {
int vertical_gap = ( get_row_height ( ) * 40 ) / 100 ;
int horizontal_gap = ( cache . breakpoint_gutter_width * 30 ) / 100 ;
int marker_radius = get_row_height ( ) - ( vertical_gap * 2 ) ;
VisualServer : : get_singleton ( ) - > canvas_item_add_circle ( ci , Point2 ( cache . style_normal - > get_margin ( MARGIN_LEFT ) + horizontal_gap - 2 + marker_radius / 2 , ofs_y + vertical_gap + marker_radius / 2 ) , marker_radius , Color ( cache . bookmark_color . r , cache . bookmark_color . g , cache . bookmark_color . b ) ) ;
}
}
2019-08-18 15:56:24 +00:00
// Draw breakpoint marker.
2018-01-26 01:41:17 +00:00
if ( text . is_breakpoint ( line ) ) {
if ( draw_breakpoint_gutter ) {
int vertical_gap = ( get_row_height ( ) * 40 ) / 100 ;
int horizontal_gap = ( cache . breakpoint_gutter_width * 30 ) / 100 ;
int marker_height = get_row_height ( ) - ( vertical_gap * 2 ) ;
int marker_width = cache . breakpoint_gutter_width - ( horizontal_gap * 2 ) ;
2019-08-18 15:56:24 +00:00
// No transparency on marker.
2018-01-26 01:41:17 +00:00
VisualServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( cache . style_normal - > get_margin ( MARGIN_LEFT ) + horizontal_gap - 2 , ofs_y + vertical_gap , marker_width , marker_height ) , Color ( cache . breakpoint_color . r , cache . breakpoint_color . g , cache . breakpoint_color . b ) ) ;
}
}
2016-03-08 23:00:52 +00:00
2019-08-18 15:56:24 +00:00
// Draw info icons.
2019-04-20 11:51:25 +00:00
if ( draw_info_gutter & & text . has_info_icon ( line ) ) {
2019-04-24 06:13:25 +00:00
int vertical_gap = ( get_row_height ( ) * 40 ) / 100 ;
2019-04-20 11:51:25 +00:00
int horizontal_gap = ( cache . info_gutter_width * 30 ) / 100 ;
int gutter_left = cache . style_normal - > get_margin ( MARGIN_LEFT ) + cache . breakpoint_gutter_width ;
Ref < Texture > info_icon = text . get_info_icon ( line ) ;
2019-08-18 15:56:24 +00:00
// Ensure the icon fits the gutter size.
2019-04-20 11:51:25 +00:00
Size2i icon_size = info_icon - > get_size ( ) ;
if ( icon_size . width > cache . info_gutter_width - horizontal_gap ) {
icon_size . width = cache . info_gutter_width - horizontal_gap ;
}
if ( icon_size . height > get_row_height ( ) - horizontal_gap ) {
icon_size . height = get_row_height ( ) - horizontal_gap ;
}
Size2i icon_pos ;
2019-04-24 06:13:25 +00:00
int xofs = horizontal_gap - ( info_icon - > get_width ( ) / 4 ) ;
int yofs = vertical_gap - ( info_icon - > get_height ( ) / 4 ) ;
2019-04-20 11:51:25 +00:00
icon_pos . x = gutter_left + xofs + ofs_x ;
icon_pos . y = ofs_y + yofs ;
draw_texture_rect ( info_icon , Rect2 ( icon_pos , icon_size ) ) ;
}
2019-08-18 15:56:24 +00:00
// Draw execution marker.
2019-04-22 16:20:27 +00:00
if ( executing_line = = line ) {
if ( draw_breakpoint_gutter ) {
int icon_extra_size = 4 ;
int vertical_gap = ( get_row_height ( ) * 40 ) / 100 ;
int horizontal_gap = ( cache . breakpoint_gutter_width * 30 ) / 100 ;
int marker_height = get_row_height ( ) - ( vertical_gap * 2 ) + icon_extra_size ;
int marker_width = cache . breakpoint_gutter_width - ( horizontal_gap * 2 ) + icon_extra_size ;
cache . executing_icon - > draw_rect ( ci , Rect2 ( cache . style_normal - > get_margin ( MARGIN_LEFT ) + horizontal_gap - 2 - icon_extra_size / 2 , ofs_y + vertical_gap - icon_extra_size / 2 , marker_width , marker_height ) , false , Color ( cache . executing_line_color . r , cache . executing_line_color . g , cache . executing_line_color . b ) ) ;
} else {
# ifdef TOOLS_ENABLED
VisualServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( xmargin_beg + ofs_x , ofs_y + get_row_height ( ) - EDSCALE , xmargin_end - xmargin_beg , EDSCALE ) , cache . executing_line_color ) ;
# else
VisualServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( xmargin_beg + ofs_x , ofs_y , xmargin_end - xmargin_beg , get_row_height ( ) ) , cache . executing_line_color ) ;
# endif
}
}
2019-08-18 15:56:24 +00:00
// Draw fold markers.
2018-01-26 01:41:17 +00:00
if ( draw_fold_gutter ) {
int horizontal_gap = ( cache . fold_gutter_width * 30 ) / 100 ;
2019-04-20 11:51:25 +00:00
int gutter_left = cache . style_normal - > get_margin ( MARGIN_LEFT ) + cache . breakpoint_gutter_width + cache . line_number_w + cache . info_gutter_width ;
2018-01-26 01:41:17 +00:00
if ( is_folded ( line ) ) {
int xofs = horizontal_gap - ( cache . can_fold_icon - > get_width ( ) ) / 2 ;
int yofs = ( get_row_height ( ) - cache . folded_icon - > get_height ( ) ) / 2 ;
cache . folded_icon - > draw ( ci , Point2 ( gutter_left + xofs + ofs_x , ofs_y + yofs ) , cache . code_folding_color ) ;
} else if ( can_fold ( line ) ) {
int xofs = - cache . can_fold_icon - > get_width ( ) / 2 - horizontal_gap + 3 ;
int yofs = ( get_row_height ( ) - cache . can_fold_icon - > get_height ( ) ) / 2 ;
cache . can_fold_icon - > draw ( ci , Point2 ( gutter_left + xofs + ofs_x , ofs_y + yofs ) , cache . code_folding_color ) ;
}
}
2019-08-18 15:56:24 +00:00
// Draw line numbers.
2018-01-26 01:41:17 +00:00
if ( cache . line_number_w ) {
2018-06-23 15:19:27 +00:00
int yofs = ofs_y + ( get_row_height ( ) - cache . font - > get_height ( ) ) / 2 ;
2018-01-26 01:41:17 +00:00
String fc = String : : num ( line + 1 ) ;
while ( fc . length ( ) < line_number_char_count ) {
fc = line_num_padding + fc ;
}
2017-11-12 23:12:17 +00:00
2019-04-20 11:51:25 +00:00
cache . font - > draw ( ci , Point2 ( cache . style_normal - > get_margin ( MARGIN_LEFT ) + cache . breakpoint_gutter_width + cache . info_gutter_width + ofs_x , yofs + cache . font - > get_ascent ( ) ) , fc , text . is_safe ( line ) ? cache . safe_line_number_color : cache . line_number_color ) ;
2018-01-26 01:41:17 +00:00
}
2016-05-30 15:28:31 +00:00
}
2016-03-08 23:00:52 +00:00
2019-08-18 15:56:24 +00:00
// Loop through characters in one line.
2018-01-26 01:41:17 +00:00
for ( int j = 0 ; j < str . length ( ) ; j + + ) {
2016-03-08 23:00:52 +00:00
2018-01-26 01:41:17 +00:00
if ( syntax_coloring ) {
if ( color_map . has ( last_wrap_column + j ) ) {
2019-08-18 15:56:24 +00:00
current_color = color_map [ last_wrap_column + j ] . color ;
if ( readonly & & current_color . a > cache . font_color_readonly . a ) {
current_color . a = cache . font_color_readonly . a ;
}
2018-01-26 01:41:17 +00:00
}
color = current_color ;
}
2016-04-02 19:46:42 +00:00
2018-01-26 01:41:17 +00:00
int char_w ;
2019-08-18 15:56:24 +00:00
// Handle tabulator.
2018-01-26 01:41:17 +00:00
char_w = text . get_char_width ( str [ j ] , str [ j + 1 ] , char_ofs ) ;
if ( ( char_ofs + char_margin ) < xmargin_beg ) {
char_ofs + = char_w ;
2016-04-05 14:50:54 +00:00
2019-08-18 15:56:24 +00:00
// Line highlighting handle horizontal clipping.
2018-01-26 01:41:17 +00:00
if ( line = = cursor . line & & cursor_wrap_index = = line_wrap_index & & highlight_current_line ) {
if ( j = = str . length ( ) - 1 ) {
2019-08-18 15:56:24 +00:00
// End of line when last char is skipped.
2018-01-26 01:41:17 +00:00
VisualServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( xmargin_beg + ofs_x , ofs_y , xmargin_end - ( char_ofs + char_margin + char_w ) , get_row_height ( ) ) , cache . current_line_color ) ;
} else if ( ( char_ofs + char_margin ) > xmargin_beg ) {
2019-08-18 15:56:24 +00:00
// Char next to margin is skipped.
2018-01-26 01:41:17 +00:00
VisualServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( xmargin_beg + ofs_x , ofs_y , ( char_ofs + char_margin ) - ( xmargin_beg + ofs_x ) , get_row_height ( ) ) , cache . current_line_color ) ;
}
2016-04-05 14:50:54 +00:00
}
2018-01-26 01:41:17 +00:00
continue ;
2016-04-05 14:50:54 +00:00
}
2016-03-08 23:00:52 +00:00
2018-01-26 01:41:17 +00:00
if ( ( char_ofs + char_margin + char_w ) > = xmargin_end ) {
2019-05-16 21:32:06 +00:00
break ;
2018-01-26 01:41:17 +00:00
}
2016-03-08 23:00:52 +00:00
2018-01-26 01:41:17 +00:00
bool in_search_result = false ;
2016-03-08 23:00:52 +00:00
2018-01-26 01:41:17 +00:00
if ( search_text_col ! = - 1 ) {
2019-08-18 15:56:24 +00:00
// If we are at the end check for new search result on same line.
2018-01-26 01:41:17 +00:00
if ( j > = search_text_col + search_text . length ( ) )
search_text_col = _get_column_pos_of_word ( search_text , str , search_flags , j ) ;
2016-03-08 23:00:52 +00:00
2018-01-26 01:41:17 +00:00
in_search_result = j > = search_text_col & & j < search_text_col + search_text . length ( ) ;
2018-01-14 16:06:27 +00:00
2018-01-26 01:41:17 +00:00
if ( in_search_result ) {
VisualServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( Point2i ( char_ofs + char_margin , ofs_y ) , Size2i ( char_w , get_row_height ( ) ) ) , cache . search_result_color ) ;
}
}
2018-01-14 16:06:27 +00:00
2019-08-18 15:56:24 +00:00
// Current line highlighting.
2018-01-26 01:41:17 +00:00
bool in_selection = ( selection . active & & line > = selection . from_line & & line < = selection . to_line & & ( line > selection . from_line | | last_wrap_column + j > = selection . from_column ) & & ( line < selection . to_line | | last_wrap_column + j < selection . to_column ) ) ;
2018-01-31 17:59:19 +00:00
2018-01-26 01:41:17 +00:00
if ( line = = cursor . line & & cursor_wrap_index = = line_wrap_index & & highlight_current_line ) {
2019-08-18 15:56:24 +00:00
// Draw the wrap indent offset highlight.
2018-01-26 01:41:17 +00:00
if ( line_wrap_index ! = 0 & & j = = 0 ) {
2019-08-18 15:56:24 +00:00
VisualServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( char_ofs + char_margin + ofs_x - indent_px , ofs_y , indent_px , get_row_height ( ) ) , cache . current_line_color ) ;
2018-01-26 01:41:17 +00:00
}
2019-08-18 15:56:24 +00:00
// If its the last char draw to end of the line.
2018-01-26 01:41:17 +00:00
if ( j = = str . length ( ) - 1 ) {
2019-08-18 15:56:24 +00:00
VisualServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( char_ofs + char_margin + char_w + ofs_x , ofs_y , xmargin_end - ( char_ofs + char_margin + char_w ) , get_row_height ( ) ) , cache . current_line_color ) ;
2018-01-26 01:41:17 +00:00
}
2019-08-18 15:56:24 +00:00
// Actual text.
2018-01-26 01:41:17 +00:00
if ( ! in_selection ) {
VisualServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( Point2i ( char_ofs + char_margin + ofs_x , ofs_y ) , Size2i ( char_w , get_row_height ( ) ) ) , cache . current_line_color ) ;
2018-01-14 16:06:27 +00:00
}
}
2016-03-08 23:00:52 +00:00
2018-01-26 01:41:17 +00:00
if ( in_selection ) {
VisualServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( Point2i ( char_ofs + char_margin + ofs_x , ofs_y ) , Size2i ( char_w , get_row_height ( ) ) ) , cache . selection_color ) ;
}
2016-05-28 16:25:45 +00:00
2018-01-26 01:41:17 +00:00
if ( in_search_result ) {
Color border_color = ( line = = search_result_line & & j > = search_result_col & & j < search_result_col + search_text . length ( ) ) ? cache . font_color : cache . search_result_border_color ;
2016-03-08 23:00:52 +00:00
2018-01-26 01:41:17 +00:00
VisualServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( Point2i ( char_ofs + char_margin + ofs_x , ofs_y ) , Size2i ( char_w , 1 ) ) , border_color ) ;
VisualServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( Point2i ( char_ofs + char_margin + ofs_x , ofs_y + get_row_height ( ) - 1 ) , Size2i ( char_w , 1 ) ) , border_color ) ;
2016-05-28 16:25:45 +00:00
2018-01-26 01:41:17 +00:00
if ( j = = search_text_col )
VisualServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( Point2i ( char_ofs + char_margin + ofs_x , ofs_y ) , Size2i ( 1 , get_row_height ( ) ) ) , border_color ) ;
if ( j = = search_text_col + search_text . length ( ) - 1 )
VisualServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( Point2i ( char_ofs + char_margin + char_w + ofs_x - 1 , ofs_y ) , Size2i ( 1 , get_row_height ( ) ) ) , border_color ) ;
2016-05-28 16:25:45 +00:00
}
2019-04-27 16:02:09 +00:00
if ( highlight_all_occurrences & & ! only_whitespaces_highlighted ) {
2018-01-26 01:41:17 +00:00
if ( highlighted_text_col ! = - 1 ) {
2019-08-18 15:56:24 +00:00
// If we are at the end check for new word on same line.
2018-01-26 01:41:17 +00:00
if ( j > highlighted_text_col + highlighted_text . length ( ) ) {
highlighted_text_col = _get_column_pos_of_word ( highlighted_text , str , SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS , j ) ;
}
bool in_highlighted_word = ( j > = highlighted_text_col & & j < highlighted_text_col + highlighted_text . length ( ) ) ;
2016-03-08 23:00:52 +00:00
2019-08-18 15:56:24 +00:00
// If this is the original highlighted text we don't want to highlight it again.
2018-01-26 01:41:17 +00:00
if ( cursor . line = = line & & cursor_wrap_index = = line_wrap_index & & ( cursor . column > = highlighted_text_col & & cursor . column < = highlighted_text_col + highlighted_text . length ( ) ) ) {
in_highlighted_word = false ;
}
if ( in_highlighted_word ) {
VisualServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( Point2i ( char_ofs + char_margin + ofs_x , ofs_y ) , Size2i ( char_w , get_row_height ( ) ) ) , cache . word_highlighted_color ) ;
}
}
2017-10-21 20:35:50 +00:00
}
2018-01-26 01:41:17 +00:00
if ( highlighted_word_col ! = - 1 ) {
if ( j + last_wrap_column > highlighted_word_col + highlighted_word . length ( ) ) {
highlighted_word_col = _get_column_pos_of_word ( highlighted_word , fullstr , SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS , j + last_wrap_column ) ;
}
underlined = ( j + last_wrap_column > = highlighted_word_col & & j + last_wrap_column < highlighted_word_col + highlighted_word . length ( ) ) ;
2017-10-21 20:35:50 +00:00
}
2017-09-28 14:10:30 +00:00
2018-01-26 01:41:17 +00:00
if ( brace_matching_enabled ) {
2018-06-23 15:19:27 +00:00
int yofs = ofs_y + ( get_row_height ( ) - cache . font - > get_height ( ) ) / 2 ;
2018-01-26 01:41:17 +00:00
if ( ( brace_open_match_line = = line & & brace_open_match_column = = last_wrap_column + j ) | |
( cursor . column = = last_wrap_column + j & & cursor . line = = line & & cursor_wrap_index = = line_wrap_index & & ( brace_open_matching | | brace_open_mismatch ) ) ) {
2016-03-08 23:00:52 +00:00
2018-01-26 01:41:17 +00:00
if ( brace_open_mismatch )
color = cache . brace_mismatch_color ;
2019-06-24 05:17:27 +00:00
drawer . draw_char ( ci , Point2i ( char_ofs + char_margin + ofs_x , yofs + ascent ) , ' _ ' , str [ j + 1 ] , in_selection & & override_selected_font_color ? cache . font_color_selected : color ) ;
2018-01-26 01:41:17 +00:00
}
2016-05-28 16:25:45 +00:00
2018-01-26 01:41:17 +00:00
if ( ( brace_close_match_line = = line & & brace_close_match_column = = last_wrap_column + j ) | |
( cursor . column = = last_wrap_column + j + 1 & & cursor . line = = line & & cursor_wrap_index = = line_wrap_index & & ( brace_close_matching | | brace_close_mismatch ) ) ) {
2016-05-28 16:25:45 +00:00
2018-01-26 01:41:17 +00:00
if ( brace_close_mismatch )
color = cache . brace_mismatch_color ;
2019-06-24 05:17:27 +00:00
drawer . draw_char ( ci , Point2i ( char_ofs + char_margin + ofs_x , yofs + ascent ) , ' _ ' , str [ j + 1 ] , in_selection & & override_selected_font_color ? cache . font_color_selected : color ) ;
2018-01-26 01:41:17 +00:00
}
}
if ( cursor . column = = last_wrap_column + j & & cursor . line = = line & & cursor_wrap_index = = line_wrap_index ) {
2016-05-28 16:25:45 +00:00
2018-01-26 01:41:17 +00:00
cursor_pos = Point2i ( char_ofs + char_margin + ofs_x , ofs_y ) ;
2018-12-22 19:35:58 +00:00
cursor_pos . y + = ( get_row_height ( ) - cache . font - > get_height ( ) ) / 2 ;
2016-03-16 21:20:42 +00:00
2018-01-26 01:41:17 +00:00
if ( insert_mode ) {
2018-12-31 11:38:42 +00:00
cursor_insert_offset_y = ( cache . font - > get_height ( ) - 3 ) ;
cursor_pos . y + = cursor_insert_offset_y ;
2016-03-16 21:20:42 +00:00
}
2018-01-26 01:41:17 +00:00
int caret_w = ( str [ j ] = = ' \t ' ) ? cache . font - > get_char_size ( ' ' ) . width : char_w ;
if ( ime_text . length ( ) > 0 ) {
int ofs = 0 ;
while ( true ) {
if ( ofs > = ime_text . length ( ) )
break ;
2016-07-10 15:10:54 +00:00
2018-01-26 01:41:17 +00:00
CharType cchar = ime_text [ ofs ] ;
CharType next = ime_text [ ofs + 1 ] ;
int im_char_width = cache . font - > get_char_size ( cchar , next ) . width ;
if ( ( char_ofs + char_margin + im_char_width ) > = xmargin_end )
break ;
2016-07-10 15:10:54 +00:00
2018-01-26 01:41:17 +00:00
bool selected = ofs > = ime_selection . x & & ofs < ime_selection . x + ime_selection . y ;
if ( selected ) {
VisualServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( Point2 ( char_ofs + char_margin , ofs_y + get_row_height ( ) ) , Size2 ( im_char_width , 3 ) ) , color ) ;
} else {
VisualServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( Point2 ( char_ofs + char_margin , ofs_y + get_row_height ( ) ) , Size2 ( im_char_width , 1 ) ) , color ) ;
}
drawer . draw_char ( ci , Point2 ( char_ofs + char_margin + ofs_x , ofs_y + ascent ) , cchar , next , color ) ;
char_ofs + = im_char_width ;
ofs + + ;
}
}
if ( ime_text . length ( ) = = 0 ) {
if ( draw_caret ) {
if ( insert_mode ) {
2018-07-26 21:41:47 +00:00
# ifdef TOOLS_ENABLED
int caret_h = ( block_caret ) ? 4 : 2 * EDSCALE ;
# else
int caret_h = ( block_caret ) ? 4 : 2 ;
# endif
2018-01-26 01:41:17 +00:00
VisualServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( cursor_pos , Size2i ( caret_w , caret_h ) ) , cache . caret_color ) ;
} else {
2018-07-26 21:41:47 +00:00
# ifdef TOOLS_ENABLED
caret_w = ( block_caret ) ? caret_w : 2 * EDSCALE ;
# else
caret_w = ( block_caret ) ? caret_w : 2 ;
# endif
2018-12-22 19:35:58 +00:00
VisualServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( cursor_pos , Size2i ( caret_w , cache . font - > get_height ( ) ) ) , cache . caret_color ) ;
2018-01-26 01:41:17 +00:00
}
}
2016-03-16 21:20:42 +00:00
}
}
2016-03-08 23:00:52 +00:00
2018-01-26 01:41:17 +00:00
if ( cursor . column = = last_wrap_column + j & & cursor . line = = line & & cursor_wrap_index = = line_wrap_index & & block_caret & & draw_caret & & ! insert_mode ) {
color = cache . caret_background_color ;
} else if ( ! syntax_coloring & & block_caret ) {
2019-06-24 03:43:52 +00:00
color = readonly ? cache . font_color_readonly : cache . font_color ;
2018-04-02 11:41:44 +00:00
}
2016-03-08 23:00:52 +00:00
2018-01-26 01:41:17 +00:00
if ( str [ j ] > = 32 ) {
2018-06-23 15:19:27 +00:00
int yofs = ofs_y + ( get_row_height ( ) - cache . font - > get_height ( ) ) / 2 ;
2019-06-24 05:17:27 +00:00
int w = drawer . draw_char ( ci , Point2i ( char_ofs + char_margin + ofs_x , yofs + ascent ) , str [ j ] , str [ j + 1 ] , in_selection & & override_selected_font_color ? cache . font_color_selected : color ) ;
2018-01-26 01:41:17 +00:00
if ( underlined ) {
2018-08-02 07:38:56 +00:00
float line_width = 1.0 ;
# ifdef TOOLS_ENABLED
line_width * = EDSCALE ;
# endif
2019-06-24 05:17:27 +00:00
draw_rect ( Rect2 ( char_ofs + char_margin + ofs_x , yofs + ascent + 2 , w , line_width ) , in_selection & & override_selected_font_color ? cache . font_color_selected : color ) ;
2018-01-26 01:41:17 +00:00
}
2018-12-13 18:57:34 +00:00
} else if ( draw_tabs & & str [ j ] = = ' \t ' ) {
2018-01-26 01:41:17 +00:00
int yofs = ( get_row_height ( ) - cache . tab_icon - > get_height ( ) ) / 2 ;
2019-06-24 05:17:27 +00:00
cache . tab_icon - > draw ( ci , Point2 ( char_ofs + char_margin + ofs_x , ofs_y + yofs ) , in_selection & & override_selected_font_color ? cache . font_color_selected : color ) ;
2014-12-17 04:53:34 +00:00
}
2016-03-08 23:00:52 +00:00
2019-04-23 23:33:20 +00:00
if ( draw_spaces & & str [ j ] = = ' ' ) {
int yofs = ( get_row_height ( ) - cache . space_icon - > get_height ( ) ) / 2 ;
2019-06-24 05:17:27 +00:00
cache . space_icon - > draw ( ci , Point2 ( char_ofs + char_margin + ofs_x , ofs_y + yofs ) , in_selection & & override_selected_font_color ? cache . font_color_selected : color ) ;
2019-04-23 23:33:20 +00:00
}
2018-01-26 01:41:17 +00:00
char_ofs + = char_w ;
2016-03-08 23:00:52 +00:00
2018-01-26 01:41:17 +00:00
if ( line_wrap_index = = line_wrap_amount & & j = = str . length ( ) - 1 & & is_folded ( line ) ) {
int yofs = ( get_row_height ( ) - cache . folded_eol_icon - > get_height ( ) ) / 2 ;
int xofs = cache . folded_eol_icon - > get_width ( ) / 2 ;
Color eol_color = cache . code_folding_color ;
eol_color . a = 1 ;
cache . folded_eol_icon - > draw ( ci , Point2 ( char_ofs + char_margin + xofs + ofs_x , ofs_y + yofs ) , eol_color ) ;
2014-12-17 04:53:34 +00:00
}
}
2016-03-08 23:00:52 +00:00
2018-01-26 01:41:17 +00:00
if ( cursor . column = = last_wrap_column + str . length ( ) & & cursor . line = = line & & cursor_wrap_index = = line_wrap_index & & ( char_ofs + char_margin ) > = xmargin_beg ) {
2016-03-08 23:00:52 +00:00
2017-11-30 03:53:15 +00:00
cursor_pos = Point2i ( char_ofs + char_margin + ofs_x , ofs_y ) ;
2018-12-22 19:35:58 +00:00
cursor_pos . y + = ( get_row_height ( ) - cache . font - > get_height ( ) ) / 2 ;
2016-05-09 18:21:55 +00:00
2016-03-31 19:49:30 +00:00
if ( insert_mode ) {
2018-12-31 11:38:42 +00:00
cursor_insert_offset_y = cache . font - > get_height ( ) - 3 ;
cursor_pos . y + = cursor_insert_offset_y ;
2016-03-31 19:49:30 +00:00
}
2017-08-07 11:09:56 +00:00
if ( ime_text . length ( ) > 0 ) {
int ofs = 0 ;
while ( true ) {
if ( ofs > = ime_text . length ( ) )
break ;
CharType cchar = ime_text [ ofs ] ;
CharType next = ime_text [ ofs + 1 ] ;
int im_char_width = cache . font - > get_char_size ( cchar , next ) . width ;
if ( ( char_ofs + char_margin + im_char_width ) > = xmargin_end )
break ;
bool selected = ofs > = ime_selection . x & & ofs < ime_selection . x + ime_selection . y ;
if ( selected ) {
VisualServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( Point2 ( char_ofs + char_margin , ofs_y + get_row_height ( ) ) , Size2 ( im_char_width , 3 ) ) , color ) ;
} else {
VisualServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( Point2 ( char_ofs + char_margin , ofs_y + get_row_height ( ) ) , Size2 ( im_char_width , 1 ) ) , color ) ;
}
2018-03-17 08:44:34 +00:00
drawer . draw_char ( ci , Point2 ( char_ofs + char_margin + ofs_x , ofs_y + ascent ) , cchar , next , color ) ;
2017-08-07 11:09:56 +00:00
char_ofs + = im_char_width ;
ofs + + ;
}
}
if ( ime_text . length ( ) = = 0 ) {
if ( draw_caret ) {
if ( insert_mode ) {
2018-01-26 01:41:17 +00:00
int char_w = cache . font - > get_char_size ( ' ' ) . width ;
2018-07-26 21:41:47 +00:00
# ifdef TOOLS_ENABLED
int caret_h = ( block_caret ) ? 4 : 2 * EDSCALE ;
# else
int caret_h = ( block_caret ) ? 4 : 2 ;
# endif
2018-01-26 01:41:17 +00:00
VisualServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( cursor_pos , Size2i ( char_w , caret_h ) ) , cache . caret_color ) ;
2017-08-07 11:09:56 +00:00
} else {
2018-01-26 01:41:17 +00:00
int char_w = cache . font - > get_char_size ( ' ' ) . width ;
2018-07-26 21:41:47 +00:00
# ifdef TOOLS_ENABLED
int caret_w = ( block_caret ) ? char_w : 2 * EDSCALE ;
# else
int caret_w = ( block_caret ) ? char_w : 2 ;
# endif
2018-12-22 19:35:58 +00:00
VisualServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( cursor_pos , Size2i ( caret_w , cache . font - > get_height ( ) ) ) , cache . caret_color ) ;
2017-08-07 11:09:56 +00:00
}
2016-05-09 18:21:55 +00:00
}
}
2014-12-17 01:31:57 +00:00
}
}
}
2016-03-08 23:00:52 +00:00
2016-03-04 10:05:42 +00:00
bool completion_below = false ;
2014-12-17 01:31:57 +00:00
if ( completion_active ) {
2019-08-18 15:56:24 +00:00
// Code completion box.
2014-12-17 01:31:57 +00:00
Ref < StyleBox > csb = get_stylebox ( " completion " ) ;
int maxlines = get_constant ( " completion_lines " ) ;
2017-03-05 15:44:50 +00:00
int cmax_width = get_constant ( " completion_max_width " ) * cache . font - > get_char_size ( ' x ' ) . x ;
2014-12-17 01:31:57 +00:00
int scrollw = get_constant ( " completion_scroll_width " ) ;
Color scrollc = get_color ( " completion_scroll_color " ) ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
int lines = MIN ( completion_options . size ( ) , maxlines ) ;
int w = 0 ;
int h = lines * get_row_height ( ) ;
2014-12-17 01:31:57 +00:00
int nofs = cache . font - > get_string_size ( completion_base ) . width ;
2016-03-08 23:00:52 +00:00
2014-12-17 01:31:57 +00:00
if ( completion_options . size ( ) < 50 ) {
2017-03-05 15:44:50 +00:00
for ( int i = 0 ; i < completion_options . size ( ) ; i + + ) {
2019-06-13 09:32:03 +00:00
int w2 = MIN ( cache . font - > get_string_size ( completion_options [ i ] . display ) . x , cmax_width ) ;
2017-03-05 15:44:50 +00:00
if ( w2 > w )
w = w2 ;
2014-12-17 01:31:57 +00:00
}
} else {
2017-03-05 15:44:50 +00:00
w = cmax_width ;
2014-12-17 01:31:57 +00:00
}
2016-03-08 23:00:52 +00:00
2019-08-18 15:56:24 +00:00
// Add space for completion icons.
2019-06-13 09:32:03 +00:00
const int icon_hsep = get_constant ( " hseparation " , " ItemList " ) ;
Size2 icon_area_size ( get_row_height ( ) , get_row_height ( ) ) ;
w + = icon_area_size . width + icon_hsep ;
2014-12-17 01:31:57 +00:00
int th = h + csb - > get_minimum_size ( ) . y ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
if ( cursor_pos . y + get_row_height ( ) + th > get_size ( ) . height ) {
2018-12-31 11:38:42 +00:00
completion_rect . position . y = cursor_pos . y - th - ( cache . line_spacing / 2.0f ) - cursor_insert_offset_y ;
2014-12-17 01:31:57 +00:00
} else {
2018-12-31 11:38:42 +00:00
completion_rect . position . y = cursor_pos . y + cache . font - > get_height ( ) + ( cache . line_spacing / 2.0f ) + csb - > get_offset ( ) . y - cursor_insert_offset_y ;
2016-03-04 10:05:42 +00:00
completion_below = true ;
2014-12-17 01:31:57 +00:00
}
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
if ( cursor_pos . x - nofs + w + scrollw > get_size ( ) . width ) {
2017-06-03 22:25:13 +00:00
completion_rect . position . x = get_size ( ) . width - w - scrollw ;
2014-12-17 01:31:57 +00:00
} else {
2017-06-03 22:25:13 +00:00
completion_rect . position . x = cursor_pos . x - nofs ;
2014-12-17 01:31:57 +00:00
}
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
completion_rect . size . width = w + 2 ;
completion_rect . size . height = h ;
if ( completion_options . size ( ) < = maxlines )
scrollw = 0 ;
2016-03-08 23:00:52 +00:00
2017-06-03 22:25:13 +00:00
draw_style_box ( csb , Rect2 ( completion_rect . position - csb - > get_offset ( ) , completion_rect . size + csb - > get_minimum_size ( ) + Size2 ( scrollw , 0 ) ) ) ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
if ( cache . completion_background_color . a > 0.01 ) {
2017-06-03 22:25:13 +00:00
VisualServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( completion_rect . position , completion_rect . size + Size2 ( scrollw , 0 ) ) , cache . completion_background_color ) ;
2016-06-21 15:50:31 +00:00
}
2017-03-05 15:44:50 +00:00
int line_from = CLAMP ( completion_index - lines / 2 , 0 , completion_options . size ( ) - lines ) ;
2017-06-03 22:25:13 +00:00
VisualServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( Point2 ( completion_rect . position . x , completion_rect . position . y + ( completion_index - line_from ) * get_row_height ( ) ) , Size2 ( completion_rect . size . width , get_row_height ( ) ) ) , cache . completion_selected_color ) ;
2019-08-15 15:53:28 +00:00
draw_rect ( Rect2 ( completion_rect . position + Vector2 ( icon_area_size . x + icon_hsep , 0 ) , Size2 ( MIN ( nofs , completion_rect . size . width - ( icon_area_size . x + icon_hsep ) ) , completion_rect . size . height ) ) , cache . completion_existing_color ) ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
for ( int i = 0 ; i < lines ; i + + ) {
2016-03-08 23:00:52 +00:00
2014-12-17 01:31:57 +00:00
int l = line_from + i ;
2017-03-05 15:44:50 +00:00
ERR_CONTINUE ( l < 0 | | l > = completion_options . size ( ) ) ;
2016-07-11 13:25:56 +00:00
Color text_color = cache . completion_font_color ;
2017-03-05 15:44:50 +00:00
for ( int j = 0 ; j < color_regions . size ( ) ; j + + ) {
2019-06-13 09:32:03 +00:00
if ( completion_options [ l ] . insert_text . begins_with ( color_regions [ j ] . begin_key ) ) {
2017-03-05 15:44:50 +00:00
text_color = color_regions [ j ] . color ;
2014-12-18 03:56:33 +00:00
}
}
2018-12-23 11:19:27 +00:00
int yofs = ( get_row_height ( ) - cache . font - > get_height ( ) ) / 2 ;
2019-06-13 09:32:03 +00:00
Point2 title_pos ( completion_rect . position . x , completion_rect . position . y + i * get_row_height ( ) + cache . font - > get_ascent ( ) + yofs ) ;
2019-08-18 15:56:24 +00:00
// Draw completion icon if it is valid.
2019-06-13 09:32:03 +00:00
Ref < Texture > icon = completion_options [ l ] . icon ;
Rect2 icon_area ( completion_rect . position . x , completion_rect . position . y + i * get_row_height ( ) , icon_area_size . width , icon_area_size . height ) ;
if ( icon . is_valid ( ) ) {
const real_t max_scale = 0.7f ;
const real_t side = max_scale * icon_area . size . width ;
real_t scale = MIN ( side / icon - > get_width ( ) , side / icon - > get_height ( ) ) ;
Size2 icon_size = icon - > get_size ( ) * scale ;
draw_texture_rect ( icon , Rect2 ( icon_area . position + ( icon_area . size - icon_size ) / 2 , icon_size ) ) ;
}
title_pos . x = icon_area . position . x + icon_area . size . width + icon_hsep ;
2019-08-15 15:53:28 +00:00
draw_string ( cache . font , title_pos , completion_options [ l ] . display , text_color , completion_rect . size . width - ( icon_area_size . x + icon_hsep ) ) ;
2014-12-17 01:31:57 +00:00
}
2016-03-08 23:00:52 +00:00
2014-12-17 01:31:57 +00:00
if ( scrollw ) {
2019-08-18 15:56:24 +00:00
// Draw a small scroll rectangle to show a position in the options.
2014-12-17 01:31:57 +00:00
float r = maxlines / ( float ) completion_options . size ( ) ;
float o = line_from / ( float ) completion_options . size ( ) ;
2017-06-03 22:25:13 +00:00
draw_rect ( Rect2 ( completion_rect . position . x + completion_rect . size . width , completion_rect . position . y + o * completion_rect . size . y , scrollw , completion_rect . size . y * r ) , scrollc ) ;
2014-12-17 01:31:57 +00:00
}
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
completion_line_ofs = line_from ;
2014-12-17 01:31:57 +00:00
}
2016-03-04 10:05:42 +00:00
2019-08-18 15:56:24 +00:00
// Check to see if the hint should be drawn.
2016-03-04 10:05:42 +00:00
bool show_hint = false ;
2017-03-05 15:44:50 +00:00
if ( completion_hint ! = " " ) {
2016-03-04 10:05:42 +00:00
if ( completion_active ) {
if ( completion_below & & ! callhint_below ) {
show_hint = true ;
2017-03-05 15:44:50 +00:00
} else if ( ! completion_below & & callhint_below ) {
2016-03-04 10:05:42 +00:00
show_hint = true ;
}
2017-03-05 15:44:50 +00:00
} else {
2016-03-04 10:05:42 +00:00
show_hint = true ;
}
}
2016-03-08 23:00:52 +00:00
2016-03-04 10:05:42 +00:00
if ( show_hint ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
Ref < StyleBox > sb = get_stylebox ( " panel " , " TooltipPanel " ) ;
2014-12-17 01:31:57 +00:00
Ref < Font > font = cache . font ;
2017-03-05 15:44:50 +00:00
Color font_color = get_color ( " font_color " , " TooltipLabel " ) ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
int max_w = 0 ;
2014-12-17 01:31:57 +00:00
int sc = completion_hint . get_slice_count ( " \n " ) ;
2017-03-05 15:44:50 +00:00
int offset = 0 ;
int spacing = 0 ;
for ( int i = 0 ; i < sc ; i + + ) {
String l = completion_hint . get_slice ( " \n " , i ) ;
int len = font - > get_string_size ( l ) . x ;
max_w = MAX ( len , max_w ) ;
if ( i = = 0 ) {
offset = font - > get_string_size ( l . substr ( 0 , l . find ( String : : chr ( 0xFFFF ) ) ) ) . x ;
2014-12-17 01:31:57 +00:00
} else {
2017-03-05 15:44:50 +00:00
spacing + = cache . line_spacing ;
2014-12-17 01:31:57 +00:00
}
}
2016-03-08 23:00:52 +00:00
2019-02-12 20:10:08 +00:00
Size2 size2 = Size2 ( max_w , sc * font - > get_height ( ) + spacing ) ;
Size2 minsize = size2 + sb - > get_minimum_size ( ) ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
if ( completion_hint_offset = = - 0xFFFF ) {
completion_hint_offset = cursor_pos . x - offset ;
2014-12-17 01:31:57 +00:00
}
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
Point2 hint_ofs = Vector2 ( completion_hint_offset , cursor_pos . y ) + callhint_offset ;
2016-03-04 10:05:42 +00:00
if ( callhint_below ) {
hint_ofs . y + = get_row_height ( ) + sb - > get_offset ( ) . y ;
2017-03-05 15:44:50 +00:00
} else {
2016-03-04 10:05:42 +00:00
hint_ofs . y - = minsize . y + sb - > get_offset ( ) . y ;
}
2017-03-05 15:44:50 +00:00
draw_style_box ( sb , Rect2 ( hint_ofs , minsize ) ) ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
spacing = 0 ;
for ( int i = 0 ; i < sc ; i + + ) {
int begin = 0 ;
int end = 0 ;
String l = completion_hint . get_slice ( " \n " , i ) ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
if ( l . find ( String : : chr ( 0xFFFF ) ) ! = - 1 ) {
begin = font - > get_string_size ( l . substr ( 0 , l . find ( String : : chr ( 0xFFFF ) ) ) ) . x ;
end = font - > get_string_size ( l . substr ( 0 , l . rfind ( String : : chr ( 0xFFFF ) ) ) ) . x ;
2014-12-17 01:31:57 +00:00
}
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
draw_string ( font , hint_ofs + sb - > get_offset ( ) + Vector2 ( 0 , font - > get_ascent ( ) + font - > get_height ( ) * i + spacing ) , l . replace ( String : : chr ( 0xFFFF ) , " " ) , font_color ) ;
if ( end > 0 ) {
Vector2 b = hint_ofs + sb - > get_offset ( ) + Vector2 ( begin , font - > get_height ( ) + font - > get_height ( ) * i + spacing - 1 ) ;
draw_line ( b , b + Vector2 ( end - begin , 0 ) , font_color ) ;
2014-12-17 01:31:57 +00:00
}
2017-03-05 15:44:50 +00:00
spacing + = cache . line_spacing ;
2014-12-17 01:31:57 +00:00
}
}
2016-03-08 23:00:52 +00:00
2017-06-25 15:50:45 +00:00
if ( has_focus ( ) ) {
2018-06-07 20:16:57 +00:00
OS : : get_singleton ( ) - > set_ime_active ( true ) ;
2017-06-25 15:50:45 +00:00
OS : : get_singleton ( ) - > set_ime_position ( get_global_position ( ) + cursor_pos + Point2 ( 0 , get_row_height ( ) ) ) ;
}
2014-12-17 01:31:57 +00:00
} break ;
case NOTIFICATION_FOCUS_ENTER : {
2016-03-08 23:00:52 +00:00
2016-06-20 19:29:58 +00:00
if ( ! caret_blink_enabled ) {
draw_caret = true ;
}
2017-06-25 15:50:45 +00:00
2018-06-07 20:16:57 +00:00
OS : : get_singleton ( ) - > set_ime_active ( true ) ;
2017-06-25 15:50:45 +00:00
Point2 cursor_pos = Point2 ( cursor_get_column ( ) , cursor_get_line ( ) ) * get_row_height ( ) ;
OS : : get_singleton ( ) - > set_ime_position ( get_global_position ( ) + cursor_pos ) ;
2014-12-17 01:31:57 +00:00
if ( OS : : get_singleton ( ) - > has_virtual_keyboard ( ) )
2017-03-05 15:44:50 +00:00
OS : : get_singleton ( ) - > show_virtual_keyboard ( get_text ( ) , get_global_rect ( ) ) ;
2014-12-17 01:31:57 +00:00
} break ;
case NOTIFICATION_FOCUS_EXIT : {
2016-03-08 23:00:52 +00:00
2017-06-25 15:50:45 +00:00
OS : : get_singleton ( ) - > set_ime_position ( Point2 ( ) ) ;
2018-06-07 20:16:57 +00:00
OS : : get_singleton ( ) - > set_ime_active ( false ) ;
2017-08-07 11:09:56 +00:00
ime_text = " " ;
ime_selection = Point2 ( ) ;
2017-06-25 15:50:45 +00:00
2014-12-17 01:31:57 +00:00
if ( OS : : get_singleton ( ) - > has_virtual_keyboard ( ) )
OS : : get_singleton ( ) - > hide_virtual_keyboard ( ) ;
} break ;
2018-11-23 12:07:48 +00:00
case MainLoop : : NOTIFICATION_OS_IME_UPDATE : {
2014-02-10 01:10:30 +00:00
2019-01-08 20:52:56 +00:00
if ( has_focus ( ) ) {
ime_text = OS : : get_singleton ( ) - > get_ime_text ( ) ;
ime_selection = OS : : get_singleton ( ) - > get_ime_selection ( ) ;
update ( ) ;
}
2018-11-23 12:07:48 +00:00
} break ;
}
2017-08-07 11:09:56 +00:00
}
2014-04-27 10:34:37 +00:00
void TextEdit : : _consume_pair_symbol ( CharType ch ) {
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
int cursor_position_to_move = cursor_get_column ( ) + 1 ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
CharType ch_single [ 2 ] = { ch , 0 } ;
CharType ch_single_pair [ 2 ] = { _get_right_pair_symbol ( ch ) , 0 } ;
CharType ch_pair [ 3 ] = { ch , _get_right_pair_symbol ( ch ) , 0 } ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
if ( is_selection_active ( ) ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
int new_column , new_line ;
2016-03-08 23:00:52 +00:00
2016-04-06 06:36:29 +00:00
begin_complex_operation ( ) ;
2015-01-02 18:08:40 +00:00
_insert_text ( get_selection_from_line ( ) , get_selection_from_column ( ) ,
2017-03-05 15:44:50 +00:00
ch_single ,
& new_line , & new_column ) ;
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
int to_col_offset = 0 ;
2017-03-05 15:44:50 +00:00
if ( get_selection_from_line ( ) = = get_selection_to_line ( ) )
2015-01-02 18:08:40 +00:00
to_col_offset = 1 ;
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
_insert_text ( get_selection_to_line ( ) ,
2017-03-05 15:44:50 +00:00
get_selection_to_column ( ) + to_col_offset ,
ch_single_pair ,
& new_line , & new_column ) ;
2016-04-06 06:36:29 +00:00
end_complex_operation ( ) ;
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
cursor_set_line ( get_selection_to_line ( ) ) ;
cursor_set_column ( get_selection_to_column ( ) + to_col_offset ) ;
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
deselect ( ) ;
update ( ) ;
return ;
}
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
if ( ( ch = = ' \' ' | | ch = = ' " ' ) & &
2019-01-26 16:35:26 +00:00
cursor_get_column ( ) > 0 & & _is_text_char ( text [ cursor . line ] [ cursor_get_column ( ) - 1 ] ) & & ! _is_pair_right_symbol ( text [ cursor . line ] [ cursor_get_column ( ) ] ) ) {
2015-01-02 18:08:40 +00:00
insert_text_at_cursor ( ch_single ) ;
cursor_set_column ( cursor_position_to_move ) ;
return ;
}
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
if ( cursor_get_column ( ) < text [ cursor . line ] . length ( ) ) {
if ( _is_text_char ( text [ cursor . line ] [ cursor_get_column ( ) ] ) ) {
2015-01-02 18:08:40 +00:00
insert_text_at_cursor ( ch_single ) ;
cursor_set_column ( cursor_position_to_move ) ;
return ;
}
2017-03-05 15:44:50 +00:00
if ( _is_pair_right_symbol ( ch ) & &
text [ cursor . line ] [ cursor_get_column ( ) ] = = ch ) {
2015-01-02 18:08:40 +00:00
cursor_set_column ( cursor_position_to_move ) ;
return ;
}
}
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
insert_text_at_cursor ( ch_pair ) ;
cursor_set_column ( cursor_position_to_move ) ;
2014-04-27 10:34:37 +00:00
}
void TextEdit : : _consume_backspace_for_pair_symbol ( int prev_line , int prev_column ) {
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
bool remove_right_symbol = false ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
if ( cursor . column < text [ cursor . line ] . length ( ) & & cursor . column > 0 ) {
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
CharType left_char = text [ cursor . line ] [ cursor . column - 1 ] ;
CharType right_char = text [ cursor . line ] [ cursor . column ] ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
if ( right_char = = _get_right_pair_symbol ( left_char ) ) {
2015-01-02 18:08:40 +00:00
remove_right_symbol = true ;
}
}
2017-03-05 15:44:50 +00:00
if ( remove_right_symbol ) {
_remove_text ( prev_line , prev_column , cursor . line , cursor . column + 1 ) ;
2015-01-02 18:08:40 +00:00
} else {
2017-03-05 15:44:50 +00:00
_remove_text ( prev_line , prev_column , cursor . line , cursor . column ) ;
2015-01-02 18:08:40 +00:00
}
2014-04-27 10:34:37 +00:00
}
2014-02-10 01:10:30 +00:00
void TextEdit : : backspace_at_cursor ( ) {
2015-01-02 18:08:40 +00:00
if ( readonly )
return ;
2017-03-05 15:44:50 +00:00
if ( cursor . column = = 0 & & cursor . line = = 0 )
2015-01-02 18:08:40 +00:00
return ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
int prev_line = cursor . column ? cursor . line : cursor . line - 1 ;
int prev_column = cursor . column ? ( cursor . column - 1 ) : ( text [ cursor . line - 1 ] . length ( ) ) ;
2017-11-12 23:12:17 +00:00
if ( is_line_hidden ( cursor . line ) )
set_line_as_hidden ( prev_line , true ) ;
2018-05-30 12:02:51 +00:00
if ( is_line_set_as_breakpoint ( cursor . line ) ) {
if ( ! text . is_breakpoint ( prev_line ) )
emit_signal ( " breakpoint_toggled " , prev_line ) ;
2017-11-12 23:12:17 +00:00
set_line_as_breakpoint ( prev_line , true ) ;
2018-05-30 12:02:51 +00:00
}
2017-11-12 23:12:17 +00:00
2019-04-20 11:51:25 +00:00
if ( text . has_info_icon ( cursor . line ) ) {
set_line_info_icon ( prev_line , text . get_info_icon ( cursor . line ) , text . get_info ( cursor . line ) ) ;
}
2017-03-05 15:44:50 +00:00
if ( auto_brace_completion_enabled & &
2015-01-02 18:08:40 +00:00
cursor . column > 0 & &
_is_pair_left_symbol ( text [ cursor . line ] [ cursor . column - 1 ] ) ) {
_consume_backspace_for_pair_symbol ( prev_line , prev_column ) ;
} else {
2019-08-18 15:56:24 +00:00
// Handle space indentation.
2019-05-08 21:56:00 +00:00
if ( cursor . column ! = 0 & & indent_using_spaces ) {
2019-08-18 15:56:24 +00:00
// Check if there are no other chars before cursor, just indentation.
2017-04-17 13:24:30 +00:00
bool unindent = true ;
int i = 0 ;
while ( i < cursor . column & & i < text [ cursor . line ] . length ( ) ) {
2019-05-08 21:56:00 +00:00
if ( ! _is_whitespace ( text [ cursor . line ] [ i ] ) ) {
2017-04-17 13:24:30 +00:00
unindent = false ;
break ;
}
i + + ;
}
2019-08-18 15:56:24 +00:00
// Then we can remove all spaces as a single character.
2017-04-17 13:24:30 +00:00
if ( unindent ) {
2019-08-18 15:56:24 +00:00
// We want to remove spaces up to closest indent, or whole indent if cursor is pointing at it.
2019-05-08 21:56:00 +00:00
int spaces_to_delete = _calculate_spaces_till_next_left_indent ( cursor . column ) ;
prev_column = cursor . column - spaces_to_delete ;
_remove_text ( cursor . line , prev_column , cursor . line , cursor . column ) ;
2017-04-17 13:24:30 +00:00
} else {
_remove_text ( prev_line , prev_column , cursor . line , cursor . column ) ;
}
} else {
_remove_text ( prev_line , prev_column , cursor . line , cursor . column ) ;
}
2015-01-02 18:08:40 +00:00
}
2016-03-08 23:00:52 +00:00
2017-11-12 23:12:17 +00:00
cursor_set_line ( prev_line , true , true ) ;
2015-01-02 18:08:40 +00:00
cursor_set_column ( prev_column ) ;
2014-02-10 01:10:30 +00:00
}
2017-12-14 09:10:53 +00:00
void TextEdit : : indent_right ( ) {
2016-04-06 15:37:03 +00:00
2017-12-14 09:10:53 +00:00
int start_line ;
int end_line ;
2019-05-08 21:56:00 +00:00
2019-08-18 15:56:24 +00:00
// This value informs us by how much we changed selection position by indenting right.
// Default is 1 for tab indentation.
2019-05-08 21:56:00 +00:00
int selection_offset = 1 ;
2016-04-06 15:37:03 +00:00
begin_complex_operation ( ) ;
2017-12-14 09:10:53 +00:00
if ( is_selection_active ( ) ) {
start_line = get_selection_from_line ( ) ;
end_line = get_selection_to_line ( ) ;
} else {
start_line = cursor . line ;
end_line = start_line ;
}
2016-04-06 15:37:03 +00:00
2019-08-18 15:56:24 +00:00
// Ignore if the cursor is not past the first column.
2017-12-14 09:10:53 +00:00
if ( is_selection_active ( ) & & get_selection_to_column ( ) = = 0 ) {
2016-04-06 15:37:03 +00:00
end_line - - ;
}
for ( int i = start_line ; i < = end_line ; i + + ) {
String line_text = get_line ( i ) ;
2017-04-17 13:24:30 +00:00
if ( indent_using_spaces ) {
2019-08-18 15:56:24 +00:00
// We don't really care where selection is - we just need to know indentation level at the beginning of the line.
2019-05-08 21:56:00 +00:00
int left = _find_first_non_whitespace_column_of_line ( line_text ) ;
int spaces_to_add = _calculate_spaces_till_next_right_indent ( left ) ;
2019-08-18 15:56:24 +00:00
// Since we will add this much spaces we want move whole selection and cursor by this much.
2019-05-08 21:56:00 +00:00
selection_offset = spaces_to_add ;
for ( int j = 0 ; j < spaces_to_add ; j + + )
line_text = ' ' + line_text ;
2017-04-17 13:24:30 +00:00
} else {
line_text = ' \t ' + line_text ;
}
2016-04-06 15:37:03 +00:00
set_line ( i , line_text ) ;
}
2019-08-18 15:56:24 +00:00
// Fix selection and cursor being off after shifting selection right.
2017-12-14 09:10:53 +00:00
if ( is_selection_active ( ) ) {
2019-05-08 21:56:00 +00:00
select ( selection . from_line , selection . from_column + selection_offset , selection . to_line , selection . to_column + selection_offset ) ;
2017-12-14 09:10:53 +00:00
}
2019-05-08 21:56:00 +00:00
cursor_set_column ( cursor . column + selection_offset , false ) ;
2016-04-06 15:37:03 +00:00
end_complex_operation ( ) ;
update ( ) ;
}
2017-12-14 09:10:53 +00:00
void TextEdit : : indent_left ( ) {
2016-04-06 15:37:03 +00:00
2017-12-14 09:10:53 +00:00
int start_line ;
int end_line ;
2019-05-08 21:56:00 +00:00
2019-08-18 15:56:24 +00:00
// Moving cursor and selection after unindenting can get tricky because
// changing content of line can move cursor and selection on it's own (if new line ends before previous position of either),
// therefore we just remember initial values and at the end of the operation offset them by number of removed characters.
2019-05-08 21:56:00 +00:00
int removed_characters = 0 ;
int initial_selection_end_column = selection . to_column ;
int initial_cursor_column = cursor . column ;
2016-04-06 15:37:03 +00:00
begin_complex_operation ( ) ;
2017-12-14 09:10:53 +00:00
if ( is_selection_active ( ) ) {
start_line = get_selection_from_line ( ) ;
end_line = get_selection_to_line ( ) ;
} else {
start_line = cursor . line ;
end_line = start_line ;
}
2016-04-06 15:37:03 +00:00
2019-08-18 15:56:24 +00:00
// Ignore if the cursor is not past the first column.
2017-12-14 09:10:53 +00:00
if ( is_selection_active ( ) & & get_selection_to_column ( ) = = 0 ) {
2016-04-06 15:37:03 +00:00
end_line - - ;
}
String last_line_text = get_line ( end_line ) ;
for ( int i = start_line ; i < = end_line ; i + + ) {
String line_text = get_line ( i ) ;
if ( line_text . begins_with ( " \t " ) ) {
line_text = line_text . substr ( 1 , line_text . length ( ) ) ;
set_line ( i , line_text ) ;
2019-05-08 21:56:00 +00:00
removed_characters = 1 ;
} else if ( line_text . begins_with ( " " ) ) {
2019-08-18 15:56:24 +00:00
// When unindenting we aim to remove spaces before line that has selection no matter what is selected,
2019-05-08 21:56:00 +00:00
// so we start of by finding first non whitespace character of line
int left = _find_first_non_whitespace_column_of_line ( line_text ) ;
2019-08-18 15:56:24 +00:00
// Here we remove only enough spaces to align text to nearest full multiple of indentation_size.
// In case where selection begins at the start of indentation_size multiple we remove whole indentation level.
2019-05-08 21:56:00 +00:00
int spaces_to_remove = _calculate_spaces_till_next_left_indent ( left ) ;
line_text = line_text . substr ( spaces_to_remove , line_text . length ( ) ) ;
2016-04-06 15:37:03 +00:00
set_line ( i , line_text ) ;
2019-05-08 21:56:00 +00:00
removed_characters = spaces_to_remove ;
2016-04-06 15:37:03 +00:00
}
}
2019-08-18 15:56:24 +00:00
// Fix selection and cursor being off by one on the last line.
2017-12-14 09:10:53 +00:00
if ( is_selection_active ( ) & & last_line_text ! = get_line ( end_line ) ) {
2019-05-08 21:56:00 +00:00
select ( selection . from_line , selection . from_column - removed_characters ,
selection . to_line , initial_selection_end_column - removed_characters ) ;
2016-04-06 15:37:03 +00:00
}
2019-05-08 21:56:00 +00:00
cursor_set_column ( initial_cursor_column - removed_characters , false ) ;
2016-04-06 15:37:03 +00:00
end_complex_operation ( ) ;
update ( ) ;
}
2014-02-10 01:10:30 +00:00
2019-05-08 21:56:00 +00:00
int TextEdit : : _calculate_spaces_till_next_left_indent ( int column ) {
int spaces_till_indent = column % indent_size ;
if ( spaces_till_indent = = 0 )
spaces_till_indent = indent_size ;
return spaces_till_indent ;
}
int TextEdit : : _calculate_spaces_till_next_right_indent ( int column ) {
return indent_size - column % indent_size ;
}
2017-03-05 15:44:50 +00:00
void TextEdit : : _get_mouse_pos ( const Point2i & p_mouse , int & r_row , int & r_col ) const {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
float rows = p_mouse . y ;
2018-01-18 14:52:51 +00:00
rows - = cache . style_normal - > get_margin ( MARGIN_TOP ) ;
2017-03-05 15:44:50 +00:00
rows / = get_row_height ( ) ;
2018-01-26 01:41:17 +00:00
rows + = get_v_scroll_offset ( ) ;
int first_vis_line = get_first_visible_line ( ) ;
2018-01-18 14:52:51 +00:00
int row = first_vis_line + Math : : floor ( rows ) ;
2018-01-26 01:41:17 +00:00
int wrap_index = 0 ;
if ( is_wrap_enabled ( ) | | is_hiding_enabled ( ) ) {
2015-11-07 12:39:03 +00:00
2018-06-14 07:10:30 +00:00
int f_ofs = num_lines_from_rows ( first_vis_line , cursor . wrap_ofs , rows + ( 1 * SGN ( rows ) ) , wrap_index ) - 1 ;
if ( rows < 0 )
row = first_vis_line - f_ofs ;
else
row = first_vis_line + f_ofs ;
2017-11-12 23:12:17 +00:00
}
2017-03-05 15:44:50 +00:00
if ( row < 0 )
2019-08-18 15:56:24 +00:00
row = 0 ; // TODO.
2015-11-07 12:39:03 +00:00
2017-03-05 15:44:50 +00:00
int col = 0 ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
if ( row > = text . size ( ) ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
row = text . size ( ) - 1 ;
col = text [ row ] . size ( ) ;
2015-01-02 18:08:40 +00:00
} else {
2016-03-08 23:00:52 +00:00
2019-04-20 11:51:25 +00:00
int colx = p_mouse . x - ( cache . style_normal - > get_margin ( MARGIN_LEFT ) + cache . line_number_w + cache . breakpoint_gutter_width + cache . fold_gutter_width + cache . info_gutter_width ) ;
2018-01-26 01:41:17 +00:00
colx + = cursor . x_ofs ;
col = get_char_pos_for_line ( colx , row , wrap_index ) ;
if ( is_wrap_enabled ( ) & & wrap_index < times_line_wraps ( row ) ) {
2019-08-18 15:56:24 +00:00
// Move back one if we are at the end of the row.
2019-02-12 20:10:08 +00:00
Vector < String > rows2 = get_wrap_rows_text ( row ) ;
2018-01-26 01:41:17 +00:00
int row_end_col = 0 ;
for ( int i = 0 ; i < wrap_index + 1 ; i + + ) {
2019-02-12 20:10:08 +00:00
row_end_col + = rows2 [ i ] . length ( ) ;
2018-01-26 01:41:17 +00:00
}
if ( col > = row_end_col )
col - = 1 ;
}
2015-01-02 18:08:40 +00:00
}
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
r_row = row ;
r_col = col ;
2014-02-10 01:10:30 +00:00
}
2019-08-11 18:31:19 +00:00
void TextEdit : : _get_minimap_mouse_row ( const Point2i & p_mouse , int & r_row ) const {
float rows = p_mouse . y ;
rows - = cache . style_normal - > get_margin ( MARGIN_TOP ) ;
rows / = ( minimap_char_size . y + minimap_line_spacing ) ;
rows + = get_v_scroll_offset ( ) ;
// calculate visible lines
int minimap_visible_lines = _get_minimap_visible_rows ( ) ;
int visible_rows = get_visible_rows ( ) + 1 ;
int first_visible_line = get_first_visible_line ( ) - 1 ;
int draw_amount = visible_rows + ( smooth_scroll_enabled ? 1 : 0 ) ;
draw_amount + = times_line_wraps ( first_visible_line + 1 ) ;
int minimap_line_height = ( minimap_char_size . y + minimap_line_spacing ) ;
// calculate viewport size and y offset
int viewport_height = ( draw_amount - 1 ) * minimap_line_height ;
2019-08-25 13:49:13 +00:00
int control_height = _get_control_height ( ) - viewport_height ;
2019-08-11 18:31:19 +00:00
int viewport_offset_y = round ( get_scroll_pos_for_line ( first_visible_line ) * control_height ) / ( ( v_scroll - > get_max ( ) < = minimap_visible_lines ) ? ( minimap_visible_lines - draw_amount ) : ( v_scroll - > get_max ( ) - draw_amount ) ) ;
// calculate the first line.
int num_lines_before = round ( ( viewport_offset_y ) / minimap_line_height ) ;
int wi ;
int minimap_line = ( v_scroll - > get_max ( ) < = minimap_visible_lines ) ? - 1 : first_visible_line ;
2019-08-22 19:07:16 +00:00
if ( first_visible_line > 0 & & minimap_line > = 0 ) {
2019-08-11 18:31:19 +00:00
minimap_line - = num_lines_from_rows ( first_visible_line , 0 , - num_lines_before , wi ) ;
minimap_line - = ( smooth_scroll_enabled ? 1 : 0 ) ;
} else {
minimap_line = 0 ;
}
int row = minimap_line + Math : : floor ( rows ) ;
int wrap_index = 0 ;
if ( is_wrap_enabled ( ) | | is_hiding_enabled ( ) ) {
int f_ofs = num_lines_from_rows ( minimap_line , cursor . wrap_ofs , rows + ( 1 * SGN ( rows ) ) , wrap_index ) - 1 ;
if ( rows < 0 ) {
row = minimap_line - f_ofs ;
} else {
row = minimap_line + f_ofs ;
}
}
if ( row < 0 ) {
row = 0 ;
}
if ( row > = text . size ( ) ) {
row = text . size ( ) - 1 ;
}
r_row = row ;
}
2017-05-20 15:38:03 +00:00
void TextEdit : : _gui_input ( const Ref < InputEvent > & p_gui_input ) {
2016-03-08 23:00:52 +00:00
2019-05-21 16:53:29 +00:00
double prev_v_scroll = v_scroll - > get_value ( ) ;
double prev_h_scroll = h_scroll - > get_value ( ) ;
2017-05-20 15:38:03 +00:00
Ref < InputEventMouseButton > mb = p_gui_input ;
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
if ( mb . is_valid ( ) ) {
2017-06-03 08:54:24 +00:00
if ( completion_active & & completion_rect . has_point ( mb - > get_position ( ) ) ) {
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
if ( ! mb - > is_pressed ( ) )
return ;
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
if ( mb - > get_button_index ( ) = = BUTTON_WHEEL_UP ) {
if ( completion_index > 0 ) {
completion_index - - ;
completion_current = completion_options [ completion_index ] ;
update ( ) ;
2015-01-02 18:08:40 +00:00
}
2017-05-20 15:38:03 +00:00
}
if ( mb - > get_button_index ( ) = = BUTTON_WHEEL_DOWN ) {
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
if ( completion_index < completion_options . size ( ) - 1 ) {
completion_index + + ;
completion_current = completion_options [ completion_index ] ;
update ( ) ;
2015-01-02 18:08:40 +00:00
}
2017-05-20 15:38:03 +00:00
}
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
if ( mb - > get_button_index ( ) = = BUTTON_LEFT ) {
2016-03-08 23:00:52 +00:00
2017-06-03 22:25:13 +00:00
completion_index = CLAMP ( completion_line_ofs + ( mb - > get_position ( ) . y - completion_rect . position . y ) / get_row_height ( ) , 0 , completion_options . size ( ) - 1 ) ;
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
completion_current = completion_options [ completion_index ] ;
update ( ) ;
if ( mb - > is_doubleclick ( ) )
_confirm_completion ( ) ;
2015-01-02 18:08:40 +00:00
}
2017-05-20 15:38:03 +00:00
return ;
} else {
_cancel_completion ( ) ;
_cancel_code_hint ( ) ;
}
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
if ( mb - > is_pressed ( ) ) {
2017-02-21 22:45:31 +00:00
2017-05-20 15:38:03 +00:00
if ( mb - > get_button_index ( ) = = BUTTON_WHEEL_UP & & ! mb - > get_command ( ) ) {
2018-01-03 22:21:54 +00:00
if ( mb - > get_shift ( ) ) {
h_scroll - > set_value ( h_scroll - > get_value ( ) - ( 100 * mb - > get_factor ( ) ) ) ;
} else {
_scroll_up ( 3 * mb - > get_factor ( ) ) ;
}
2017-05-20 15:38:03 +00:00
}
if ( mb - > get_button_index ( ) = = BUTTON_WHEEL_DOWN & & ! mb - > get_command ( ) ) {
2018-01-03 22:21:54 +00:00
if ( mb - > get_shift ( ) ) {
h_scroll - > set_value ( h_scroll - > get_value ( ) + ( 100 * mb - > get_factor ( ) ) ) ;
} else {
_scroll_down ( 3 * mb - > get_factor ( ) ) ;
}
2017-05-20 15:38:03 +00:00
}
if ( mb - > get_button_index ( ) = = BUTTON_WHEEL_LEFT ) {
h_scroll - > set_value ( h_scroll - > get_value ( ) - ( 100 * mb - > get_factor ( ) ) ) ;
}
if ( mb - > get_button_index ( ) = = BUTTON_WHEEL_RIGHT ) {
h_scroll - > set_value ( h_scroll - > get_value ( ) + ( 100 * mb - > get_factor ( ) ) ) ;
}
if ( mb - > get_button_index ( ) = = BUTTON_LEFT ) {
2016-09-12 13:52:29 +00:00
2017-05-20 15:38:03 +00:00
_reset_caret_blink_timer ( ) ;
int row , col ;
2017-06-03 08:54:24 +00:00
_get_mouse_pos ( Point2i ( mb - > get_position ( ) . x , mb - > get_position ( ) . y ) , row , col ) ;
2016-05-09 18:21:55 +00:00
2017-05-20 15:38:03 +00:00
if ( mb - > get_command ( ) & & highlighted_word ! = String ( ) ) {
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
emit_signal ( " symbol_lookup " , highlighted_word , row , col ) ;
return ;
}
2016-09-12 13:52:29 +00:00
2019-08-18 15:56:24 +00:00
// Toggle breakpoint on gutter click.
2017-05-20 15:38:03 +00:00
if ( draw_breakpoint_gutter ) {
int gutter = cache . style_normal - > get_margin ( MARGIN_LEFT ) ;
2019-04-20 11:51:25 +00:00
if ( mb - > get_position ( ) . x > gutter - 6 & & mb - > get_position ( ) . x < = gutter + cache . breakpoint_gutter_width - 3 ) {
2017-05-20 15:38:03 +00:00
set_line_as_breakpoint ( row , ! is_line_set_as_breakpoint ( row ) ) ;
emit_signal ( " breakpoint_toggled " , row ) ;
2016-09-12 13:52:29 +00:00
return ;
}
2017-05-20 15:38:03 +00:00
}
2016-09-12 13:52:29 +00:00
2019-08-18 15:56:24 +00:00
// Emit info clicked.
2019-04-20 11:51:25 +00:00
if ( draw_info_gutter & & text . has_info_icon ( row ) ) {
int left_margin = cache . style_normal - > get_margin ( MARGIN_LEFT ) ;
int gutter_left = left_margin + cache . breakpoint_gutter_width ;
if ( mb - > get_position ( ) . x > gutter_left - 6 & & mb - > get_position ( ) . x < = gutter_left + cache . info_gutter_width - 3 ) {
emit_signal ( " info_clicked " , row , text . get_info ( row ) ) ;
return ;
}
}
2019-08-18 15:56:24 +00:00
// Toggle fold on gutter click if can.
2017-11-12 23:12:17 +00:00
if ( draw_fold_gutter ) {
int left_margin = cache . style_normal - > get_margin ( MARGIN_LEFT ) ;
2019-04-20 11:51:25 +00:00
int gutter_left = left_margin + cache . breakpoint_gutter_width + cache . line_number_w + cache . info_gutter_width ;
2017-11-16 04:00:27 +00:00
if ( mb - > get_position ( ) . x > gutter_left - 6 & & mb - > get_position ( ) . x < = gutter_left + cache . fold_gutter_width - 3 ) {
2017-11-12 23:12:17 +00:00
if ( is_folded ( row ) ) {
unfold_line ( row ) ;
} else if ( can_fold ( row ) ) {
fold_line ( row ) ;
}
2017-11-16 04:00:27 +00:00
return ;
}
}
2019-08-18 15:56:24 +00:00
// Unfold on folded icon click.
2017-11-16 04:00:27 +00:00
if ( is_folded ( row ) ) {
int line_width = text . get_line_width ( row ) ;
2019-04-20 11:51:25 +00:00
line_width + = cache . style_normal - > get_margin ( MARGIN_LEFT ) + cache . line_number_w + cache . breakpoint_gutter_width + cache . info_gutter_width + cache . fold_gutter_width - cursor . x_ofs ;
2017-11-16 04:00:27 +00:00
if ( mb - > get_position ( ) . x > line_width - 3 & & mb - > get_position ( ) . x < = line_width + cache . folded_eol_icon - > get_width ( ) + 3 ) {
unfold_line ( row ) ;
2017-11-12 23:12:17 +00:00
return ;
}
}
2019-08-11 18:31:19 +00:00
// minimap
if ( draw_minimap ) {
2019-08-25 13:38:14 +00:00
_update_minimap_click ( ) ;
2019-08-11 18:31:19 +00:00
if ( dragging_minimap ) {
return ;
}
}
2017-05-20 15:38:03 +00:00
int prev_col = cursor . column ;
int prev_line = cursor . line ;
2016-05-26 13:17:14 +00:00
2017-11-12 23:12:17 +00:00
cursor_set_line ( row , true , false ) ;
2017-05-20 15:38:03 +00:00
cursor_set_column ( col ) ;
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
if ( mb - > get_shift ( ) & & ( cursor . column ! = prev_col | | cursor . line ! = prev_line ) ) {
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
if ( ! selection . active ) {
selection . active = true ;
selection . selecting_mode = Selection : : MODE_POINTER ;
selection . from_column = prev_col ;
selection . from_line = prev_line ;
selection . to_column = cursor . column ;
selection . to_line = cursor . line ;
if ( selection . from_line > selection . to_line | | ( selection . from_line = = selection . to_line & & selection . from_column > selection . to_column ) ) {
SWAP ( selection . from_column , selection . to_column ) ;
SWAP ( selection . from_line , selection . to_line ) ;
selection . shiftclick_left = false ;
} else {
selection . shiftclick_left = true ;
}
selection . selecting_line = prev_line ;
selection . selecting_column = prev_col ;
update ( ) ;
} else {
2015-01-02 18:08:40 +00:00
2017-05-20 15:38:03 +00:00
if ( cursor . line < selection . selecting_line | | ( cursor . line = = selection . selecting_line & & cursor . column < selection . selecting_column ) ) {
2017-03-05 15:44:50 +00:00
2017-05-20 15:38:03 +00:00
if ( selection . shiftclick_left ) {
2017-03-05 15:44:50 +00:00
SWAP ( selection . from_column , selection . to_column ) ;
SWAP ( selection . from_line , selection . to_line ) ;
2017-05-20 15:38:03 +00:00
selection . shiftclick_left = ! selection . shiftclick_left ;
2015-01-02 18:08:40 +00:00
}
2017-05-20 15:38:03 +00:00
selection . from_column = cursor . column ;
selection . from_line = cursor . line ;
2015-08-12 22:34:07 +00:00
2017-05-20 15:38:03 +00:00
} else if ( cursor . line > selection . selecting_line | | ( cursor . line = = selection . selecting_line & & cursor . column > selection . selecting_column ) ) {
2014-02-10 01:10:30 +00:00
2017-05-20 15:38:03 +00:00
if ( ! selection . shiftclick_left ) {
SWAP ( selection . from_column , selection . to_column ) ;
SWAP ( selection . from_line , selection . to_line ) ;
selection . shiftclick_left = ! selection . shiftclick_left ;
2015-01-02 18:08:40 +00:00
}
2017-05-20 15:38:03 +00:00
selection . to_column = cursor . column ;
selection . to_line = cursor . line ;
2015-08-12 22:34:07 +00:00
2017-05-20 15:38:03 +00:00
} else {
selection . active = false ;
2015-01-02 18:08:40 +00:00
}
2014-02-10 01:10:30 +00:00
2017-05-20 15:38:03 +00:00
update ( ) ;
2015-01-02 18:08:40 +00:00
}
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
} else {
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
selection . active = false ;
selection . selecting_mode = Selection : : MODE_POINTER ;
selection . selecting_line = row ;
selection . selecting_column = col ;
}
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
if ( ! mb - > is_doubleclick ( ) & & ( OS : : get_singleton ( ) - > get_ticks_msec ( ) - last_dblclk ) < 600 & & cursor . line = = prev_line ) {
2019-08-18 15:56:24 +00:00
// Triple-click select line.
2017-11-05 15:54:00 +00:00
selection . selecting_mode = Selection : : MODE_LINE ;
_update_selection_mode_line ( ) ;
2017-05-20 15:38:03 +00:00
last_dblclk = 0 ;
} else if ( mb - > is_doubleclick ( ) & & text [ cursor . line ] . length ( ) ) {
2016-03-08 23:00:52 +00:00
2019-08-18 15:56:24 +00:00
// Double-click select word.
2017-11-05 15:54:00 +00:00
selection . selecting_mode = Selection : : MODE_WORD ;
_update_selection_mode_word ( ) ;
2017-05-20 15:38:03 +00:00
last_dblclk = OS : : get_singleton ( ) - > get_ticks_msec ( ) ;
2016-05-16 23:25:17 +00:00
}
2015-12-09 18:56:41 +00:00
2017-05-20 15:38:03 +00:00
update ( ) ;
}
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
if ( mb - > get_button_index ( ) = = BUTTON_RIGHT & & context_menu_enabled ) {
2017-12-20 01:36:47 +00:00
_reset_caret_blink_timer ( ) ;
int row , col ;
_get_mouse_pos ( Point2i ( mb - > get_position ( ) . x , mb - > get_position ( ) . y ) , row , col ) ;
if ( is_right_click_moving_caret ( ) ) {
if ( is_selection_active ( ) ) {
int from_line = get_selection_from_line ( ) ;
int to_line = get_selection_to_line ( ) ;
int from_column = get_selection_from_column ( ) ;
int to_column = get_selection_to_column ( ) ;
if ( row < from_line | | row > to_line | | ( row = = from_line & & col < from_column ) | | ( row = = to_line & & col > to_column ) ) {
2019-08-18 15:56:24 +00:00
// Right click is outside the selected text.
2017-12-20 01:36:47 +00:00
deselect ( ) ;
}
}
if ( ! is_selection_active ( ) ) {
cursor_set_line ( row , true , false ) ;
cursor_set_column ( col ) ;
}
}
2017-09-10 13:37:49 +00:00
menu - > set_position ( get_global_transform ( ) . xform ( get_local_mouse_position ( ) ) ) ;
2017-05-20 15:38:03 +00:00
menu - > set_size ( Vector2 ( 1 , 1 ) ) ;
2019-04-23 01:28:38 +00:00
menu - > set_scale ( get_global_transform ( ) . get_scale ( ) ) ;
2017-05-20 15:38:03 +00:00
menu - > popup ( ) ;
grab_focus ( ) ;
2015-01-02 18:08:40 +00:00
}
2017-05-20 15:38:03 +00:00
} else {
2016-03-08 23:00:52 +00:00
2019-08-11 18:31:19 +00:00
if ( mb - > get_button_index ( ) = = BUTTON_LEFT ) {
dragging_minimap = false ;
dragging_selection = false ;
2019-08-25 13:38:14 +00:00
can_drag_minimap = false ;
2017-05-20 15:38:03 +00:00
click_select_held - > stop ( ) ;
2019-08-11 18:31:19 +00:00
}
2016-03-08 23:00:52 +00:00
2019-08-18 15:56:24 +00:00
// Notify to show soft keyboard.
2017-05-20 15:38:03 +00:00
notification ( NOTIFICATION_FOCUS_ENTER ) ;
}
}
2016-03-08 23:00:52 +00:00
2017-11-01 20:49:39 +00:00
const Ref < InputEventPanGesture > pan_gesture = p_gui_input ;
if ( pan_gesture . is_valid ( ) ) {
const real_t delta = pan_gesture - > get_delta ( ) . y ;
if ( delta < 0 ) {
_scroll_up ( - delta ) ;
} else {
_scroll_down ( delta ) ;
}
h_scroll - > set_value ( h_scroll - > get_value ( ) + pan_gesture - > get_delta ( ) . x * 100 ) ;
2019-05-21 16:53:29 +00:00
if ( v_scroll - > get_value ( ) ! = prev_v_scroll | | h_scroll - > get_value ( ) ! = prev_h_scroll )
2019-08-18 15:56:24 +00:00
accept_event ( ) ; // Accept event if scroll changed.
2019-05-21 16:53:29 +00:00
2017-11-01 20:49:39 +00:00
return ;
}
2017-05-20 15:38:03 +00:00
Ref < InputEventMouseMotion > mm = p_gui_input ;
2016-09-12 13:52:29 +00:00
2017-05-20 15:38:03 +00:00
if ( mm . is_valid ( ) ) {
if ( select_identifiers_enabled ) {
if ( mm - > get_command ( ) & & mm - > get_button_mask ( ) = = 0 ) {
2017-06-03 08:54:24 +00:00
String new_word = get_word_at_pos ( mm - > get_position ( ) ) ;
2017-05-20 15:38:03 +00:00
if ( new_word ! = highlighted_word ) {
highlighted_word = new_word ;
update ( ) ;
}
} else {
if ( highlighted_word ! = String ( ) ) {
highlighted_word = String ( ) ;
update ( ) ;
2016-09-12 13:52:29 +00:00
}
}
2017-05-20 15:38:03 +00:00
}
2016-09-12 13:52:29 +00:00
2019-08-18 15:56:24 +00:00
if ( mm - > get_button_mask ( ) & BUTTON_MASK_LEFT & & get_viewport ( ) - > gui_get_drag_data ( ) = = Variant ( ) ) { // Ignore if dragging.
2017-11-05 15:54:00 +00:00
_reset_caret_blink_timer ( ) ;
2016-03-08 23:00:52 +00:00
2019-08-11 18:31:19 +00:00
if ( draw_minimap & & ! dragging_selection ) {
2019-08-25 13:38:14 +00:00
_update_minimap_drag ( ) ;
2019-08-11 18:31:19 +00:00
}
if ( ! dragging_minimap ) {
switch ( selection . selecting_mode ) {
case Selection : : MODE_POINTER : {
_update_selection_mode_pointer ( ) ;
} break ;
case Selection : : MODE_WORD : {
_update_selection_mode_word ( ) ;
} break ;
case Selection : : MODE_LINE : {
_update_selection_mode_line ( ) ;
} break ;
default : {
break ;
}
2017-11-05 15:54:00 +00:00
}
2015-01-02 18:08:40 +00:00
}
2017-05-20 15:38:03 +00:00
}
}
2016-03-08 23:00:52 +00:00
2019-05-21 16:53:29 +00:00
if ( v_scroll - > get_value ( ) ! = prev_v_scroll | | h_scroll - > get_value ( ) ! = prev_h_scroll )
2019-08-18 15:56:24 +00:00
accept_event ( ) ; // Accept event if scroll changed.
2019-05-21 16:53:29 +00:00
2017-05-20 15:38:03 +00:00
Ref < InputEventKey > k = p_gui_input ;
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
if ( k . is_valid ( ) ) {
2016-03-08 23:00:52 +00:00
2019-08-18 15:56:24 +00:00
k = k - > duplicate ( ) ; // It will be modified later on.
2016-09-12 13:52:29 +00:00
# ifdef OSX_ENABLED
2017-05-20 15:38:03 +00:00
if ( k - > get_scancode ( ) = = KEY_META ) {
2016-09-12 13:52:29 +00:00
# else
2017-05-20 15:38:03 +00:00
if ( k - > get_scancode ( ) = = KEY_CONTROL ) {
2016-09-12 13:52:29 +00:00
# endif
2017-05-20 15:38:03 +00:00
if ( select_identifiers_enabled ) {
2016-09-12 13:52:29 +00:00
2017-05-20 15:38:03 +00:00
if ( k - > is_pressed ( ) ) {
2016-09-12 13:52:29 +00:00
2017-09-10 13:37:49 +00:00
highlighted_word = get_word_at_pos ( get_local_mouse_position ( ) ) ;
2017-05-20 15:38:03 +00:00
update ( ) ;
2016-09-12 13:52:29 +00:00
2017-05-20 15:38:03 +00:00
} else {
highlighted_word = String ( ) ;
update ( ) ;
2016-09-12 13:52:29 +00:00
}
}
2017-05-20 15:38:03 +00:00
}
2016-09-12 13:52:29 +00:00
2017-05-20 15:38:03 +00:00
if ( ! k - > is_pressed ( ) )
return ;
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
if ( completion_active ) {
if ( readonly )
return ;
2015-01-05 01:39:21 +00:00
2017-05-20 15:38:03 +00:00
bool valid = true ;
if ( k - > get_command ( ) | | k - > get_metakey ( ) )
valid = false ;
2015-01-05 01:39:21 +00:00
2017-05-20 15:38:03 +00:00
if ( valid ) {
2015-01-05 01:39:21 +00:00
2017-05-20 15:38:03 +00:00
if ( ! k - > get_alt ( ) ) {
if ( k - > get_scancode ( ) = = KEY_UP ) {
2015-01-05 01:39:21 +00:00
2017-05-20 15:38:03 +00:00
if ( completion_index > 0 ) {
completion_index - - ;
2018-03-08 02:47:42 +00:00
} else {
completion_index = completion_options . size ( ) - 1 ;
2015-01-02 18:08:40 +00:00
}
2018-03-08 02:47:42 +00:00
completion_current = completion_options [ completion_index ] ;
update ( ) ;
2017-05-20 15:38:03 +00:00
accept_event ( ) ;
return ;
}
2015-01-05 01:39:21 +00:00
2017-05-20 15:38:03 +00:00
if ( k - > get_scancode ( ) = = KEY_DOWN ) {
2015-01-05 01:39:21 +00:00
2017-05-20 15:38:03 +00:00
if ( completion_index < completion_options . size ( ) - 1 ) {
completion_index + + ;
2018-03-08 02:47:42 +00:00
} else {
completion_index = 0 ;
2015-01-02 18:08:40 +00:00
}
2018-03-08 02:47:42 +00:00
completion_current = completion_options [ completion_index ] ;
update ( ) ;
2017-05-20 15:38:03 +00:00
accept_event ( ) ;
return ;
}
2015-01-05 01:39:21 +00:00
2017-05-20 15:38:03 +00:00
if ( k - > get_scancode ( ) = = KEY_PAGEUP ) {
2015-01-05 01:39:21 +00:00
2017-05-20 15:38:03 +00:00
completion_index - = get_constant ( " completion_lines " ) ;
if ( completion_index < 0 )
2017-03-05 15:44:50 +00:00
completion_index = 0 ;
2017-05-20 15:38:03 +00:00
completion_current = completion_options [ completion_index ] ;
update ( ) ;
accept_event ( ) ;
return ;
}
2015-01-05 01:39:21 +00:00
2017-05-20 15:38:03 +00:00
if ( k - > get_scancode ( ) = = KEY_PAGEDOWN ) {
2015-01-05 01:39:21 +00:00
2017-05-20 15:38:03 +00:00
completion_index + = get_constant ( " completion_lines " ) ;
if ( completion_index > = completion_options . size ( ) )
2017-03-05 15:44:50 +00:00
completion_index = completion_options . size ( ) - 1 ;
2017-05-20 15:38:03 +00:00
completion_current = completion_options [ completion_index ] ;
update ( ) ;
accept_event ( ) ;
return ;
}
2015-01-05 01:39:21 +00:00
2017-05-20 15:38:03 +00:00
if ( k - > get_scancode ( ) = = KEY_HOME & & completion_index > 0 ) {
2015-01-05 01:39:21 +00:00
2017-05-20 15:38:03 +00:00
completion_index = 0 ;
completion_current = completion_options [ completion_index ] ;
update ( ) ;
accept_event ( ) ;
return ;
}
2015-01-05 01:39:21 +00:00
2017-05-20 15:38:03 +00:00
if ( k - > get_scancode ( ) = = KEY_END & & completion_index < completion_options . size ( ) - 1 ) {
2015-01-05 01:39:21 +00:00
2017-05-20 15:38:03 +00:00
completion_index = completion_options . size ( ) - 1 ;
completion_current = completion_options [ completion_index ] ;
update ( ) ;
accept_event ( ) ;
return ;
}
2015-01-05 01:39:21 +00:00
2017-08-06 13:26:07 +00:00
if ( k - > get_scancode ( ) = = KEY_KP_ENTER | | k - > get_scancode ( ) = = KEY_ENTER | | k - > get_scancode ( ) = = KEY_TAB ) {
2017-05-20 15:38:03 +00:00
_confirm_completion ( ) ;
accept_event ( ) ;
return ;
2015-01-02 18:08:40 +00:00
}
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
if ( k - > get_scancode ( ) = = KEY_BACKSPACE ) {
2016-03-08 23:00:52 +00:00
2016-05-09 18:21:55 +00:00
_reset_caret_blink_timer ( ) ;
2017-05-20 15:38:03 +00:00
backspace_at_cursor ( ) ;
_update_completion_candidates ( ) ;
accept_event ( ) ;
return ;
}
2016-04-03 14:21:16 +00:00
2017-05-20 15:38:03 +00:00
if ( k - > get_scancode ( ) = = KEY_SHIFT ) {
accept_event ( ) ;
return ;
}
}
2016-04-03 14:21:16 +00:00
2017-05-20 15:38:03 +00:00
if ( k - > get_unicode ( ) > 32 ) {
2016-04-03 14:21:16 +00:00
2017-05-20 15:38:03 +00:00
_reset_caret_blink_timer ( ) ;
2016-04-03 14:21:16 +00:00
2017-05-20 15:38:03 +00:00
const CharType chr [ 2 ] = { ( CharType ) k - > get_unicode ( ) , 0 } ;
if ( auto_brace_completion_enabled & & _is_pair_symbol ( chr [ 0 ] ) ) {
_consume_pair_symbol ( chr [ 0 ] ) ;
} else {
2019-08-18 15:56:24 +00:00
// Remove the old character if in insert mode.
2017-05-20 15:38:03 +00:00
if ( insert_mode ) {
begin_complex_operation ( ) ;
2019-08-18 15:56:24 +00:00
// Make sure we don't try and remove empty space.
2017-05-20 15:38:03 +00:00
if ( cursor . column < get_line ( cursor . line ) . length ( ) ) {
_remove_text ( cursor . line , cursor . column , cursor . line , cursor . column + 1 ) ;
2015-01-02 18:08:40 +00:00
}
}
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
_insert_text_at_cursor ( chr ) ;
if ( insert_mode ) {
end_complex_operation ( ) ;
}
2015-01-02 18:08:40 +00:00
}
2017-05-20 15:38:03 +00:00
_update_completion_candidates ( ) ;
accept_event ( ) ;
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
return ;
}
2015-01-02 18:08:40 +00:00
}
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
_cancel_completion ( ) ;
}
2016-03-08 23:00:52 +00:00
2019-08-18 15:56:24 +00:00
/* TEST CONTROL FIRST! */
2016-03-08 23:00:52 +00:00
2019-08-18 15:56:24 +00:00
// Some remaps for duplicate functions.
2017-05-20 15:38:03 +00:00
if ( k - > get_command ( ) & & ! k - > get_shift ( ) & & ! k - > get_alt ( ) & & ! k - > get_metakey ( ) & & k - > get_scancode ( ) = = KEY_INSERT ) {
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
k - > set_scancode ( KEY_C ) ;
}
if ( ! k - > get_command ( ) & & k - > get_shift ( ) & & ! k - > get_alt ( ) & & ! k - > get_metakey ( ) & & k - > get_scancode ( ) = = KEY_INSERT ) {
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
k - > set_scancode ( KEY_V ) ;
k - > set_command ( true ) ;
k - > set_shift ( false ) ;
}
2019-05-02 17:57:37 +00:00
# ifdef APPLE_STYLE_KEYS
if ( k - > get_control ( ) & & ! k - > get_shift ( ) & & ! k - > get_alt ( ) & & ! k - > get_command ( ) ) {
2019-05-04 14:05:49 +00:00
uint32_t remap_key = KEY_UNKNOWN ;
2019-05-02 17:57:37 +00:00
switch ( k - > get_scancode ( ) ) {
case KEY_F : {
2019-05-04 14:05:49 +00:00
remap_key = KEY_RIGHT ;
2019-05-02 17:57:37 +00:00
} break ;
case KEY_B : {
2019-05-04 14:05:49 +00:00
remap_key = KEY_LEFT ;
2019-05-02 17:57:37 +00:00
} break ;
case KEY_P : {
2019-05-04 14:05:49 +00:00
remap_key = KEY_UP ;
2019-05-02 17:57:37 +00:00
} break ;
case KEY_N : {
2019-05-04 14:05:49 +00:00
remap_key = KEY_DOWN ;
} break ;
case KEY_D : {
remap_key = KEY_DELETE ;
} break ;
case KEY_H : {
remap_key = KEY_BACKSPACE ;
2019-05-02 17:57:37 +00:00
} break ;
}
2019-05-04 14:05:49 +00:00
if ( remap_key ! = KEY_UNKNOWN ) {
k - > set_scancode ( remap_key ) ;
2019-05-02 17:57:37 +00:00
k - > set_control ( false ) ;
}
}
# endif
2016-09-12 13:52:29 +00:00
2018-08-10 09:23:13 +00:00
_reset_caret_blink_timer ( ) ;
2016-03-08 23:00:52 +00:00
2019-08-18 15:56:24 +00:00
// Save here for insert mode, just in case it is cleared in the following section.
2017-05-20 15:38:03 +00:00
bool had_selection = selection . active ;
2015-01-02 18:08:40 +00:00
2019-08-18 15:56:24 +00:00
// Stuff to do when selection is active.
2019-02-16 06:56:24 +00:00
if ( ! readonly & & selection . active ) {
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
bool clear = false ;
bool unselect = false ;
bool dobreak = false ;
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
switch ( k - > get_scancode ( ) ) {
case KEY_TAB : {
if ( k - > get_shift ( ) ) {
2017-12-14 09:10:53 +00:00
indent_left ( ) ;
2017-05-20 15:38:03 +00:00
} else {
2017-12-14 09:10:53 +00:00
indent_right ( ) ;
2017-05-20 15:38:03 +00:00
}
dobreak = true ;
accept_event ( ) ;
} break ;
case KEY_X :
case KEY_C :
2019-08-18 15:56:24 +00:00
// Special keys often used with control, wait.
2017-05-20 15:38:03 +00:00
clear = ( ! k - > get_command ( ) | | k - > get_shift ( ) | | k - > get_alt ( ) ) ;
break ;
case KEY_DELETE :
if ( ! k - > get_shift ( ) ) {
2015-01-02 18:08:40 +00:00
accept_event ( ) ;
2017-03-05 15:44:50 +00:00
clear = true ;
dobreak = true ;
2017-05-20 15:38:03 +00:00
} else if ( k - > get_command ( ) | | k - > get_alt ( ) ) {
dobreak = true ;
}
break ;
case KEY_BACKSPACE :
accept_event ( ) ;
clear = true ;
dobreak = true ;
break ;
case KEY_LEFT :
case KEY_RIGHT :
case KEY_UP :
case KEY_DOWN :
case KEY_PAGEUP :
case KEY_PAGEDOWN :
case KEY_HOME :
case KEY_END :
2019-08-18 15:56:24 +00:00
// Ignore arrows if any modifiers are held (shift = selecting, others may be used for editor hotkeys).
2017-05-20 15:38:03 +00:00
if ( k - > get_command ( ) | | k - > get_shift ( ) | | k - > get_alt ( ) )
2015-01-02 18:08:40 +00:00
break ;
2017-05-20 15:38:03 +00:00
unselect = true ;
break ;
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
default :
if ( k - > get_unicode ( ) > = 32 & & ! k - > get_command ( ) & & ! k - > get_alt ( ) & & ! k - > get_metakey ( ) )
clear = true ;
if ( auto_brace_completion_enabled & & _is_pair_left_symbol ( k - > get_unicode ( ) ) )
clear = false ;
}
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
if ( unselect ) {
selection . active = false ;
selection . selecting_mode = Selection : : MODE_NONE ;
update ( ) ;
}
if ( clear ) {
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
if ( ! dobreak ) {
begin_complex_operation ( ) ;
2015-01-02 18:08:40 +00:00
}
2017-05-20 15:38:03 +00:00
selection . active = false ;
update ( ) ;
_remove_text ( selection . from_line , selection . from_column , selection . to_line , selection . to_column ) ;
2017-11-12 23:12:17 +00:00
cursor_set_line ( selection . from_line , true , false ) ;
2017-05-20 15:38:03 +00:00
cursor_set_column ( selection . from_column ) ;
update ( ) ;
2015-01-02 18:08:40 +00:00
}
2017-05-20 15:38:03 +00:00
if ( dobreak )
return ;
}
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
selection . selecting_text = false ;
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
bool scancode_handled = true ;
2016-03-08 23:00:52 +00:00
2019-08-18 15:56:24 +00:00
// Special scancode test.
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
switch ( k - > get_scancode ( ) ) {
2016-03-08 23:00:52 +00:00
2017-08-06 13:26:07 +00:00
case KEY_KP_ENTER :
case KEY_ENTER : {
2015-01-02 18:08:40 +00:00
2017-05-20 15:38:03 +00:00
if ( readonly )
break ;
2015-01-02 18:08:40 +00:00
2017-05-20 15:38:03 +00:00
String ins = " \n " ;
2019-08-18 15:56:24 +00:00
// Keep indentation.
2017-05-20 15:38:03 +00:00
int space_count = 0 ;
2017-08-30 18:35:38 +00:00
for ( int i = 0 ; i < cursor . column ; i + + ) {
if ( text [ cursor . line ] [ i ] = = ' \t ' ) {
2017-05-20 15:38:03 +00:00
if ( indent_using_spaces ) {
ins + = space_indent ;
} else {
ins + = " \t " ;
}
space_count = 0 ;
2017-08-30 18:35:38 +00:00
} else if ( text [ cursor . line ] [ i ] = = ' ' ) {
2017-05-20 15:38:03 +00:00
space_count + + ;
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
if ( space_count = = indent_size ) {
2017-04-17 13:24:30 +00:00
if ( indent_using_spaces ) {
ins + = space_indent ;
} else {
ins + = " \t " ;
}
space_count = 0 ;
}
2017-05-20 15:38:03 +00:00
} else {
break ;
2015-01-02 18:08:40 +00:00
}
2017-05-20 15:38:03 +00:00
}
2017-09-02 14:59:30 +00:00
2017-11-12 23:12:17 +00:00
if ( is_folded ( cursor . line ) )
unfold_line ( cursor . line ) ;
2017-09-02 14:59:30 +00:00
bool brace_indent = false ;
2019-08-18 15:56:24 +00:00
// No need to indent if we are going upwards.
2017-09-02 14:59:30 +00:00
if ( auto_indent & & ! ( k - > get_command ( ) & & k - > get_shift ( ) ) ) {
2019-08-18 15:56:24 +00:00
// Indent once again if previous line will end with ':' or '{' and the line is not a comment
// (i.e. colon/brace precedes current cursor position).
2018-10-09 21:36:12 +00:00
if ( cursor . column > 0 & & ( text [ cursor . line ] [ cursor . column - 1 ] = = ' : ' | | text [ cursor . line ] [ cursor . column - 1 ] = = ' { ' ) & & ! is_line_comment ( cursor . line ) ) {
2017-05-20 15:38:03 +00:00
if ( indent_using_spaces ) {
ins + = space_indent ;
} else {
ins + = " \t " ;
2015-11-29 16:02:35 +00:00
}
2017-09-02 14:59:30 +00:00
2019-08-18 15:56:24 +00:00
// No need to move the brace below if we are not taking the text with us.
2017-09-02 14:59:30 +00:00
if ( text [ cursor . line ] [ cursor . column ] = = ' } ' & & ! k - > get_command ( ) ) {
brace_indent = true ;
ins + = " \n " + ins . substr ( 1 , ins . length ( ) - 2 ) ;
}
2015-11-29 16:02:35 +00:00
}
2017-05-20 15:38:03 +00:00
}
2017-10-31 20:42:53 +00:00
begin_complex_operation ( ) ;
2017-05-20 15:38:03 +00:00
bool first_line = false ;
if ( k - > get_command ( ) ) {
if ( k - > get_shift ( ) ) {
if ( cursor . line > 0 ) {
cursor_set_line ( cursor . line - 1 ) ;
2016-03-04 19:10:31 +00:00
cursor_set_column ( text [ cursor . line ] . length ( ) ) ;
2017-05-20 15:38:03 +00:00
} else {
cursor_set_column ( 0 ) ;
first_line = true ;
2016-03-04 19:10:31 +00:00
}
2017-05-20 15:38:03 +00:00
} else {
cursor_set_column ( text [ cursor . line ] . length ( ) ) ;
2016-03-04 19:10:31 +00:00
}
2017-05-20 15:38:03 +00:00
}
2016-03-08 23:00:52 +00:00
2017-10-31 20:42:53 +00:00
insert_text_at_cursor ( ins ) ;
2016-03-04 19:10:31 +00:00
2017-05-20 15:38:03 +00:00
if ( first_line ) {
cursor_set_line ( 0 ) ;
2017-09-02 14:59:30 +00:00
} else if ( brace_indent ) {
cursor_set_line ( cursor . line - 1 ) ;
cursor_set_column ( text [ cursor . line ] . length ( ) ) ;
2017-05-20 15:38:03 +00:00
}
2017-10-31 20:42:53 +00:00
end_complex_operation ( ) ;
2017-05-20 15:38:03 +00:00
} break ;
case KEY_ESCAPE : {
if ( completion_hint ! = " " ) {
completion_hint = " " ;
update ( ) ;
} else {
scancode_handled = false ;
}
} break ;
case KEY_TAB : {
2019-08-18 15:56:24 +00:00
if ( k - > get_command ( ) ) break ; // Avoid tab when command.
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
if ( readonly )
break ;
2016-03-08 23:00:52 +00:00
2017-12-14 09:10:53 +00:00
if ( is_selection_active ( ) ) {
if ( k - > get_shift ( ) ) {
indent_left ( ) ;
} else {
indent_right ( ) ;
}
2017-05-20 15:38:03 +00:00
} else {
if ( k - > get_shift ( ) ) {
2019-08-18 15:56:24 +00:00
// Simple unindent.
2017-05-20 15:38:03 +00:00
int cc = cursor . column ;
2017-12-23 08:28:02 +00:00
const String & line = text [ cursor . line ] ;
2019-05-08 21:56:00 +00:00
int left = _find_first_non_whitespace_column_of_line ( line ) ;
2017-12-23 08:28:02 +00:00
cc = MIN ( cc , left ) ;
while ( cc < indent_size & & cc < left & & line [ cc ] = = ' ' )
cc + + ;
2017-05-20 15:38:03 +00:00
if ( cc > 0 & & cc < = text [ cursor . line ] . length ( ) ) {
2017-12-23 08:28:02 +00:00
if ( text [ cursor . line ] [ cc - 1 ] = = ' \t ' ) {
2019-08-18 15:56:24 +00:00
// Tabs unindentation.
2017-12-23 08:28:02 +00:00
_remove_text ( cursor . line , cc - 1 , cursor . line , cc ) ;
if ( cursor . column > = left )
cursor_set_column ( MAX ( 0 , cursor . column - 1 ) ) ;
update ( ) ;
2017-05-20 15:38:03 +00:00
} else {
2019-08-18 15:56:24 +00:00
// Spaces unindentation.
2019-05-08 21:56:00 +00:00
int spaces_to_remove = _calculate_spaces_till_next_left_indent ( cc ) ;
if ( spaces_to_remove > 0 ) {
_remove_text ( cursor . line , cc - spaces_to_remove , cursor . line , cc ) ;
2019-08-18 15:56:24 +00:00
if ( cursor . column > left - spaces_to_remove ) // Inside text?
2019-05-08 21:56:00 +00:00
cursor_set_column ( MAX ( 0 , cursor . column - spaces_to_remove ) ) ;
2017-12-23 08:28:02 +00:00
update ( ) ;
2017-04-17 13:24:30 +00:00
}
2015-01-02 18:08:40 +00:00
}
2017-12-23 08:28:02 +00:00
} else if ( cc = = 0 & & line . length ( ) > 0 & & line [ 0 ] = = ' \t ' ) {
_remove_text ( cursor . line , 0 , cursor . line , 1 ) ;
update ( ) ;
2017-05-20 15:38:03 +00:00
}
} else {
2019-08-18 15:56:24 +00:00
// Simple indent.
2017-05-20 15:38:03 +00:00
if ( indent_using_spaces ) {
2019-08-18 15:56:24 +00:00
// Insert only as much spaces as needed till next indentation level.
2019-05-08 21:56:00 +00:00
int spaces_to_add = _calculate_spaces_till_next_right_indent ( cursor . column ) ;
String indent_to_insert = String ( ) ;
for ( int i = 0 ; i < spaces_to_add ; i + + )
indent_to_insert = ' ' + indent_to_insert ;
_insert_text_at_cursor ( indent_to_insert ) ;
2015-01-02 18:08:40 +00:00
} else {
2017-05-20 15:38:03 +00:00
_insert_text_at_cursor ( " \t " ) ;
2015-01-02 18:08:40 +00:00
}
}
2017-05-20 15:38:03 +00:00
}
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
} break ;
case KEY_BACKSPACE : {
if ( readonly )
break ;
2015-11-12 16:35:48 +00:00
# ifdef APPLE_STYLE_KEYS
2017-08-01 17:40:43 +00:00
if ( k - > get_alt ( ) & & cursor . column > 1 ) {
2015-11-12 16:35:48 +00:00
# else
2017-05-20 15:38:03 +00:00
if ( k - > get_alt ( ) ) {
scancode_handled = false ;
break ;
2017-08-01 17:40:43 +00:00
} else if ( k - > get_command ( ) & & cursor . column > 1 ) {
2015-11-12 16:35:48 +00:00
# endif
2017-05-20 15:38:03 +00:00
int line = cursor . line ;
int column = cursor . column ;
2015-11-12 16:35:48 +00:00
2019-08-18 15:56:24 +00:00
// Check if we are removing a single whitespace, if so remove it and the next char type,
// else we just remove the whitespace.
2017-08-01 17:40:43 +00:00
bool only_whitespace = false ;
if ( _is_whitespace ( text [ line ] [ column - 1 ] ) & & _is_whitespace ( text [ line ] [ column - 2 ] ) ) {
only_whitespace = true ;
} else if ( _is_whitespace ( text [ line ] [ column - 1 ] ) ) {
2019-08-18 15:56:24 +00:00
// Remove the single whitespace.
2017-08-01 17:40:43 +00:00
column - - ;
}
2015-11-12 16:35:48 +00:00
2019-08-18 15:56:24 +00:00
// Check if its a text char.
2017-08-01 17:40:43 +00:00
bool only_char = ( _is_text_char ( text [ line ] [ column - 1 ] ) & & ! only_whitespace ) ;
2015-11-12 16:35:48 +00:00
2019-08-18 15:56:24 +00:00
// If its not whitespace or char then symbol.
2017-08-01 17:40:43 +00:00
bool only_symbols = ! ( only_whitespace | | only_char ) ;
2015-11-12 16:35:48 +00:00
2017-05-20 15:38:03 +00:00
while ( column > 0 ) {
2017-08-01 17:40:43 +00:00
bool is_whitespace = _is_whitespace ( text [ line ] [ column - 1 ] ) ;
bool is_text_char = _is_text_char ( text [ line ] [ column - 1 ] ) ;
2015-11-12 16:35:48 +00:00
2017-08-01 17:40:43 +00:00
if ( only_whitespace & & ! is_whitespace ) {
2017-05-20 15:38:03 +00:00
break ;
2017-08-01 17:40:43 +00:00
} else if ( only_char & & ! is_text_char ) {
break ;
} else if ( only_symbols & & ( is_whitespace | | is_text_char ) ) {
break ;
}
2017-05-20 15:38:03 +00:00
column - - ;
2016-01-07 20:38:38 +00:00
}
2017-05-20 15:38:03 +00:00
_remove_text ( line , column , cursor . line , cursor . column ) ;
cursor_set_line ( line ) ;
cursor_set_column ( column ) ;
2018-04-24 13:23:04 +00:00
# ifdef APPLE_STYLE_KEYS
} else if ( k - > get_command ( ) ) {
int cursor_current_column = cursor . column ;
cursor . column = 0 ;
_remove_text ( cursor . line , 0 , cursor . line , cursor_current_column ) ;
# endif
2017-05-20 15:38:03 +00:00
} else {
2017-11-12 23:12:17 +00:00
if ( cursor . line > 0 & & is_line_hidden ( cursor . line - 1 ) )
unfold_line ( cursor . line - 1 ) ;
2017-05-20 15:38:03 +00:00
backspace_at_cursor ( ) ;
2016-01-07 20:38:38 +00:00
}
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
} break ;
case KEY_KP_4 : {
if ( k - > get_unicode ( ) ! = 0 ) {
scancode_handled = false ;
break ;
}
2019-04-05 12:06:16 +00:00
FALLTHROUGH ;
2017-05-20 15:38:03 +00:00
}
case KEY_LEFT : {
if ( k - > get_shift ( ) )
_pre_shift_selection ( ) ;
2016-06-23 21:03:32 +00:00
# ifdef APPLE_STYLE_KEYS
2017-05-20 15:38:03 +00:00
else
2016-06-23 21:03:32 +00:00
# else
2017-05-20 15:38:03 +00:00
else if ( ! k - > get_alt ( ) )
2016-06-23 21:03:32 +00:00
# endif
2017-05-20 15:38:03 +00:00
deselect ( ) ;
2016-03-08 23:00:52 +00:00
2014-02-10 01:10:30 +00:00
# ifdef APPLE_STYLE_KEYS
2017-05-20 15:38:03 +00:00
if ( k - > get_command ( ) ) {
2019-04-28 15:51:47 +00:00
// Start at first column (it's slightly faster that way) and look for the first non-whitespace character.
int new_cursor_pos = 0 ;
for ( int i = 0 ; i < text [ cursor . line ] . length ( ) ; + + i ) {
if ( ! _is_whitespace ( text [ cursor . line ] [ i ] ) ) {
new_cursor_pos = i ;
break ;
}
}
if ( new_cursor_pos = = cursor . column ) {
// We're already at the first text character, so move to the very beginning of the line.
cursor_set_column ( 0 ) ;
} else {
// We're somewhere to the right of the first text character; move to the first one.
cursor_set_column ( new_cursor_pos ) ;
}
2017-05-20 15:38:03 +00:00
} else if ( k - > get_alt ( ) ) {
2014-02-10 01:10:30 +00:00
# else
2017-05-20 15:38:03 +00:00
if ( k - > get_alt ( ) ) {
scancode_handled = false ;
break ;
} else if ( k - > get_command ( ) ) {
2014-02-10 01:10:30 +00:00
# endif
2017-05-20 15:38:03 +00:00
int cc = cursor . column ;
2017-08-18 18:53:03 +00:00
if ( cc = = 0 & & cursor . line > 0 ) {
cursor_set_line ( cursor . line - 1 ) ;
cursor_set_column ( text [ cursor . line ] . length ( ) ) ;
2017-12-23 14:47:04 +00:00
} else {
2018-05-14 02:26:45 +00:00
bool prev_char = false ;
2017-12-23 14:47:04 +00:00
while ( cc > 0 ) {
bool ischar = _is_text_char ( text [ cursor . line ] [ cc - 1 ] ) ;
2016-03-08 23:00:52 +00:00
2017-12-23 14:47:04 +00:00
if ( prev_char & & ! ischar )
break ;
2016-03-08 23:00:52 +00:00
2017-12-23 14:47:04 +00:00
prev_char = ischar ;
cc - - ;
}
cursor_set_column ( cc ) ;
2015-01-02 18:08:40 +00:00
}
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
} else if ( cursor . column = = 0 ) {
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
if ( cursor . line > 0 ) {
2017-11-16 04:00:27 +00:00
cursor_set_line ( cursor . line - num_lines_from ( CLAMP ( cursor . line - 1 , 0 , text . size ( ) - 1 ) , - 1 ) ) ;
2017-05-20 15:38:03 +00:00
cursor_set_column ( text [ cursor . line ] . length ( ) ) ;
2016-01-07 20:38:38 +00:00
}
2017-05-20 15:38:03 +00:00
} else {
cursor_set_column ( cursor_get_column ( ) - 1 ) ;
}
if ( k - > get_shift ( ) )
_post_shift_selection ( ) ;
} break ;
case KEY_KP_6 : {
if ( k - > get_unicode ( ) ! = 0 ) {
scancode_handled = false ;
break ;
2016-01-07 20:38:38 +00:00
}
2019-04-05 12:06:16 +00:00
FALLTHROUGH ;
2017-05-20 15:38:03 +00:00
}
case KEY_RIGHT : {
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
if ( k - > get_shift ( ) )
_pre_shift_selection ( ) ;
2016-06-23 21:03:32 +00:00
# ifdef APPLE_STYLE_KEYS
2017-05-20 15:38:03 +00:00
else
2016-06-23 21:03:32 +00:00
# else
2017-05-20 15:38:03 +00:00
else if ( ! k - > get_alt ( ) )
2016-06-23 21:03:32 +00:00
# endif
2017-05-20 15:38:03 +00:00
deselect ( ) ;
2016-03-08 23:00:52 +00:00
2014-02-10 01:10:30 +00:00
# ifdef APPLE_STYLE_KEYS
2017-05-20 15:38:03 +00:00
if ( k - > get_command ( ) ) {
cursor_set_column ( text [ cursor . line ] . length ( ) ) ;
} else if ( k - > get_alt ( ) ) {
2014-02-10 01:10:30 +00:00
# else
2017-05-20 15:38:03 +00:00
if ( k - > get_alt ( ) ) {
scancode_handled = false ;
break ;
} else if ( k - > get_command ( ) ) {
2014-02-10 01:10:30 +00:00
# endif
2017-05-20 15:38:03 +00:00
int cc = cursor . column ;
2017-08-18 18:53:03 +00:00
if ( cc = = text [ cursor . line ] . length ( ) & & cursor . line < text . size ( ) - 1 ) {
cursor_set_line ( cursor . line + 1 ) ;
cursor_set_column ( 0 ) ;
2017-12-23 14:47:04 +00:00
} else {
2018-05-14 02:26:45 +00:00
bool prev_char = false ;
2017-12-23 14:47:04 +00:00
while ( cc < text [ cursor . line ] . length ( ) ) {
bool ischar = _is_text_char ( text [ cursor . line ] [ cc ] ) ;
2016-03-08 23:00:52 +00:00
2017-12-23 14:47:04 +00:00
if ( prev_char & & ! ischar )
break ;
prev_char = ischar ;
cc + + ;
}
cursor_set_column ( cc ) ;
2017-05-20 15:38:03 +00:00
}
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
} else if ( cursor . column = = text [ cursor . line ] . length ( ) ) {
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
if ( cursor . line < text . size ( ) - 1 ) {
2017-11-16 04:00:27 +00:00
cursor_set_line ( cursor_get_line ( ) + num_lines_from ( CLAMP ( cursor . line + 1 , 0 , text . size ( ) - 1 ) , 1 ) , true , false ) ;
2017-05-20 15:38:03 +00:00
cursor_set_column ( 0 ) ;
2015-01-02 18:08:40 +00:00
}
2017-05-20 15:38:03 +00:00
} else {
cursor_set_column ( cursor_get_column ( ) + 1 ) ;
}
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
if ( k - > get_shift ( ) )
_post_shift_selection ( ) ;
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
} break ;
case KEY_KP_8 : {
if ( k - > get_unicode ( ) ! = 0 ) {
scancode_handled = false ;
break ;
2016-01-07 20:38:38 +00:00
}
2019-04-05 12:06:16 +00:00
FALLTHROUGH ;
2017-05-20 15:38:03 +00:00
}
case KEY_UP : {
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
if ( k - > get_alt ( ) ) {
scancode_handled = false ;
break ;
}
2016-04-27 17:32:14 +00:00
# ifndef APPLE_STYLE_KEYS
2017-05-20 15:38:03 +00:00
if ( k - > get_command ( ) ) {
2016-04-27 17:32:14 +00:00
# else
2017-05-20 15:38:03 +00:00
if ( k - > get_command ( ) & & k - > get_alt ( ) ) {
2019-02-04 10:55:45 +00:00
# endif
2017-05-20 15:38:03 +00:00
_scroll_lines_up ( ) ;
break ;
}
2016-04-27 17:32:14 +00:00
2019-02-04 10:55:45 +00:00
if ( k - > get_shift ( ) ) {
_pre_shift_selection ( ) ;
}
# ifdef APPLE_STYLE_KEYS
2018-01-26 01:41:17 +00:00
if ( k - > get_command ( ) ) {
2019-02-04 10:55:45 +00:00
2017-05-20 15:38:03 +00:00
cursor_set_line ( 0 ) ;
2018-01-26 01:41:17 +00:00
} else
2014-02-10 01:10:30 +00:00
# endif
2018-01-26 01:41:17 +00:00
{
int cur_wrap_index = get_cursor_wrap_index ( ) ;
if ( cur_wrap_index > 0 ) {
cursor_set_line ( cursor . line , true , false , cur_wrap_index - 1 ) ;
} else if ( cursor . line = = 0 ) {
cursor_set_column ( 0 ) ;
} else {
int new_line = cursor . line - num_lines_from ( cursor . line - 1 , - 1 ) ;
if ( line_wraps ( new_line ) ) {
cursor_set_line ( new_line , true , false , times_line_wraps ( new_line ) ) ;
} else {
cursor_set_line ( new_line , true , false ) ;
}
}
}
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
if ( k - > get_shift ( ) )
_post_shift_selection ( ) ;
_cancel_code_hint ( ) ;
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
} break ;
case KEY_KP_2 : {
if ( k - > get_unicode ( ) ! = 0 ) {
scancode_handled = false ;
break ;
2016-01-07 20:38:38 +00:00
}
2019-04-05 12:06:16 +00:00
FALLTHROUGH ;
2017-05-20 15:38:03 +00:00
}
case KEY_DOWN : {
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
if ( k - > get_alt ( ) ) {
scancode_handled = false ;
break ;
}
2016-04-27 17:32:14 +00:00
# ifndef APPLE_STYLE_KEYS
2017-05-20 15:38:03 +00:00
if ( k - > get_command ( ) ) {
2016-04-27 17:32:14 +00:00
# else
2017-05-20 15:38:03 +00:00
if ( k - > get_command ( ) & & k - > get_alt ( ) ) {
2019-02-04 10:55:45 +00:00
# endif
2017-05-20 15:38:03 +00:00
_scroll_lines_down ( ) ;
break ;
}
2016-04-27 17:32:14 +00:00
2019-02-04 10:55:45 +00:00
if ( k - > get_shift ( ) ) {
_pre_shift_selection ( ) ;
}
# ifdef APPLE_STYLE_KEYS
2018-01-26 01:41:17 +00:00
if ( k - > get_command ( ) ) {
cursor_set_line ( get_last_unhidden_line ( ) , true , false , 9999 ) ;
} else
2014-02-10 01:10:30 +00:00
# endif
2018-01-26 01:41:17 +00:00
{
int cur_wrap_index = get_cursor_wrap_index ( ) ;
if ( cur_wrap_index < times_line_wraps ( cursor . line ) ) {
cursor_set_line ( cursor . line , true , false , cur_wrap_index + 1 ) ;
} else if ( cursor . line = = get_last_unhidden_line ( ) ) {
cursor_set_column ( text [ cursor . line ] . length ( ) ) ;
2017-12-26 06:53:16 +00:00
} else {
2018-01-26 01:41:17 +00:00
int new_line = cursor . line + num_lines_from ( CLAMP ( cursor . line + 1 , 0 , text . size ( ) - 1 ) , 1 ) ;
cursor_set_line ( new_line , true , false , 0 ) ;
2017-12-26 06:53:16 +00:00
}
}
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
if ( k - > get_shift ( ) )
_post_shift_selection ( ) ;
_cancel_code_hint ( ) ;
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
} break ;
case KEY_DELETE : {
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
if ( readonly )
break ;
2016-03-15 12:03:38 +00:00
2017-05-20 15:38:03 +00:00
if ( k - > get_shift ( ) & & ! k - > get_command ( ) & & ! k - > get_alt ( ) ) {
cut ( ) ;
break ;
}
2016-03-15 12:03:38 +00:00
2017-05-20 15:38:03 +00:00
int curline_len = text [ cursor . line ] . length ( ) ;
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
if ( cursor . line = = text . size ( ) - 1 & & cursor . column = = curline_len )
2019-08-18 15:56:24 +00:00
break ; // Nothing to do.
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
int next_line = cursor . column < curline_len ? cursor . line : cursor . line + 1 ;
int next_column ;
2015-11-12 16:35:48 +00:00
# ifdef APPLE_STYLE_KEYS
2017-08-01 17:40:43 +00:00
if ( k - > get_alt ( ) & & cursor . column < curline_len - 1 ) {
2015-11-12 16:35:48 +00:00
# else
2017-05-20 15:38:03 +00:00
if ( k - > get_alt ( ) ) {
scancode_handled = false ;
break ;
2017-08-01 17:40:43 +00:00
} else if ( k - > get_command ( ) & & cursor . column < curline_len - 1 ) {
2015-11-12 16:35:48 +00:00
# endif
2017-05-20 15:38:03 +00:00
int line = cursor . line ;
int column = cursor . column ;
2015-11-12 16:35:48 +00:00
2019-08-18 15:56:24 +00:00
// Check if we are removing a single whitespace, if so remove it and the next char type,
// else we just remove the whitespace.
2017-08-01 17:40:43 +00:00
bool only_whitespace = false ;
if ( _is_whitespace ( text [ line ] [ column ] ) & & _is_whitespace ( text [ line ] [ column + 1 ] ) ) {
only_whitespace = true ;
} else if ( _is_whitespace ( text [ line ] [ column ] ) ) {
2019-08-18 15:56:24 +00:00
// Remove the single whitespace.
2017-08-01 17:40:43 +00:00
column + + ;
2017-05-20 15:38:03 +00:00
}
2015-11-12 16:35:48 +00:00
2019-08-18 15:56:24 +00:00
// Check if its a text char.
2017-08-01 17:40:43 +00:00
bool only_char = ( _is_text_char ( text [ line ] [ column ] ) & & ! only_whitespace ) ;
2015-11-12 16:35:48 +00:00
2019-08-18 15:56:24 +00:00
// If its not whitespace or char then symbol.
2017-08-01 17:40:43 +00:00
bool only_symbols = ! ( only_whitespace | | only_char ) ;
2015-11-12 16:35:48 +00:00
2017-08-01 17:40:43 +00:00
while ( column < curline_len ) {
bool is_whitespace = _is_whitespace ( text [ line ] [ column ] ) ;
bool is_text_char = _is_text_char ( text [ line ] [ column ] ) ;
if ( only_whitespace & & ! is_whitespace ) {
2017-05-20 15:38:03 +00:00
break ;
2017-08-01 17:40:43 +00:00
} else if ( only_char & & ! is_text_char ) {
break ;
} else if ( only_symbols & & ( is_whitespace | | is_text_char ) ) {
break ;
}
2017-05-20 15:38:03 +00:00
column + + ;
2015-11-12 16:35:48 +00:00
}
2017-05-20 15:38:03 +00:00
next_line = line ;
next_column = column ;
2018-04-24 13:23:04 +00:00
# ifdef APPLE_STYLE_KEYS
} else if ( k - > get_command ( ) ) {
next_column = curline_len ;
next_line = cursor . line ;
# endif
2017-05-20 15:38:03 +00:00
} else {
next_column = cursor . column < curline_len ? ( cursor . column + 1 ) : 0 ;
}
2015-11-12 16:35:48 +00:00
2017-05-20 15:38:03 +00:00
_remove_text ( cursor . line , cursor . column , next_line , next_column ) ;
update ( ) ;
} break ;
case KEY_KP_7 : {
if ( k - > get_unicode ( ) ! = 0 ) {
scancode_handled = false ;
break ;
2016-01-07 21:45:28 +00:00
}
2019-04-05 12:06:16 +00:00
FALLTHROUGH ;
2017-05-20 15:38:03 +00:00
}
case KEY_HOME : {
2019-04-05 12:06:16 +00:00
# ifdef APPLE_STYLE_KEYS
2017-05-20 15:38:03 +00:00
if ( k - > get_shift ( ) )
_pre_shift_selection ( ) ;
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
cursor_set_line ( 0 ) ;
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
if ( k - > get_shift ( ) )
_post_shift_selection ( ) ;
else if ( k - > get_command ( ) | | k - > get_control ( ) )
deselect ( ) ;
2014-02-10 01:10:30 +00:00
# else
2017-05-20 15:38:03 +00:00
if ( k - > get_shift ( ) )
_pre_shift_selection ( ) ;
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
if ( k - > get_command ( ) ) {
cursor_set_line ( 0 ) ;
cursor_set_column ( 0 ) ;
} else {
2018-01-26 01:41:17 +00:00
2019-08-18 15:56:24 +00:00
// Move cursor column to start of wrapped row and then to start of text.
2018-01-26 01:41:17 +00:00
Vector < String > rows = get_wrap_rows_text ( cursor . line ) ;
int wi = get_cursor_wrap_index ( ) ;
int row_start_col = 0 ;
for ( int i = 0 ; i < wi ; i + + ) {
row_start_col + = rows [ i ] . length ( ) ;
2017-02-03 07:01:41 +00:00
}
2018-01-26 01:41:17 +00:00
if ( cursor . column = = row_start_col | | wi = = 0 ) {
2019-08-18 15:56:24 +00:00
// Compute whitespace symbols seq length.
2018-01-26 01:41:17 +00:00
int current_line_whitespace_len = 0 ;
while ( current_line_whitespace_len < text [ cursor . line ] . length ( ) ) {
CharType c = text [ cursor . line ] [ current_line_whitespace_len ] ;
if ( c ! = ' \t ' & & c ! = ' ' )
break ;
current_line_whitespace_len + + ;
}
2016-03-08 23:00:52 +00:00
2018-01-26 01:41:17 +00:00
if ( cursor_get_column ( ) = = current_line_whitespace_len )
cursor_set_column ( 0 ) ;
else
cursor_set_column ( current_line_whitespace_len ) ;
} else {
cursor_set_column ( row_start_col ) ;
}
2017-05-20 15:38:03 +00:00
}
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
if ( k - > get_shift ( ) )
_post_shift_selection ( ) ;
else if ( k - > get_command ( ) | | k - > get_control ( ) )
deselect ( ) ;
_cancel_completion ( ) ;
completion_hint = " " ;
2016-01-07 21:45:28 +00:00
# endif
2019-04-05 12:06:16 +00:00
} break ;
2017-05-20 15:38:03 +00:00
case KEY_KP_1 : {
if ( k - > get_unicode ( ) ! = 0 ) {
scancode_handled = false ;
break ;
2016-01-07 21:45:28 +00:00
}
2019-04-05 12:06:16 +00:00
FALLTHROUGH ;
2017-05-20 15:38:03 +00:00
}
case KEY_END : {
2019-04-05 12:06:16 +00:00
# ifdef APPLE_STYLE_KEYS
2017-05-20 15:38:03 +00:00
if ( k - > get_shift ( ) )
_pre_shift_selection ( ) ;
2016-01-07 21:45:28 +00:00
2018-01-26 01:41:17 +00:00
cursor_set_line ( get_last_unhidden_line ( ) , true , false , 9999 ) ;
2016-01-07 21:45:28 +00:00
2017-05-20 15:38:03 +00:00
if ( k - > get_shift ( ) )
_post_shift_selection ( ) ;
else if ( k - > get_command ( ) | | k - > get_control ( ) )
deselect ( ) ;
2016-01-07 21:45:28 +00:00
# else
2017-05-20 15:38:03 +00:00
if ( k - > get_shift ( ) )
_pre_shift_selection ( ) ;
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
if ( k - > get_command ( ) )
2018-01-26 01:41:17 +00:00
cursor_set_line ( get_last_unhidden_line ( ) , true , false , 9999 ) ;
2019-08-18 15:56:24 +00:00
// Move cursor column to end of wrapped row and then to end of text.
2018-01-26 01:41:17 +00:00
Vector < String > rows = get_wrap_rows_text ( cursor . line ) ;
int wi = get_cursor_wrap_index ( ) ;
int row_end_col = - 1 ;
for ( int i = 0 ; i < wi + 1 ; i + + ) {
row_end_col + = rows [ i ] . length ( ) ;
}
if ( wi = = rows . size ( ) - 1 | | cursor . column = = row_end_col ) {
cursor_set_column ( text [ cursor . line ] . length ( ) ) ;
} else {
cursor_set_column ( row_end_col ) ;
}
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
if ( k - > get_shift ( ) )
_post_shift_selection ( ) ;
else if ( k - > get_command ( ) | | k - > get_control ( ) )
deselect ( ) ;
2015-06-27 18:52:39 +00:00
2017-05-20 15:38:03 +00:00
_cancel_completion ( ) ;
completion_hint = " " ;
2014-02-10 01:10:30 +00:00
# endif
2019-04-05 12:06:16 +00:00
} break ;
2017-05-20 15:38:03 +00:00
case KEY_KP_9 : {
if ( k - > get_unicode ( ) ! = 0 ) {
scancode_handled = false ;
break ;
2016-01-07 21:45:28 +00:00
}
2019-04-05 12:06:16 +00:00
FALLTHROUGH ;
2017-05-20 15:38:03 +00:00
}
case KEY_PAGEUP : {
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
if ( k - > get_shift ( ) )
_pre_shift_selection ( ) ;
2016-03-08 23:00:52 +00:00
2018-01-26 01:41:17 +00:00
int wi ;
int n_line = cursor . line - num_lines_from_rows ( cursor . line , get_cursor_wrap_index ( ) , - get_visible_rows ( ) , wi ) + 1 ;
cursor_set_line ( n_line , true , false , wi ) ;
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
if ( k - > get_shift ( ) )
_post_shift_selection ( ) ;
2015-06-27 18:52:39 +00:00
2017-05-20 15:38:03 +00:00
_cancel_completion ( ) ;
completion_hint = " " ;
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
} break ;
case KEY_KP_3 : {
if ( k - > get_unicode ( ) ! = 0 ) {
scancode_handled = false ;
break ;
2016-01-07 21:45:28 +00:00
}
2019-04-05 12:06:16 +00:00
FALLTHROUGH ;
2017-05-20 15:38:03 +00:00
}
case KEY_PAGEDOWN : {
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
if ( k - > get_shift ( ) )
_pre_shift_selection ( ) ;
2016-03-08 23:00:52 +00:00
2018-01-26 01:41:17 +00:00
int wi ;
int n_line = cursor . line + num_lines_from_rows ( cursor . line , get_cursor_wrap_index ( ) , get_visible_rows ( ) , wi ) - 1 ;
cursor_set_line ( n_line , true , false , wi ) ;
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
if ( k - > get_shift ( ) )
_post_shift_selection ( ) ;
2015-06-27 18:52:39 +00:00
2017-05-20 15:38:03 +00:00
_cancel_completion ( ) ;
completion_hint = " " ;
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
} break ;
case KEY_A : {
2016-03-08 23:00:52 +00:00
2018-04-05 16:58:15 +00:00
# ifndef APPLE_STYLE_KEYS
2018-05-18 17:46:40 +00:00
if ( ! k - > get_control ( ) | | k - > get_shift ( ) | | k - > get_alt ( ) ) {
2017-05-20 15:38:03 +00:00
scancode_handled = false ;
break ;
}
select_all ( ) ;
2018-04-05 16:58:15 +00:00
# else
2018-05-18 17:46:40 +00:00
if ( ( ! k - > get_command ( ) & & ! k - > get_control ( ) ) ) {
2018-04-05 16:58:15 +00:00
scancode_handled = false ;
break ;
}
if ( ! k - > get_shift ( ) & & k - > get_command ( ) )
select_all ( ) ;
else if ( k - > get_control ( ) ) {
if ( k - > get_shift ( ) )
_pre_shift_selection ( ) ;
int current_line_whitespace_len = 0 ;
while ( current_line_whitespace_len < text [ cursor . line ] . length ( ) ) {
CharType c = text [ cursor . line ] [ current_line_whitespace_len ] ;
if ( c ! = ' \t ' & & c ! = ' ' )
break ;
current_line_whitespace_len + + ;
}
if ( cursor_get_column ( ) = = current_line_whitespace_len )
cursor_set_column ( 0 ) ;
else
cursor_set_column ( current_line_whitespace_len ) ;
if ( k - > get_shift ( ) )
_post_shift_selection ( ) ;
else if ( k - > get_command ( ) | | k - > get_control ( ) )
deselect ( ) ;
}
} break ;
case KEY_E : {
if ( ! k - > get_control ( ) | | k - > get_command ( ) | | k - > get_alt ( ) ) {
scancode_handled = false ;
break ;
}
2016-03-08 23:00:52 +00:00
2018-04-05 16:58:15 +00:00
if ( k - > get_shift ( ) )
_pre_shift_selection ( ) ;
if ( k - > get_command ( ) )
cursor_set_line ( text . size ( ) - 1 , true , false ) ;
cursor_set_column ( text [ cursor . line ] . length ( ) ) ;
if ( k - > get_shift ( ) )
_post_shift_selection ( ) ;
else if ( k - > get_command ( ) | | k - > get_control ( ) )
deselect ( ) ;
_cancel_completion ( ) ;
completion_hint = " " ;
# endif
2017-05-20 15:38:03 +00:00
} break ;
case KEY_X : {
if ( readonly ) {
break ;
}
if ( ! k - > get_command ( ) | | k - > get_shift ( ) | | k - > get_alt ( ) ) {
scancode_handled = false ;
break ;
}
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
cut ( ) ;
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
} break ;
case KEY_C : {
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
if ( ! k - > get_command ( ) | | k - > get_shift ( ) | | k - > get_alt ( ) ) {
scancode_handled = false ;
break ;
}
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
copy ( ) ;
2016-03-15 12:02:38 +00:00
2017-05-20 15:38:03 +00:00
} break ;
case KEY_Z : {
2016-03-08 23:00:52 +00:00
2019-08-25 06:57:59 +00:00
if ( readonly ) {
break ;
}
2017-05-20 15:38:03 +00:00
if ( ! k - > get_command ( ) ) {
scancode_handled = false ;
break ;
}
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
if ( k - > get_shift ( ) )
redo ( ) ;
else
undo ( ) ;
} break ;
2017-08-24 05:22:00 +00:00
case KEY_Y : {
2019-08-25 06:57:59 +00:00
if ( readonly ) {
break ;
}
2017-08-24 05:22:00 +00:00
if ( ! k - > get_command ( ) ) {
scancode_handled = false ;
break ;
}
redo ( ) ;
} break ;
2017-05-20 15:38:03 +00:00
case KEY_V : {
if ( readonly ) {
break ;
}
if ( ! k - > get_command ( ) | | k - > get_shift ( ) | | k - > get_alt ( ) ) {
scancode_handled = false ;
break ;
}
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
paste ( ) ;
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
} break ;
case KEY_SPACE : {
2015-01-04 14:03:31 +00:00
# ifdef OSX_ENABLED
2019-08-18 15:56:24 +00:00
if ( completion_enabled & & k - > get_metakey ( ) ) { // cmd-space is spotlight shortcut in OSX
2015-01-04 14:03:31 +00:00
# else
2017-05-20 15:38:03 +00:00
if ( completion_enabled & & k - > get_command ( ) ) {
2015-01-04 14:03:31 +00:00
# endif
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
query_code_comple ( ) ;
scancode_handled = true ;
} else {
scancode_handled = false ;
}
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
} break ;
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
default : {
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
scancode_handled = false ;
} break ;
}
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
if ( scancode_handled )
accept_event ( ) ;
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
if ( k - > get_scancode ( ) = = KEY_INSERT ) {
set_insert_mode ( ! insert_mode ) ;
accept_event ( ) ;
return ;
}
2016-03-08 23:00:52 +00:00
2019-08-18 15:56:24 +00:00
if ( ! scancode_handled & & ! k - > get_command ( ) ) { // For German keyboards.
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
if ( k - > get_unicode ( ) > = 32 ) {
2016-03-31 19:49:30 +00:00
2017-05-20 15:38:03 +00:00
if ( readonly )
return ;
2016-03-31 19:49:30 +00:00
2019-08-18 15:56:24 +00:00
// Remove the old character if in insert mode and no selection.
2017-05-20 15:38:03 +00:00
if ( insert_mode & & ! had_selection ) {
begin_complex_operation ( ) ;
2016-03-08 23:00:52 +00:00
2019-08-18 15:56:24 +00:00
// Make sure we don't try and remove empty space.
2017-05-20 15:38:03 +00:00
if ( cursor . column < get_line ( cursor . line ) . length ( ) ) {
_remove_text ( cursor . line , cursor . column , cursor . line , cursor . column + 1 ) ;
2015-01-02 18:08:40 +00:00
}
2017-05-20 15:38:03 +00:00
}
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
const CharType chr [ 2 ] = { ( CharType ) k - > get_unicode ( ) , 0 } ;
2016-04-05 12:52:18 +00:00
2017-05-20 15:38:03 +00:00
if ( completion_hint ! = " " & & k - > get_unicode ( ) = = ' ) ' ) {
completion_hint = " " ;
}
if ( auto_brace_completion_enabled & & _is_pair_symbol ( chr [ 0 ] ) ) {
_consume_pair_symbol ( chr [ 0 ] ) ;
2015-01-02 18:08:40 +00:00
} else {
2017-05-20 15:38:03 +00:00
_insert_text_at_cursor ( chr ) ;
}
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
if ( insert_mode & & ! had_selection ) {
end_complex_operation ( ) ;
2015-01-02 18:08:40 +00:00
}
2017-05-20 15:38:03 +00:00
if ( selection . active ! = had_selection ) {
end_complex_operation ( ) ;
}
accept_event ( ) ;
2015-01-02 18:08:40 +00:00
}
2017-05-20 15:38:03 +00:00
}
2016-03-08 23:00:52 +00:00
2017-05-20 15:38:03 +00:00
return ;
2015-01-02 18:08:40 +00:00
}
2014-02-10 01:10:30 +00:00
}
2017-11-01 20:49:39 +00:00
void TextEdit : : _scroll_up ( real_t p_delta ) {
2019-08-11 18:31:19 +00:00
if ( scrolling & & smooth_scroll_enabled & & SGN ( target_v_scroll - v_scroll - > get_value ( ) ) ! = SGN ( - p_delta ) ) {
2017-12-28 14:49:37 +00:00
scrolling = false ;
2019-08-11 18:31:19 +00:00
minimap_clicked = false ;
}
2017-12-28 14:49:37 +00:00
2017-11-01 20:49:39 +00:00
if ( scrolling ) {
target_v_scroll = ( target_v_scroll - p_delta ) ;
} else {
2018-01-26 01:41:17 +00:00
target_v_scroll = ( get_v_scroll ( ) - p_delta ) ;
2017-11-01 20:49:39 +00:00
}
if ( smooth_scroll_enabled ) {
if ( target_v_scroll < = 0 ) {
target_v_scroll = 0 ;
}
2017-12-28 14:49:37 +00:00
if ( Math : : abs ( target_v_scroll - v_scroll - > get_value ( ) ) < 1.0 ) {
v_scroll - > set_value ( target_v_scroll ) ;
} else {
scrolling = true ;
2018-04-11 07:28:14 +00:00
set_physics_process_internal ( true ) ;
2017-12-28 14:49:37 +00:00
}
2017-11-01 20:49:39 +00:00
} else {
2018-01-26 01:41:17 +00:00
set_v_scroll ( target_v_scroll ) ;
2017-11-01 20:49:39 +00:00
}
}
void TextEdit : : _scroll_down ( real_t p_delta ) {
2019-08-11 18:31:19 +00:00
if ( scrolling & & smooth_scroll_enabled & & SGN ( target_v_scroll - v_scroll - > get_value ( ) ) ! = SGN ( p_delta ) ) {
2017-12-28 14:49:37 +00:00
scrolling = false ;
2019-08-11 18:31:19 +00:00
minimap_clicked = false ;
}
2017-12-28 14:49:37 +00:00
2017-11-01 20:49:39 +00:00
if ( scrolling ) {
target_v_scroll = ( target_v_scroll + p_delta ) ;
} else {
2018-01-26 01:41:17 +00:00
target_v_scroll = ( get_v_scroll ( ) + p_delta ) ;
2017-11-01 20:49:39 +00:00
}
if ( smooth_scroll_enabled ) {
2018-10-05 20:50:49 +00:00
int max_v_scroll = round ( v_scroll - > get_max ( ) - v_scroll - > get_page ( ) ) ;
2017-11-01 20:49:39 +00:00
if ( target_v_scroll > max_v_scroll ) {
target_v_scroll = max_v_scroll ;
}
2017-12-28 14:49:37 +00:00
if ( Math : : abs ( target_v_scroll - v_scroll - > get_value ( ) ) < 1.0 ) {
v_scroll - > set_value ( target_v_scroll ) ;
} else {
scrolling = true ;
2018-04-11 07:28:14 +00:00
set_physics_process_internal ( true ) ;
2017-12-28 14:49:37 +00:00
}
2017-11-01 20:49:39 +00:00
} else {
2018-01-26 01:41:17 +00:00
set_v_scroll ( target_v_scroll ) ;
2017-11-01 20:49:39 +00:00
}
}
2014-02-10 01:10:30 +00:00
void TextEdit : : _pre_shift_selection ( ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
if ( ! selection . active | | selection . selecting_mode = = Selection : : MODE_NONE ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
selection . selecting_line = cursor . line ;
selection . selecting_column = cursor . column ;
selection . active = true ;
2015-01-02 18:08:40 +00:00
}
2015-08-12 22:34:07 +00:00
2017-03-05 15:44:50 +00:00
selection . selecting_mode = Selection : : MODE_SHIFT ;
2014-02-10 01:10:30 +00:00
}
void TextEdit : : _post_shift_selection ( ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
if ( selection . active & & selection . selecting_mode = = Selection : : MODE_SHIFT ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
select ( selection . selecting_line , selection . selecting_column , cursor . line , cursor . column ) ;
2015-01-02 18:08:40 +00:00
update ( ) ;
}
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
selection . selecting_text = true ;
2014-02-10 01:10:30 +00:00
}
2016-04-27 17:32:14 +00:00
void TextEdit : : _scroll_lines_up ( ) {
2017-08-22 19:02:08 +00:00
scrolling = false ;
2019-08-11 18:31:19 +00:00
minimap_clicked = false ;
2017-08-22 19:02:08 +00:00
2019-08-18 15:56:24 +00:00
// Adjust the vertical scroll.
2018-01-26 01:41:17 +00:00
set_v_scroll ( get_v_scroll ( ) - 1 ) ;
2019-08-18 15:56:24 +00:00
// Adjust the cursor to viewport.
2018-01-26 01:41:17 +00:00
if ( ! selection . active ) {
int cur_line = cursor . line ;
int cur_wrap = get_cursor_wrap_index ( ) ;
int last_vis_line = get_last_visible_line ( ) ;
int last_vis_wrap = get_last_visible_line_wrap_index ( ) ;
2016-04-27 17:32:14 +00:00
2018-01-26 01:41:17 +00:00
if ( cur_line > last_vis_line | | ( cur_line = = last_vis_line & & cur_wrap > last_vis_wrap ) ) {
cursor_set_line ( last_vis_line , false , false , last_vis_wrap ) ;
}
2016-04-27 17:32:14 +00:00
}
}
void TextEdit : : _scroll_lines_down ( ) {
2017-08-22 19:02:08 +00:00
scrolling = false ;
2019-08-11 18:31:19 +00:00
minimap_clicked = false ;
2017-08-22 19:02:08 +00:00
2019-08-18 15:56:24 +00:00
// Adjust the vertical scroll.
2018-01-26 01:41:17 +00:00
set_v_scroll ( get_v_scroll ( ) + 1 ) ;
2016-04-27 17:32:14 +00:00
2019-08-18 15:56:24 +00:00
// Adjust the cursor to viewport.
2018-01-26 01:41:17 +00:00
if ( ! selection . active ) {
int cur_line = cursor . line ;
int cur_wrap = get_cursor_wrap_index ( ) ;
int first_vis_line = get_first_visible_line ( ) ;
int first_vis_wrap = cursor . wrap_ofs ;
if ( cur_line < first_vis_line | | ( cur_line = = first_vis_line & & cur_wrap < first_vis_wrap ) ) {
cursor_set_line ( first_vis_line , false , false , first_vis_wrap ) ;
}
2016-04-27 17:32:14 +00:00
}
}
2014-02-10 01:10:30 +00:00
/**** TEXT EDIT CORE API ****/
2017-03-05 15:44:50 +00:00
void TextEdit : : _base_insert_text ( int p_line , int p_char , const String & p_text , int & r_end_line , int & r_end_column ) {
2016-03-08 23:00:52 +00:00
2019-08-18 15:56:24 +00:00
// Save for undo.
2017-03-05 15:44:50 +00:00
ERR_FAIL_INDEX ( p_line , text . size ( ) ) ;
ERR_FAIL_COND ( p_char < 0 ) ;
2016-03-08 23:00:52 +00:00
2019-08-18 15:56:24 +00:00
/* STEP 1: Remove \r from source text and separate in substrings. */
2018-05-30 12:02:51 +00:00
Vector < String > substrings = p_text . replace ( " \r " , " " ) . split ( " \n " ) ;
2019-08-18 15:56:24 +00:00
/* STEP 2: Fire breakpoint_toggled signals. */
2018-05-30 12:02:51 +00:00
// Is this just a new empty line?
bool shift_first_line = p_char = = 0 & & p_text . replace ( " \r " , " " ) = = " \n " ;
int i = p_line + ! shift_first_line ;
int lines = substrings . size ( ) - 1 ;
for ( ; i < text . size ( ) ; i + + ) {
if ( text . is_breakpoint ( i ) ) {
if ( ( i - lines < p_line | | ! text . is_breakpoint ( i - lines ) ) | | ( i - lines = = p_line & & ! shift_first_line ) )
emit_signal ( " breakpoint_toggled " , i ) ;
if ( i + lines > = text . size ( ) | | ! text . is_breakpoint ( i + lines ) )
emit_signal ( " breakpoint_toggled " , i + lines ) ;
}
}
2019-08-18 15:56:24 +00:00
/* STEP 3: Add spaces if the char is greater than the end of the line. */
2017-03-05 15:44:50 +00:00
while ( p_char > text [ p_line ] . length ( ) ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
text . set ( p_line , text [ p_line ] + String : : chr ( ' ' ) ) ;
2015-01-02 18:08:40 +00:00
}
2016-03-08 23:00:52 +00:00
2019-08-18 15:56:24 +00:00
/* STEP 4: Separate dest string in pre and post text. */
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
String preinsert_text = text [ p_line ] . substr ( 0 , p_char ) ;
String postinsert_text = text [ p_line ] . substr ( p_char , text [ p_line ] . size ( ) ) ;
2016-03-08 23:00:52 +00:00
2019-02-12 20:10:08 +00:00
for ( int j = 0 ; j < substrings . size ( ) ; j + + ) {
2019-08-18 15:56:24 +00:00
// Insert the substrings.
2016-03-08 23:00:52 +00:00
2019-02-12 20:10:08 +00:00
if ( j = = 0 ) {
2016-03-08 23:00:52 +00:00
2019-02-12 20:10:08 +00:00
text . set ( p_line , preinsert_text + substrings [ j ] ) ;
2015-01-02 18:08:40 +00:00
} else {
2016-03-08 23:00:52 +00:00
2019-02-12 20:10:08 +00:00
text . insert ( p_line + j , substrings [ j ] ) ;
2015-01-02 18:08:40 +00:00
}
2016-03-08 23:00:52 +00:00
2019-02-12 20:10:08 +00:00
if ( j = = substrings . size ( ) - 1 ) {
2016-03-08 23:00:52 +00:00
2019-02-12 20:10:08 +00:00
text . set ( p_line + j , text [ p_line + j ] + postinsert_text ) ;
2015-01-02 18:08:40 +00:00
}
}
2016-03-08 23:00:52 +00:00
2018-05-30 12:02:51 +00:00
if ( shift_first_line ) {
2017-11-12 23:12:17 +00:00
text . set_breakpoint ( p_line + 1 , text . is_breakpoint ( p_line ) ) ;
text . set_hidden ( p_line + 1 , text . is_hidden ( p_line ) ) ;
2019-04-20 11:51:25 +00:00
text . set_info_icon ( p_line + 1 , text . get_info_icon ( p_line ) , text . get_info ( p_line ) ) ;
2017-11-12 23:12:17 +00:00
text . set_breakpoint ( p_line , false ) ;
text . set_hidden ( p_line , false ) ;
2019-04-20 11:51:25 +00:00
text . set_info_icon ( p_line , NULL , " " ) ;
2017-11-12 23:12:17 +00:00
}
2018-01-26 01:41:17 +00:00
text . set_line_wrap_amount ( p_line , - 1 ) ;
2017-03-05 15:44:50 +00:00
r_end_line = p_line + substrings . size ( ) - 1 ;
r_end_column = text [ r_end_line ] . length ( ) - postinsert_text . length ( ) ;
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
if ( ! text_changed_dirty & & ! setting_text ) {
if ( is_inside_tree ( ) )
2017-03-05 15:44:50 +00:00
MessageQueue : : get_singleton ( ) - > push_call ( this , " _text_changed_emit " ) ;
text_changed_dirty = true ;
2015-01-02 18:08:40 +00:00
}
2018-04-07 13:14:19 +00:00
_line_edited_from ( p_line ) ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
String TextEdit : : _base_get_text ( int p_from_line , int p_from_column , int p_to_line , int p_to_column ) const {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
ERR_FAIL_INDEX_V ( p_from_line , text . size ( ) , String ( ) ) ;
ERR_FAIL_INDEX_V ( p_from_column , text [ p_from_line ] . length ( ) + 1 , String ( ) ) ;
ERR_FAIL_INDEX_V ( p_to_line , text . size ( ) , String ( ) ) ;
ERR_FAIL_INDEX_V ( p_to_column , text [ p_to_line ] . length ( ) + 1 , String ( ) ) ;
2019-08-18 15:56:24 +00:00
ERR_FAIL_COND_V ( p_to_line < p_from_line , String ( ) ) ; // 'from > to'.
ERR_FAIL_COND_V ( p_to_line = = p_from_line & & p_to_column < p_from_column , String ( ) ) ; // 'from > to'.
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
String ret ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
for ( int i = p_from_line ; i < = p_to_line ; i + + ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
int begin = ( i = = p_from_line ) ? p_from_column : 0 ;
int end = ( i = = p_to_line ) ? p_to_column : text [ i ] . length ( ) ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
if ( i > p_from_line )
ret + = " \n " ;
ret + = text [ i ] . substr ( begin , end - begin ) ;
2015-01-02 18:08:40 +00:00
}
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
return ret ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
void TextEdit : : _base_remove_text ( int p_from_line , int p_from_column , int p_to_line , int p_to_column ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
ERR_FAIL_INDEX ( p_from_line , text . size ( ) ) ;
ERR_FAIL_INDEX ( p_from_column , text [ p_from_line ] . length ( ) + 1 ) ;
ERR_FAIL_INDEX ( p_to_line , text . size ( ) ) ;
ERR_FAIL_INDEX ( p_to_column , text [ p_to_line ] . length ( ) + 1 ) ;
2019-08-18 15:56:24 +00:00
ERR_FAIL_COND ( p_to_line < p_from_line ) ; // 'from > to'.
ERR_FAIL_COND ( p_to_line = = p_from_line & & p_to_column < p_from_column ) ; // 'from > to'.
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
String pre_text = text [ p_from_line ] . substr ( 0 , p_from_column ) ;
String post_text = text [ p_to_line ] . substr ( p_to_column , text [ p_to_line ] . length ( ) ) ;
2016-03-08 23:00:52 +00:00
2018-05-30 12:02:51 +00:00
int lines = p_to_line - p_from_line ;
2016-03-08 23:00:52 +00:00
2018-05-30 12:02:51 +00:00
for ( int i = p_from_line + 1 ; i < text . size ( ) ; i + + ) {
if ( text . is_breakpoint ( i ) ) {
if ( i + lines > = text . size ( ) | | ! text . is_breakpoint ( i + lines ) )
emit_signal ( " breakpoint_toggled " , i ) ;
if ( i > p_to_line & & ( i - lines < 0 | | ! text . is_breakpoint ( i - lines ) ) )
emit_signal ( " breakpoint_toggled " , i - lines ) ;
}
2015-01-02 18:08:40 +00:00
}
2016-03-08 23:00:52 +00:00
2018-05-30 12:02:51 +00:00
for ( int i = p_from_line ; i < p_to_line ; i + + ) {
text . remove ( p_from_line + 1 ) ;
}
2017-03-05 15:44:50 +00:00
text . set ( p_from_line , pre_text + post_text ) ;
2016-03-08 23:00:52 +00:00
2018-01-26 01:41:17 +00:00
text . set_line_wrap_amount ( p_from_line , - 1 ) ;
2015-01-02 18:08:40 +00:00
if ( ! text_changed_dirty & & ! setting_text ) {
if ( is_inside_tree ( ) )
2017-03-05 15:44:50 +00:00
MessageQueue : : get_singleton ( ) - > push_call ( this , " _text_changed_emit " ) ;
text_changed_dirty = true ;
2015-01-02 18:08:40 +00:00
}
2018-04-07 13:14:19 +00:00
_line_edited_from ( p_from_line ) ;
2014-02-10 01:10:30 +00:00
}
2017-08-11 19:10:05 +00:00
void TextEdit : : _insert_text ( int p_line , int p_char , const String & p_text , int * r_end_line , int * r_end_char ) {
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
if ( ! setting_text )
idle_detect - > start ( ) ;
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
if ( undo_enabled ) {
_clear_redo ( ) ;
}
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
int retline , retchar ;
_base_insert_text ( p_line , p_char , p_text , retline , retchar ) ;
2015-01-02 18:08:40 +00:00
if ( r_end_line )
2017-03-05 15:44:50 +00:00
* r_end_line = retline ;
2017-08-11 19:10:05 +00:00
if ( r_end_char )
* r_end_char = retchar ;
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
if ( ! undo_enabled )
return ;
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
/* UNDO!! */
TextOperation op ;
2017-03-05 15:44:50 +00:00
op . type = TextOperation : : TYPE_INSERT ;
op . from_line = p_line ;
op . from_column = p_char ;
op . to_line = retline ;
op . to_column = retchar ;
op . text = p_text ;
op . version = + + version ;
op . chain_forward = false ;
op . chain_backward = false ;
2016-03-08 23:00:52 +00:00
2019-08-18 15:56:24 +00:00
// See if it should just be set as current op.
2017-03-05 15:44:50 +00:00
if ( current_op . type ! = op . type ) {
2016-03-13 20:08:12 +00:00
op . prev_version = get_version ( ) ;
2015-01-02 18:08:40 +00:00
_push_current_op ( ) ;
2017-03-05 15:44:50 +00:00
current_op = op ;
2016-03-08 23:00:52 +00:00
2019-08-18 15:56:24 +00:00
return ; // Set as current op, return.
2015-01-02 18:08:40 +00:00
}
2019-08-18 15:56:24 +00:00
// See if it can be merged.
2017-03-05 15:44:50 +00:00
if ( current_op . to_line ! = p_line | | current_op . to_column ! = p_char ) {
2016-03-13 20:08:12 +00:00
op . prev_version = get_version ( ) ;
2015-01-02 18:08:40 +00:00
_push_current_op ( ) ;
2017-03-05 15:44:50 +00:00
current_op = op ;
2019-08-18 15:56:24 +00:00
return ; // Set as current op, return.
2015-01-02 18:08:40 +00:00
}
2019-08-18 15:56:24 +00:00
// Merge current op.
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
current_op . text + = p_text ;
current_op . to_column = retchar ;
current_op . to_line = retline ;
current_op . version = op . version ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
void TextEdit : : _remove_text ( int p_from_line , int p_from_column , int p_to_line , int p_to_column ) {
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
if ( ! setting_text )
idle_detect - > start ( ) ;
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
String text ;
if ( undo_enabled ) {
_clear_redo ( ) ;
2017-03-05 15:44:50 +00:00
text = _base_get_text ( p_from_line , p_from_column , p_to_line , p_to_column ) ;
2015-01-02 18:08:40 +00:00
}
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
_base_remove_text ( p_from_line , p_from_column , p_to_line , p_to_column ) ;
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
if ( ! undo_enabled )
return ;
2016-03-08 23:00:52 +00:00
2019-08-18 15:56:24 +00:00
/* UNDO! */
2015-01-02 18:08:40 +00:00
TextOperation op ;
2017-03-05 15:44:50 +00:00
op . type = TextOperation : : TYPE_REMOVE ;
op . from_line = p_from_line ;
op . from_column = p_from_column ;
op . to_line = p_to_line ;
op . to_column = p_to_column ;
op . text = text ;
op . version = + + version ;
op . chain_forward = false ;
op . chain_backward = false ;
2016-03-08 23:00:52 +00:00
2019-08-18 15:56:24 +00:00
// See if it should just be set as current op.
2017-03-05 15:44:50 +00:00
if ( current_op . type ! = op . type ) {
2016-03-13 20:08:12 +00:00
op . prev_version = get_version ( ) ;
2015-01-02 18:08:40 +00:00
_push_current_op ( ) ;
2017-03-05 15:44:50 +00:00
current_op = op ;
2019-08-18 15:56:24 +00:00
return ; // Set as current op, return.
2015-01-02 18:08:40 +00:00
}
2019-08-18 15:56:24 +00:00
// See if it can be merged.
2017-03-05 15:44:50 +00:00
if ( current_op . from_line = = p_to_line & & current_op . from_column = = p_to_column ) {
2019-08-18 15:56:24 +00:00
// Backspace or similar.
2017-03-05 15:44:50 +00:00
current_op . text = text + current_op . text ;
current_op . from_line = p_from_line ;
current_op . from_column = p_from_column ;
2019-08-18 15:56:24 +00:00
return ; // Update current op.
2015-01-02 18:08:40 +00:00
}
2016-03-08 23:00:52 +00:00
2016-03-13 20:08:12 +00:00
op . prev_version = get_version ( ) ;
2015-01-02 18:08:40 +00:00
_push_current_op ( ) ;
2017-03-05 15:44:50 +00:00
current_op = op ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
void TextEdit : : _insert_text_at_cursor ( const String & p_text ) {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
int new_column , new_line ;
_insert_text ( cursor . line , cursor . column , p_text , & new_line , & new_column ) ;
2015-01-02 18:08:40 +00:00
cursor_set_line ( new_line ) ;
cursor_set_column ( new_column ) ;
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
update ( ) ;
2014-02-10 01:10:30 +00:00
}
2018-04-07 13:14:19 +00:00
void TextEdit : : _line_edited_from ( int p_line ) {
int cache_size = color_region_cache . size ( ) ;
for ( int i = p_line ; i < cache_size ; i + + ) {
color_region_cache . erase ( i ) ;
}
2019-08-11 18:29:09 +00:00
if ( syntax_highlighting_cache . size ( ) > 0 ) {
cache_size = syntax_highlighting_cache . back ( ) - > key ( ) ;
2019-08-24 14:21:45 +00:00
for ( int i = p_line - 1 ; i < = cache_size ; i + + ) {
2019-08-11 18:29:09 +00:00
if ( syntax_highlighting_cache . has ( i ) ) {
syntax_highlighting_cache . erase ( i ) ;
}
}
}
2018-04-07 13:14:19 +00:00
}
2014-02-10 01:10:30 +00:00
int TextEdit : : get_char_count ( ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
int totalsize = 0 ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
for ( int i = 0 ; i < text . size ( ) ; i + + ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
if ( i > 0 )
2019-08-18 15:56:24 +00:00
totalsize + + ; // Include \n.
2017-03-05 15:44:50 +00:00
totalsize + = text [ i ] . length ( ) ;
2015-01-02 18:08:40 +00:00
}
2016-03-08 23:00:52 +00:00
2019-08-18 15:56:24 +00:00
return totalsize ; // Omit last \n.
2014-02-10 01:10:30 +00:00
}
2016-06-12 16:31:22 +00:00
Size2 TextEdit : : get_minimum_size ( ) const {
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
return cache . style_normal - > get_minimum_size ( ) ;
2014-02-10 01:10:30 +00:00
}
2017-11-12 23:12:17 +00:00
2019-08-25 13:49:13 +00:00
int TextEdit : : _get_control_height ( ) const {
int control_height = get_size ( ) . height ;
control_height - = cache . style_normal - > get_minimum_size ( ) . height ;
if ( h_scroll - > is_visible_in_tree ( ) ) {
control_height - = h_scroll - > get_size ( ) . height ;
}
return control_height ;
}
2016-03-08 23:00:52 +00:00
2019-08-25 13:49:13 +00:00
int TextEdit : : get_visible_rows ( ) const {
return _get_control_height ( ) / get_row_height ( ) ;
2014-02-10 01:10:30 +00:00
}
2017-11-12 23:12:17 +00:00
2019-08-11 18:31:19 +00:00
int TextEdit : : _get_minimap_visible_rows ( ) const {
2019-08-25 13:49:13 +00:00
return _get_control_height ( ) / ( minimap_char_size . y + minimap_line_spacing ) ;
2019-08-11 18:31:19 +00:00
}
2018-01-26 01:41:17 +00:00
int TextEdit : : get_total_visible_rows ( ) const {
2019-08-18 15:56:24 +00:00
// Returns the total amount of rows we need in the editor.
2018-01-26 01:41:17 +00:00
// This skips hidden lines and counts each wrapping of a line.
if ( ! is_hiding_enabled ( ) & & ! is_wrap_enabled ( ) )
2017-11-16 04:00:27 +00:00
return text . size ( ) ;
2018-01-26 01:41:17 +00:00
int total_rows = 0 ;
2017-11-16 04:00:27 +00:00
for ( int i = 0 ; i < text . size ( ) ; i + + ) {
2018-01-26 01:41:17 +00:00
if ( ! text . is_hidden ( i ) ) {
total_rows + + ;
total_rows + = times_line_wraps ( i ) ;
}
2017-11-16 04:00:27 +00:00
}
2018-01-26 01:41:17 +00:00
return total_rows ;
2017-11-16 04:00:27 +00:00
}
2019-01-05 16:58:54 +00:00
void TextEdit : : _update_wrap_at ( ) {
2017-11-16 04:00:27 +00:00
2019-08-11 18:31:19 +00:00
wrap_at = get_size ( ) . width - cache . style_normal - > get_minimum_size ( ) . width - cache . line_number_w - cache . breakpoint_gutter_width - cache . fold_gutter_width - cache . info_gutter_width - cache . minimap_width - wrap_right_offset ;
2018-01-26 01:41:17 +00:00
update_cursor_wrap_offset ( ) ;
text . clear_wrap_cache ( ) ;
2017-11-16 04:00:27 +00:00
2018-01-26 01:41:17 +00:00
for ( int i = 0 ; i < text . size ( ) ; i + + ) {
2019-08-18 15:56:24 +00:00
// Update all values that wrap.
2018-01-26 01:41:17 +00:00
if ( ! line_wraps ( i ) )
continue ;
Vector < String > rows = get_wrap_rows_text ( i ) ;
text . set_line_wrap_amount ( i , rows . size ( ) - 1 ) ;
2017-11-16 04:00:27 +00:00
}
}
2018-01-26 01:41:17 +00:00
void TextEdit : : adjust_viewport_to_cursor ( ) {
2017-11-16 04:00:27 +00:00
2019-08-18 15:56:24 +00:00
// Make sure cursor is visible on the screen.
2018-01-26 01:41:17 +00:00
scrolling = false ;
2019-08-11 18:31:19 +00:00
minimap_clicked = false ;
2017-11-16 04:00:27 +00:00
2018-01-26 01:41:17 +00:00
int cur_line = cursor . line ;
int cur_wrap = get_cursor_wrap_index ( ) ;
2017-11-16 04:00:27 +00:00
2018-01-26 01:41:17 +00:00
int first_vis_line = get_first_visible_line ( ) ;
int first_vis_wrap = cursor . wrap_ofs ;
int last_vis_line = get_last_visible_line ( ) ;
int last_vis_wrap = get_last_visible_line_wrap_index ( ) ;
2016-03-08 23:00:52 +00:00
2018-01-26 01:41:17 +00:00
if ( cur_line < first_vis_line | | ( cur_line = = first_vis_line & & cur_wrap < first_vis_wrap ) ) {
2019-08-18 15:56:24 +00:00
// Cursor is above screen.
2018-01-26 01:41:17 +00:00
set_line_as_first_visible ( cur_line , cur_wrap ) ;
} else if ( cur_line > last_vis_line | | ( cur_line = = last_vis_line & & cur_wrap > last_vis_wrap ) ) {
2019-08-18 15:56:24 +00:00
// Cursor is below screen.
2018-01-26 01:41:17 +00:00
set_line_as_last_visible ( cur_line , cur_wrap ) ;
2017-11-16 04:00:27 +00:00
}
2016-03-08 23:00:52 +00:00
2019-08-11 18:31:19 +00:00
int visible_width = get_size ( ) . width - cache . style_normal - > get_minimum_size ( ) . width - cache . line_number_w - cache . breakpoint_gutter_width - cache . fold_gutter_width - cache . info_gutter_width - cache . minimap_width ;
2017-01-13 13:45:50 +00:00
if ( v_scroll - > is_visible_in_tree ( ) )
2017-03-05 15:44:50 +00:00
visible_width - = v_scroll - > get_combined_minimum_size ( ) . width ;
2019-08-18 15:56:24 +00:00
visible_width - = 20 ; // Give it a little more space.
2016-03-08 23:00:52 +00:00
2018-05-15 19:32:09 +00:00
if ( ! is_wrap_enabled ( ) ) {
2019-08-18 15:56:24 +00:00
// Adjust x offset.
2018-01-26 01:41:17 +00:00
int cursor_x = get_column_x_offset ( cursor . column , text [ cursor . line ] ) ;
2016-03-08 23:00:52 +00:00
2018-01-26 01:41:17 +00:00
if ( cursor_x > ( cursor . x_ofs + visible_width ) )
cursor . x_ofs = cursor_x - visible_width + 1 ;
2016-03-08 23:00:52 +00:00
2018-01-26 01:41:17 +00:00
if ( cursor_x < cursor . x_ofs )
cursor . x_ofs = cursor_x ;
} else {
cursor . x_ofs = 0 ;
2017-11-26 19:10:05 +00:00
}
2018-01-26 01:41:17 +00:00
h_scroll - > set_value ( cursor . x_ofs ) ;
2015-01-02 18:08:40 +00:00
update ( ) ;
2014-02-10 01:10:30 +00:00
}
2016-07-21 01:40:08 +00:00
void TextEdit : : center_viewport_to_cursor ( ) {
2019-08-18 15:56:24 +00:00
// Move viewport so the cursor is in the center of the screen.
2018-01-26 01:41:17 +00:00
scrolling = false ;
2019-08-11 18:31:19 +00:00
minimap_clicked = false ;
2016-07-21 01:40:08 +00:00
2017-11-12 23:12:17 +00:00
if ( is_line_hidden ( cursor . line ) )
unfold_line ( cursor . line ) ;
2018-01-26 01:41:17 +00:00
set_line_as_center_visible ( cursor . line , get_cursor_wrap_index ( ) ) ;
2019-08-11 18:31:19 +00:00
int visible_width = get_size ( ) . width - cache . style_normal - > get_minimum_size ( ) . width - cache . line_number_w - cache . breakpoint_gutter_width - cache . fold_gutter_width - cache . info_gutter_width - cache . minimap_width ;
2017-01-13 13:45:50 +00:00
if ( v_scroll - > is_visible_in_tree ( ) )
2017-03-05 15:44:50 +00:00
visible_width - = v_scroll - > get_combined_minimum_size ( ) . width ;
2019-08-18 15:56:24 +00:00
visible_width - = 20 ; // Give it a little more space.
2016-07-21 01:40:08 +00:00
2018-01-26 01:41:17 +00:00
if ( is_wrap_enabled ( ) ) {
2019-08-18 15:56:24 +00:00
// Center x offset.
2018-01-26 01:41:17 +00:00
int cursor_x = get_column_x_offset_for_line ( cursor . column , cursor . line ) ;
if ( cursor_x > ( cursor . x_ofs + visible_width ) )
cursor . x_ofs = cursor_x - visible_width + 1 ;
if ( cursor_x < cursor . x_ofs )
cursor . x_ofs = cursor_x ;
} else {
cursor . x_ofs = 0 ;
2018-01-06 21:46:31 +00:00
}
2018-01-26 01:41:17 +00:00
h_scroll - > set_value ( cursor . x_ofs ) ;
2016-07-21 01:40:08 +00:00
2018-01-26 01:41:17 +00:00
update ( ) ;
}
2016-07-21 01:40:08 +00:00
2018-01-26 01:41:17 +00:00
void TextEdit : : update_cursor_wrap_offset ( ) {
int first_vis_line = get_first_visible_line ( ) ;
if ( line_wraps ( first_vis_line ) ) {
cursor . wrap_ofs = MIN ( cursor . wrap_ofs , times_line_wraps ( first_vis_line ) ) ;
} else {
cursor . wrap_ofs = 0 ;
}
set_line_as_first_visible ( cursor . line_ofs , cursor . wrap_ofs ) ;
}
2016-07-21 01:40:08 +00:00
2018-01-26 01:41:17 +00:00
bool TextEdit : : line_wraps ( int line ) const {
ERR_FAIL_INDEX_V ( line , text . size ( ) , 0 ) ;
if ( ! is_wrap_enabled ( ) )
return false ;
return text . get_line_width ( line ) > wrap_at ;
}
int TextEdit : : times_line_wraps ( int line ) const {
ERR_FAIL_INDEX_V ( line , text . size ( ) , 0 ) ;
if ( ! line_wraps ( line ) )
return 0 ;
int wrap_amount = text . get_line_wrap_amount ( line ) ;
if ( wrap_amount = = - 1 ) {
2019-08-18 15:56:24 +00:00
// Update the value.
2018-01-26 01:41:17 +00:00
Vector < String > rows = get_wrap_rows_text ( line ) ;
wrap_amount = rows . size ( ) - 1 ;
text . set_line_wrap_amount ( line , wrap_amount ) ;
2017-11-26 19:10:05 +00:00
}
2018-01-26 01:41:17 +00:00
return wrap_amount ;
}
Vector < String > TextEdit : : get_wrap_rows_text ( int p_line ) const {
ERR_FAIL_INDEX_V ( p_line , text . size ( ) , Vector < String > ( ) ) ;
Vector < String > lines ;
if ( ! line_wraps ( p_line ) ) {
lines . push_back ( text [ p_line ] ) ;
return lines ;
}
int px = 0 ;
int col = 0 ;
String line_text = text [ p_line ] ;
String wrap_substring = " " ;
int word_px = 0 ;
String word_str = " " ;
int cur_wrap_index = 0 ;
int tab_offset_px = get_indent_level ( p_line ) * cache . font - > get_char_size ( ' ' ) . width ;
2019-02-20 13:52:33 +00:00
if ( tab_offset_px > = wrap_at ) {
tab_offset_px = 0 ;
}
2018-01-26 01:41:17 +00:00
while ( col < line_text . length ( ) ) {
2018-09-28 04:31:15 +00:00
CharType c = line_text [ col ] ;
2018-01-26 01:41:17 +00:00
int w = text . get_char_width ( c , line_text [ col + 1 ] , px + word_px ) ;
int indent_ofs = ( cur_wrap_index ! = 0 ? tab_offset_px : 0 ) ;
2019-02-20 13:52:33 +00:00
if ( indent_ofs + word_px + w > wrap_at ) {
2019-08-18 15:56:24 +00:00
// Not enough space to add this char; start next line.
2018-01-26 01:41:17 +00:00
wrap_substring + = word_str ;
2019-02-20 13:52:33 +00:00
lines . push_back ( wrap_substring ) ;
cur_wrap_index + + ;
wrap_substring = " " ;
px = 0 ;
2018-01-26 01:41:17 +00:00
2019-02-20 13:52:33 +00:00
word_str = " " ;
word_str + = c ;
word_px = w ;
} else {
word_str + = c ;
word_px + = w ;
if ( c = = ' ' ) {
2019-08-18 15:56:24 +00:00
// End of a word; add this word to the substring.
2018-01-26 01:41:17 +00:00
wrap_substring + = word_str ;
2019-02-20 13:52:33 +00:00
px + = word_px ;
2018-01-26 01:41:17 +00:00
word_str = " " ;
word_px = 0 ;
}
2019-02-20 13:52:33 +00:00
if ( indent_ofs + px + word_px > wrap_at ) {
2019-08-18 15:56:24 +00:00
// This word will be moved to the next line.
2019-02-20 13:52:33 +00:00
lines . push_back ( wrap_substring ) ;
2019-08-18 15:56:24 +00:00
// Reset for next wrap.
2019-02-20 13:52:33 +00:00
cur_wrap_index + + ;
wrap_substring = " " ;
px = 0 ;
}
2018-01-26 01:41:17 +00:00
}
col + + ;
}
2019-08-18 15:56:24 +00:00
// Line ends before hit wrap_at; add this word to the substring.
2018-01-26 01:41:17 +00:00
wrap_substring + = word_str ;
lines . push_back ( wrap_substring ) ;
2018-12-18 13:01:15 +00:00
2019-08-18 15:56:24 +00:00
// Update cache.
2018-12-18 13:01:15 +00:00
text . set_line_wrap_amount ( p_line , lines . size ( ) - 1 ) ;
2018-01-26 01:41:17 +00:00
return lines ;
}
int TextEdit : : get_cursor_wrap_index ( ) const {
return get_line_wrap_index_at_col ( cursor . line , cursor . column ) ;
}
int TextEdit : : get_line_wrap_index_at_col ( int p_line , int p_column ) const {
ERR_FAIL_INDEX_V ( p_line , text . size ( ) , 0 ) ;
if ( ! line_wraps ( p_line ) )
return 0 ;
2019-08-18 15:56:24 +00:00
// Loop through wraps in the line text until we get to the column.
2018-01-26 01:41:17 +00:00
int wrap_index = 0 ;
int col = 0 ;
Vector < String > rows = get_wrap_rows_text ( p_line ) ;
for ( int i = 0 ; i < rows . size ( ) ; i + + ) {
wrap_index = i ;
String s = rows [ wrap_index ] ;
col + = s . length ( ) ;
if ( col > p_column )
break ;
}
return wrap_index ;
2016-07-21 01:40:08 +00:00
}
2015-08-12 22:34:07 +00:00
void TextEdit : : cursor_set_column ( int p_col , bool p_adjust_viewport ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
if ( p_col < 0 )
p_col = 0 ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
cursor . column = p_col ;
if ( cursor . column > get_line ( cursor . line ) . length ( ) )
cursor . column = get_line ( cursor . line ) . length ( ) ;
2016-03-08 23:00:52 +00:00
2018-01-26 01:41:17 +00:00
cursor . last_fit_x = get_column_x_offset_for_line ( cursor . column , cursor . line ) ;
2016-03-08 23:00:52 +00:00
2015-08-12 22:34:07 +00:00
if ( p_adjust_viewport )
adjust_viewport_to_cursor ( ) ;
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
if ( ! cursor_changed_dirty ) {
if ( is_inside_tree ( ) )
2017-03-05 15:44:50 +00:00
MessageQueue : : get_singleton ( ) - > push_call ( this , " _cursor_changed_emit " ) ;
cursor_changed_dirty = true ;
2015-01-02 18:08:40 +00:00
}
2014-02-10 01:10:30 +00:00
}
2018-01-26 01:41:17 +00:00
void TextEdit : : cursor_set_line ( int p_row , bool p_adjust_viewport , bool p_can_be_hidden , int p_wrap_index ) {
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
if ( setting_row )
return ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
setting_row = true ;
if ( p_row < 0 )
p_row = 0 ;
2016-03-08 23:00:52 +00:00
2018-01-26 01:41:17 +00:00
if ( p_row > = text . size ( ) )
p_row = text . size ( ) - 1 ;
2016-03-08 23:00:52 +00:00
2017-11-12 23:12:17 +00:00
if ( ! p_can_be_hidden ) {
2017-11-16 04:00:27 +00:00
if ( is_line_hidden ( CLAMP ( p_row , 0 , text . size ( ) - 1 ) ) ) {
int move_down = num_lines_from ( p_row , 1 ) - 1 ;
if ( p_row + move_down < = text . size ( ) - 1 & & ! is_line_hidden ( p_row + move_down ) ) {
p_row + = move_down ;
} else {
int move_up = num_lines_from ( p_row , - 1 ) - 1 ;
if ( p_row - move_up > 0 & & ! is_line_hidden ( p_row - move_up ) ) {
p_row - = move_up ;
} else {
WARN_PRINTS ( ( " Cursor set to hidden line " + itos ( p_row ) + " and there are no nonhidden lines. " ) ) ;
2017-11-12 23:12:17 +00:00
}
}
}
}
2017-03-05 15:44:50 +00:00
cursor . line = p_row ;
2018-01-26 01:41:17 +00:00
int n_col = get_char_pos_for_line ( cursor . last_fit_x , p_row , p_wrap_index ) ;
2019-06-14 20:48:16 +00:00
if ( n_col ! = 0 & & is_wrap_enabled ( ) & & p_wrap_index < times_line_wraps ( p_row ) ) {
2018-01-26 01:41:17 +00:00
Vector < String > rows = get_wrap_rows_text ( p_row ) ;
int row_end_col = 0 ;
for ( int i = 0 ; i < p_wrap_index + 1 ; i + + ) {
row_end_col + = rows [ i ] . length ( ) ;
}
if ( n_col > = row_end_col )
n_col - = 1 ;
}
cursor . column = n_col ;
2016-03-08 23:00:52 +00:00
2015-08-12 22:34:07 +00:00
if ( p_adjust_viewport )
adjust_viewport_to_cursor ( ) ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
setting_row = false ;
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
if ( ! cursor_changed_dirty ) {
if ( is_inside_tree ( ) )
2017-03-05 15:44:50 +00:00
MessageQueue : : get_singleton ( ) - > push_call ( this , " _cursor_changed_emit " ) ;
cursor_changed_dirty = true ;
2015-01-02 18:08:40 +00:00
}
2014-02-10 01:10:30 +00:00
}
int TextEdit : : cursor_get_column ( ) const {
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
return cursor . column ;
2014-02-10 01:10:30 +00:00
}
int TextEdit : : cursor_get_line ( ) const {
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
return cursor . line ;
2014-02-10 01:10:30 +00:00
}
2016-05-09 18:21:55 +00:00
bool TextEdit : : cursor_get_blink_enabled ( ) const {
return caret_blink_enabled ;
}
void TextEdit : : cursor_set_blink_enabled ( const bool p_enabled ) {
caret_blink_enabled = p_enabled ;
if ( p_enabled ) {
caret_blink_timer - > start ( ) ;
} else {
caret_blink_timer - > stop ( ) ;
}
draw_caret = true ;
}
float TextEdit : : cursor_get_blink_speed ( ) const {
return caret_blink_timer - > get_wait_time ( ) ;
}
void TextEdit : : cursor_set_blink_speed ( const float p_speed ) {
ERR_FAIL_COND ( p_speed < = 0 ) ;
caret_blink_timer - > set_wait_time ( p_speed ) ;
}
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
void TextEdit : : cursor_set_block_mode ( const bool p_enable ) {
2016-07-12 15:07:17 +00:00
block_caret = p_enable ;
update ( ) ;
}
bool TextEdit : : cursor_is_block_mode ( ) const {
return block_caret ;
}
2017-12-20 01:36:47 +00:00
void TextEdit : : set_right_click_moves_caret ( bool p_enable ) {
right_click_moves_caret = p_enable ;
}
bool TextEdit : : is_right_click_moving_caret ( ) const {
return right_click_moves_caret ;
}
2017-08-22 19:02:08 +00:00
void TextEdit : : _v_scroll_input ( ) {
scrolling = false ;
2019-08-11 18:31:19 +00:00
minimap_clicked = false ;
2017-08-22 19:02:08 +00:00
}
2014-02-10 01:10:30 +00:00
void TextEdit : : _scroll_moved ( double p_to_val ) {
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
if ( updating_scrolls )
return ;
2016-03-08 23:00:52 +00:00
2017-01-13 13:45:50 +00:00
if ( h_scroll - > is_visible_in_tree ( ) )
2017-03-05 15:44:50 +00:00
cursor . x_ofs = h_scroll - > get_value ( ) ;
2017-11-16 04:00:27 +00:00
if ( v_scroll - > is_visible_in_tree ( ) ) {
2018-01-26 01:41:17 +00:00
2019-08-18 15:56:24 +00:00
// Set line ofs and wrap ofs.
2018-01-26 01:41:17 +00:00
int v_scroll_i = floor ( get_v_scroll ( ) ) ;
int sc = 0 ;
int n_line ;
2019-05-25 15:55:35 +00:00
for ( n_line = 0 ; n_line < text . size ( ) - 1 ; n_line + + ) {
2018-01-26 01:41:17 +00:00
if ( ! is_line_hidden ( n_line ) ) {
sc + + ;
sc + = times_line_wraps ( n_line ) ;
if ( sc > v_scroll_i )
break ;
}
}
int line_wrap_amount = times_line_wraps ( n_line ) ;
int wi = line_wrap_amount - ( sc - v_scroll_i - 1 ) ;
wi = CLAMP ( wi , 0 , line_wrap_amount ) ;
cursor . line_ofs = n_line ;
cursor . wrap_ofs = wi ;
2017-11-16 04:00:27 +00:00
}
2015-01-02 18:08:40 +00:00
update ( ) ;
2014-02-10 01:10:30 +00:00
}
int TextEdit : : get_row_height ( ) const {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
return cache . font - > get_height ( ) + cache . line_spacing ;
2014-02-10 01:10:30 +00:00
}
2018-01-26 01:41:17 +00:00
int TextEdit : : get_char_pos_for_line ( int p_px , int p_line , int p_wrap_index ) const {
2016-03-08 23:00:52 +00:00
2018-01-26 01:41:17 +00:00
ERR_FAIL_INDEX_V ( p_line , text . size ( ) , 0 ) ;
2016-03-08 23:00:52 +00:00
2018-01-26 01:41:17 +00:00
if ( line_wraps ( p_line ) ) {
2016-03-08 23:00:52 +00:00
2018-01-26 01:41:17 +00:00
int line_wrap_amount = times_line_wraps ( p_line ) ;
int wrap_offset_px = get_indent_level ( p_line ) * cache . font - > get_char_size ( ' ' ) . width ;
2019-02-20 13:52:33 +00:00
if ( wrap_offset_px > = wrap_at ) {
wrap_offset_px = 0 ;
}
2018-01-26 01:41:17 +00:00
if ( p_wrap_index > line_wrap_amount )
p_wrap_index = line_wrap_amount ;
if ( p_wrap_index > 0 )
p_px - = wrap_offset_px ;
else
p_wrap_index = 0 ;
Vector < String > rows = get_wrap_rows_text ( p_line ) ;
int c_pos = get_char_pos_for ( p_px , rows [ p_wrap_index ] ) ;
for ( int i = 0 ; i < p_wrap_index ; i + + ) {
String s = rows [ i ] ;
c_pos + = s . length ( ) ;
}
2016-03-08 23:00:52 +00:00
2018-01-26 01:41:17 +00:00
return c_pos ;
} else {
2016-03-08 23:00:52 +00:00
2018-01-26 01:41:17 +00:00
return get_char_pos_for ( p_px , text [ p_line ] ) ;
}
}
2016-03-08 23:00:52 +00:00
2018-01-26 01:41:17 +00:00
int TextEdit : : get_column_x_offset_for_line ( int p_char , int p_line ) const {
2016-03-08 23:00:52 +00:00
2018-01-26 01:41:17 +00:00
ERR_FAIL_INDEX_V ( p_line , text . size ( ) , 0 ) ;
2016-03-08 23:00:52 +00:00
2018-01-26 01:41:17 +00:00
if ( line_wraps ( p_line ) ) {
int n_char = p_char ;
int col = 0 ;
Vector < String > rows = get_wrap_rows_text ( p_line ) ;
int wrap_index = 0 ;
for ( int i = 0 ; i < rows . size ( ) ; i + + ) {
wrap_index = i ;
String s = rows [ wrap_index ] ;
col + = s . length ( ) ;
if ( col > p_char )
break ;
n_char - = s . length ( ) ;
2015-01-02 18:08:40 +00:00
}
2018-01-26 01:41:17 +00:00
int px = get_column_x_offset ( n_char , rows [ wrap_index ] ) ;
int wrap_offset_px = get_indent_level ( p_line ) * cache . font - > get_char_size ( ' ' ) . width ;
2019-02-20 13:52:33 +00:00
if ( wrap_offset_px > = wrap_at ) {
wrap_offset_px = 0 ;
}
2018-01-26 01:41:17 +00:00
if ( wrap_index ! = 0 )
px + = wrap_offset_px ;
return px ;
} else {
return get_column_x_offset ( p_char , text [ p_line ] ) ;
}
}
int TextEdit : : get_char_pos_for ( int p_px , String p_str ) const {
int px = 0 ;
int c = 0 ;
while ( c < p_str . length ( ) ) {
int w = text . get_char_width ( p_str [ c ] , p_str [ c + 1 ] , px ) ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
if ( p_px < ( px + w / 2 ) )
2015-01-02 18:08:40 +00:00
break ;
2017-03-05 15:44:50 +00:00
px + = w ;
2015-01-02 18:08:40 +00:00
c + + ;
}
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
return c ;
2014-02-10 01:10:30 +00:00
}
2018-01-26 01:41:17 +00:00
int TextEdit : : get_column_x_offset ( int p_char , String p_str ) const {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
int px = 0 ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
for ( int i = 0 ; i < p_char ; i + + ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
if ( i > = p_str . length ( ) )
2015-01-02 18:08:40 +00:00
break ;
2016-03-08 23:00:52 +00:00
2018-01-26 01:41:17 +00:00
px + = text . get_char_width ( p_str [ i ] , p_str [ i + 1 ] , px ) ;
2015-01-02 18:08:40 +00:00
}
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
return px ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
void TextEdit : : insert_text_at_cursor ( const String & p_text ) {
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
if ( selection . active ) {
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
cursor_set_line ( selection . from_line ) ;
cursor_set_column ( selection . from_column ) ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
_remove_text ( selection . from_line , selection . from_column , selection . to_line , selection . to_column ) ;
selection . active = false ;
selection . selecting_mode = Selection : : MODE_NONE ;
2015-01-02 18:08:40 +00:00
}
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
_insert_text_at_cursor ( p_text ) ;
update ( ) ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
Control : : CursorShape TextEdit : : get_cursor_shape ( const Point2 & p_pos ) const {
2017-04-06 15:35:08 +00:00
if ( highlighted_word ! = String ( ) )
2016-09-12 13:52:29 +00:00
return CURSOR_POINTING_HAND ;
2019-04-20 11:51:25 +00:00
int gutter = cache . style_normal - > get_margin ( MARGIN_LEFT ) + cache . line_number_w + cache . breakpoint_gutter_width + cache . fold_gutter_width + cache . info_gutter_width ;
2017-11-16 04:00:27 +00:00
if ( ( completion_active & & completion_rect . has_point ( p_pos ) ) ) {
return CURSOR_ARROW ;
}
if ( p_pos . x < gutter ) {
int row , col ;
_get_mouse_pos ( p_pos , row , col ) ;
int left_margin = cache . style_normal - > get_margin ( MARGIN_LEFT ) ;
2019-08-18 15:56:24 +00:00
// Breakpoint icon.
2019-04-20 11:51:25 +00:00
if ( draw_breakpoint_gutter & & p_pos . x > left_margin - 6 & & p_pos . x < = left_margin + cache . breakpoint_gutter_width - 3 ) {
2017-11-16 04:00:27 +00:00
return CURSOR_POINTING_HAND ;
}
2019-08-18 15:56:24 +00:00
// Info icons.
2019-04-20 11:51:25 +00:00
int gutter_left = left_margin + cache . breakpoint_gutter_width + cache . info_gutter_width ;
if ( draw_info_gutter & & p_pos . x > left_margin + cache . breakpoint_gutter_width - 6 & & p_pos . x < = gutter_left - 3 ) {
if ( text . has_info_icon ( row ) ) {
return CURSOR_POINTING_HAND ;
}
return CURSOR_ARROW ;
}
2019-08-18 15:56:24 +00:00
// Fold icon.
2019-04-20 11:51:25 +00:00
if ( draw_fold_gutter & & p_pos . x > gutter_left + cache . line_number_w - 6 & & p_pos . x < = gutter_left + cache . line_number_w + cache . fold_gutter_width - 3 ) {
2017-11-16 04:00:27 +00:00
if ( is_folded ( row ) | | can_fold ( row ) )
return CURSOR_POINTING_HAND ;
else
return CURSOR_ARROW ;
}
2019-04-20 11:51:25 +00:00
2015-01-02 18:08:40 +00:00
return CURSOR_ARROW ;
2017-11-16 04:00:27 +00:00
} else {
2019-08-11 18:31:19 +00:00
int xmargin_end = get_size ( ) . width - cache . style_normal - > get_margin ( MARGIN_RIGHT ) ;
if ( p_pos . x > xmargin_end - minimap_width & & p_pos . x < = xmargin_end ) {
return CURSOR_ARROW ;
}
2017-11-16 04:00:27 +00:00
int row , col ;
_get_mouse_pos ( p_pos , row , col ) ;
2019-08-18 15:56:24 +00:00
// EOL fold icon.
2017-11-16 04:00:27 +00:00
if ( is_folded ( row ) ) {
int line_width = text . get_line_width ( row ) ;
2019-04-20 11:51:25 +00:00
line_width + = cache . style_normal - > get_margin ( MARGIN_LEFT ) + cache . line_number_w + cache . breakpoint_gutter_width + cache . fold_gutter_width + cache . info_gutter_width - cursor . x_ofs ;
2017-11-16 04:00:27 +00:00
if ( p_pos . x > line_width - 3 & & p_pos . x < = line_width + cache . folded_eol_icon - > get_width ( ) + 3 ) {
return CURSOR_POINTING_HAND ;
}
}
2015-01-02 18:08:40 +00:00
}
2017-11-16 04:00:27 +00:00
2018-09-11 16:25:40 +00:00
return get_default_cursor_shape ( ) ;
2014-04-17 11:30:40 +00:00
}
2017-03-05 15:44:50 +00:00
void TextEdit : : set_text ( String p_text ) {
2014-04-17 11:30:40 +00:00
2017-03-05 15:44:50 +00:00
setting_text = true ;
2019-03-09 00:26:49 +00:00
if ( ! undo_enabled ) {
_clear ( ) ;
_insert_text_at_cursor ( p_text ) ;
}
if ( undo_enabled ) {
cursor_set_line ( 0 ) ;
cursor_set_column ( 0 ) ;
begin_complex_operation ( ) ;
_remove_text ( 0 , 0 , MAX ( 0 , get_line_count ( ) - 1 ) , MAX ( get_line ( MAX ( get_line_count ( ) - 1 , 0 ) ) . size ( ) - 1 , 0 ) ) ;
_insert_text_at_cursor ( p_text ) ;
end_complex_operation ( ) ;
selection . active = false ;
}
2015-01-02 18:08:40 +00:00
update ( ) ;
2017-03-05 15:44:50 +00:00
setting_text = false ;
2014-02-10 01:10:30 +00:00
} ;
String TextEdit : : get_text ( ) {
2015-01-02 18:08:40 +00:00
String longthing ;
int len = text . size ( ) ;
2017-03-05 15:44:50 +00:00
for ( int i = 0 ; i < len ; i + + ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
longthing + = text [ i ] ;
if ( i ! = len - 1 )
longthing + = " \n " ;
2014-12-17 01:31:57 +00:00
}
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
return longthing ;
} ;
2014-12-17 01:31:57 +00:00
2016-09-12 13:52:29 +00:00
String TextEdit : : get_text_for_lookup_completion ( ) {
2017-03-05 15:44:50 +00:00
int row , col ;
2017-09-10 13:37:49 +00:00
_get_mouse_pos ( get_local_mouse_position ( ) , row , col ) ;
2016-09-12 13:52:29 +00:00
String longthing ;
int len = text . size ( ) ;
2017-03-05 15:44:50 +00:00
for ( int i = 0 ; i < len ; i + + ) {
2016-09-12 13:52:29 +00:00
2017-03-05 15:44:50 +00:00
if ( i = = row ) {
longthing + = text [ i ] . substr ( 0 , col ) ;
2019-08-18 15:56:24 +00:00
longthing + = String : : chr ( 0xFFFF ) ; // Not unicode, represents the cursor.
2017-03-05 15:44:50 +00:00
longthing + = text [ i ] . substr ( col , text [ i ] . size ( ) ) ;
2016-09-12 13:52:29 +00:00
} else {
2017-03-05 15:44:50 +00:00
longthing + = text [ i ] ;
2016-09-12 13:52:29 +00:00
}
2017-03-05 15:44:50 +00:00
if ( i ! = len - 1 )
longthing + = " \n " ;
2016-09-12 13:52:29 +00:00
}
return longthing ;
}
2015-01-02 18:08:40 +00:00
String TextEdit : : get_text_for_completion ( ) {
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
String longthing ;
int len = text . size ( ) ;
2017-03-05 15:44:50 +00:00
for ( int i = 0 ; i < len ; i + + ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
if ( i = = cursor . line ) {
longthing + = text [ i ] . substr ( 0 , cursor . column ) ;
2019-08-18 15:56:24 +00:00
longthing + = String : : chr ( 0xFFFF ) ; // Not unicode, represents the cursor.
2017-03-05 15:44:50 +00:00
longthing + = text [ i ] . substr ( cursor . column , text [ i ] . size ( ) ) ;
2015-01-02 18:08:40 +00:00
} else {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
longthing + = text [ i ] ;
2015-01-02 18:08:40 +00:00
}
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
if ( i ! = len - 1 )
longthing + = " \n " ;
2015-01-02 18:08:40 +00:00
}
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
return longthing ;
2014-12-17 01:31:57 +00:00
} ;
2014-02-10 01:10:30 +00:00
String TextEdit : : get_line ( int line ) const {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
if ( line < 0 | | line > = text . size ( ) )
2015-01-02 18:08:40 +00:00
return " " ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
return text [ line ] ;
2014-02-10 01:10:30 +00:00
} ;
void TextEdit : : _clear ( ) {
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
clear_undo_history ( ) ;
text . clear ( ) ;
2017-03-05 15:44:50 +00:00
cursor . column = 0 ;
cursor . line = 0 ;
cursor . x_ofs = 0 ;
cursor . line_ofs = 0 ;
2018-01-26 01:41:17 +00:00
cursor . wrap_ofs = 0 ;
2017-03-05 15:44:50 +00:00
cursor . last_fit_x = 0 ;
2018-11-25 17:24:49 +00:00
selection . active = false ;
2014-02-10 01:10:30 +00:00
}
void TextEdit : : clear ( ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
setting_text = true ;
2015-01-02 18:08:40 +00:00
_clear ( ) ;
2017-03-05 15:44:50 +00:00
setting_text = false ;
2014-02-10 01:10:30 +00:00
} ;
void TextEdit : : set_readonly ( bool p_readonly ) {
2016-03-08 23:00:52 +00:00
2019-04-21 23:09:52 +00:00
if ( readonly = = p_readonly )
return ;
2017-03-05 15:44:50 +00:00
readonly = p_readonly ;
2019-04-21 23:09:52 +00:00
// Reorganize context menu.
menu - > clear ( ) ;
2019-08-12 20:23:00 +00:00
if ( ! readonly ) {
menu - > add_item ( RTR ( " Undo " ) , MENU_UNDO , KEY_MASK_CMD | KEY_Z ) ;
menu - > add_item ( RTR ( " Redo " ) , MENU_REDO , KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_Z ) ;
}
if ( ! readonly ) {
menu - > add_separator ( ) ;
2019-04-21 23:09:52 +00:00
menu - > add_item ( RTR ( " Cut " ) , MENU_CUT , KEY_MASK_CMD | KEY_X ) ;
2019-08-12 20:23:00 +00:00
}
2019-04-21 23:09:52 +00:00
menu - > add_item ( RTR ( " Copy " ) , MENU_COPY , KEY_MASK_CMD | KEY_C ) ;
2019-08-12 20:23:00 +00:00
if ( ! readonly ) {
2019-04-21 23:09:52 +00:00
menu - > add_item ( RTR ( " Paste " ) , MENU_PASTE , KEY_MASK_CMD | KEY_V ) ;
2019-08-12 20:23:00 +00:00
}
2019-04-21 23:09:52 +00:00
menu - > add_separator ( ) ;
menu - > add_item ( RTR ( " Select All " ) , MENU_SELECT_ALL , KEY_MASK_CMD | KEY_A ) ;
2019-08-12 20:23:00 +00:00
2019-04-21 23:09:52 +00:00
if ( ! readonly ) {
menu - > add_item ( RTR ( " Clear " ) , MENU_CLEAR ) ;
}
2017-11-30 03:53:15 +00:00
update ( ) ;
2014-02-10 01:10:30 +00:00
}
2017-11-11 18:07:17 +00:00
bool TextEdit : : is_readonly ( ) const {
return readonly ;
}
2018-01-26 01:41:17 +00:00
void TextEdit : : set_wrap_enabled ( bool p_wrap_enabled ) {
2016-03-08 23:00:52 +00:00
2018-01-26 01:41:17 +00:00
wrap_enabled = p_wrap_enabled ;
2014-02-10 01:10:30 +00:00
}
2018-01-26 01:41:17 +00:00
bool TextEdit : : is_wrap_enabled ( ) const {
2018-01-11 22:35:12 +00:00
2018-01-26 01:41:17 +00:00
return wrap_enabled ;
2018-01-11 22:35:12 +00:00
}
2014-02-10 01:10:30 +00:00
void TextEdit : : set_max_chars ( int p_max_chars ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
max_chars = p_max_chars ;
2014-02-10 01:10:30 +00:00
}
2018-01-11 22:35:12 +00:00
int TextEdit : : get_max_chars ( ) const {
return max_chars ;
}
2016-05-09 18:21:55 +00:00
void TextEdit : : _reset_caret_blink_timer ( ) {
if ( caret_blink_enabled ) {
caret_blink_timer - > stop ( ) ;
caret_blink_timer - > start ( ) ;
draw_caret = true ;
update ( ) ;
}
}
void TextEdit : : _toggle_draw_caret ( ) {
draw_caret = ! draw_caret ;
2017-01-13 13:45:50 +00:00
if ( is_visible_in_tree ( ) & & has_focus ( ) & & window_has_focus ) {
2016-06-08 13:02:39 +00:00
update ( ) ;
}
2016-05-09 18:21:55 +00:00
}
2014-02-10 01:10:30 +00:00
void TextEdit : : _update_caches ( ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
cache . style_normal = get_stylebox ( " normal " ) ;
cache . style_focus = get_stylebox ( " focus " ) ;
2017-11-30 03:53:15 +00:00
cache . style_readonly = get_stylebox ( " read_only " ) ;
2017-03-05 15:44:50 +00:00
cache . completion_background_color = get_color ( " completion_background_color " ) ;
cache . completion_selected_color = get_color ( " completion_selected_color " ) ;
cache . completion_existing_color = get_color ( " completion_existing_color " ) ;
cache . completion_font_color = get_color ( " completion_font_color " ) ;
cache . font = get_font ( " font " ) ;
cache . caret_color = get_color ( " caret_color " ) ;
cache . caret_background_color = get_color ( " caret_background_color " ) ;
cache . line_number_color = get_color ( " line_number_color " ) ;
2018-06-05 16:50:21 +00:00
cache . safe_line_number_color = get_color ( " safe_line_number_color " ) ;
2017-03-05 15:44:50 +00:00
cache . font_color = get_color ( " font_color " ) ;
2019-06-24 05:17:27 +00:00
cache . font_color_selected = get_color ( " font_color_selected " ) ;
2019-06-24 03:43:52 +00:00
cache . font_color_readonly = get_color ( " font_color_readonly " ) ;
2017-03-05 15:44:50 +00:00
cache . keyword_color = get_color ( " keyword_color " ) ;
cache . function_color = get_color ( " function_color " ) ;
cache . member_variable_color = get_color ( " member_variable_color " ) ;
cache . number_color = get_color ( " number_color " ) ;
cache . selection_color = get_color ( " selection_color " ) ;
cache . mark_color = get_color ( " mark_color " ) ;
cache . current_line_color = get_color ( " current_line_color " ) ;
cache . line_length_guideline_color = get_color ( " line_length_guideline_color " ) ;
2019-04-19 23:51:25 +00:00
cache . bookmark_color = get_color ( " bookmark_color " ) ;
2017-03-05 15:44:50 +00:00
cache . breakpoint_color = get_color ( " breakpoint_color " ) ;
2019-04-22 16:20:27 +00:00
cache . executing_line_color = get_color ( " executing_line_color " ) ;
2017-12-04 21:48:20 +00:00
cache . code_folding_color = get_color ( " code_folding_color " ) ;
2017-03-05 15:44:50 +00:00
cache . brace_mismatch_color = get_color ( " brace_mismatch_color " ) ;
cache . word_highlighted_color = get_color ( " word_highlighted_color " ) ;
cache . search_result_color = get_color ( " search_result_color " ) ;
cache . search_result_border_color = get_color ( " search_result_border_color " ) ;
cache . symbol_color = get_color ( " symbol_color " ) ;
cache . background_color = get_color ( " background_color " ) ;
2018-07-26 21:50:16 +00:00
# ifdef TOOLS_ENABLED
cache . line_spacing = get_constant ( " line_spacing " ) * EDSCALE ;
# else
2017-03-05 15:44:50 +00:00
cache . line_spacing = get_constant ( " line_spacing " ) ;
2018-07-26 21:50:16 +00:00
# endif
2015-01-02 18:08:40 +00:00
cache . row_height = cache . font - > get_height ( ) + cache . line_spacing ;
2017-03-05 15:44:50 +00:00
cache . tab_icon = get_icon ( " tab " ) ;
2019-04-23 23:33:20 +00:00
cache . space_icon = get_icon ( " space " ) ;
2019-04-24 07:59:17 +00:00
cache . folded_icon = get_icon ( " folded " ) ;
cache . can_fold_icon = get_icon ( " fold " ) ;
2017-11-20 19:03:22 +00:00
cache . folded_eol_icon = get_icon ( " GuiEllipsis " , " EditorIcons " ) ;
2019-04-22 16:20:27 +00:00
cache . executing_icon = get_icon ( " MainPlay " , " EditorIcons " ) ;
2015-01-02 18:08:40 +00:00
text . set_font ( cache . font ) ;
2018-04-02 11:41:44 +00:00
if ( syntax_highlighter ) {
syntax_highlighter - > _update_cache ( ) ;
}
}
SyntaxHighlighter * TextEdit : : _get_syntax_highlighting ( ) {
return syntax_highlighter ;
}
void TextEdit : : _set_syntax_highlighting ( SyntaxHighlighter * p_syntax_highlighter ) {
syntax_highlighter = p_syntax_highlighter ;
if ( syntax_highlighter ) {
syntax_highlighter - > set_text_editor ( this ) ;
syntax_highlighter - > _update_cache ( ) ;
}
2019-08-11 18:29:09 +00:00
syntax_highlighting_cache . clear ( ) ;
2018-04-02 11:41:44 +00:00
update ( ) ;
}
2018-04-07 13:14:19 +00:00
int TextEdit : : _is_line_in_region ( int p_line ) {
2019-08-18 15:56:24 +00:00
// Do we have in cache?
2018-04-07 13:14:19 +00:00
if ( color_region_cache . has ( p_line ) ) {
return color_region_cache [ p_line ] ;
}
2019-08-18 15:56:24 +00:00
// If not find the closest line we have.
2018-04-07 13:14:19 +00:00
int previous_line = p_line - 1 ;
2018-09-27 10:07:59 +00:00
for ( ; previous_line > - 1 ; previous_line - - ) {
2018-04-07 13:14:19 +00:00
if ( color_region_cache . has ( p_line ) ) {
break ;
}
2018-04-02 11:41:44 +00:00
}
2018-04-07 13:14:19 +00:00
2019-08-18 15:56:24 +00:00
// Calculate up to line we need and update the cache along the way.
2018-04-07 13:14:19 +00:00
int in_region = color_region_cache [ previous_line ] ;
2018-04-10 19:42:28 +00:00
if ( previous_line = = - 1 ) {
in_region = - 1 ;
}
2018-04-07 13:14:19 +00:00
for ( int i = previous_line ; i < p_line ; i + + ) {
const Map < int , Text : : ColorRegionInfo > & cri_map = _get_line_color_region_info ( i ) ;
for ( const Map < int , Text : : ColorRegionInfo > : : Element * E = cri_map . front ( ) ; E ; E = E - > next ( ) ) {
const Text : : ColorRegionInfo & cri = E - > get ( ) ;
if ( in_region = = - 1 ) {
if ( ! cri . end ) {
in_region = cri . region ;
}
} else if ( in_region = = cri . region & & ! _get_color_region ( cri . region ) . line_only ) {
if ( cri . end | | _get_color_region ( cri . region ) . eq ) {
in_region = - 1 ;
}
}
}
if ( in_region > = 0 & & _get_color_region ( in_region ) . line_only ) {
in_region = - 1 ;
}
color_region_cache [ i + 1 ] = in_region ;
}
return in_region ;
2018-04-02 11:41:44 +00:00
}
TextEdit : : ColorRegion TextEdit : : _get_color_region ( int p_region ) const {
2018-04-07 13:14:19 +00:00
if ( p_region < 0 | | p_region > = color_regions . size ( ) ) {
2018-04-02 11:41:44 +00:00
return ColorRegion ( ) ;
}
return color_regions [ p_region ] ;
}
Map < int , TextEdit : : Text : : ColorRegionInfo > TextEdit : : _get_line_color_region_info ( int p_line ) const {
if ( p_line < 0 | | p_line > text . size ( ) - 1 ) {
return Map < int , Text : : ColorRegionInfo > ( ) ;
}
return text . get_color_region_info ( p_line ) ;
2014-02-10 01:10:30 +00:00
}
void TextEdit : : clear_colors ( ) {
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
keywords . clear ( ) ;
2016-11-06 13:50:23 +00:00
color_regions . clear ( ) ;
2018-04-07 13:14:19 +00:00
color_region_cache . clear ( ) ;
2019-08-11 18:29:09 +00:00
syntax_highlighting_cache . clear ( ) ;
2018-01-26 01:41:17 +00:00
text . clear_width_cache ( ) ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
void TextEdit : : add_keyword_color ( const String & p_keyword , const Color & p_color ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
keywords [ p_keyword ] = p_color ;
2015-01-02 18:08:40 +00:00
update ( ) ;
2014-02-10 01:10:30 +00:00
}
2018-04-02 11:41:44 +00:00
bool TextEdit : : has_keyword_color ( String p_keyword ) const {
return keywords . has ( p_keyword ) ;
}
Color TextEdit : : get_keyword_color ( String p_keyword ) const {
2019-06-06 10:59:29 +00:00
2019-06-11 12:49:34 +00:00
ERR_FAIL_COND_V ( ! keywords . has ( p_keyword ) , Color ( ) ) ;
2018-04-02 11:41:44 +00:00
return keywords [ p_keyword ] ;
}
2017-03-05 15:44:50 +00:00
void TextEdit : : add_color_region ( const String & p_begin_key , const String & p_end_key , const Color & p_color , bool p_line_only ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
color_regions . push_back ( ColorRegion ( p_begin_key , p_end_key , p_color , p_line_only ) ) ;
2018-01-26 01:41:17 +00:00
text . clear_width_cache ( ) ;
2015-01-02 18:08:40 +00:00
update ( ) ;
2014-02-10 01:10:30 +00:00
}
2018-01-12 14:00:41 +00:00
void TextEdit : : add_member_keyword ( const String & p_keyword , const Color & p_color ) {
member_keywords [ p_keyword ] = p_color ;
update ( ) ;
}
2018-04-02 11:41:44 +00:00
bool TextEdit : : has_member_color ( String p_member ) const {
return member_keywords . has ( p_member ) ;
}
Color TextEdit : : get_member_color ( String p_member ) const {
return member_keywords [ p_member ] ;
}
2018-01-12 14:00:41 +00:00
void TextEdit : : clear_member_keywords ( ) {
member_keywords . clear ( ) ;
update ( ) ;
}
2014-02-10 01:10:30 +00:00
void TextEdit : : set_syntax_coloring ( bool p_enabled ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
syntax_coloring = p_enabled ;
2015-01-02 18:08:40 +00:00
update ( ) ;
2014-02-10 01:10:30 +00:00
}
bool TextEdit : : is_syntax_coloring_enabled ( ) const {
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
return syntax_coloring ;
2014-02-10 01:10:30 +00:00
}
2015-11-29 16:02:35 +00:00
void TextEdit : : set_auto_indent ( bool p_auto_indent ) {
auto_indent = p_auto_indent ;
}
2014-02-10 01:10:30 +00:00
void TextEdit : : cut ( ) {
2016-03-08 23:00:52 +00:00
2016-03-15 12:02:38 +00:00
if ( ! selection . active ) {
2016-03-08 23:00:52 +00:00
2016-03-15 12:02:38 +00:00
String clipboard = text [ cursor . line ] ;
OS : : get_singleton ( ) - > set_clipboard ( clipboard ) ;
cursor_set_line ( cursor . line ) ;
cursor_set_column ( 0 ) ;
2017-03-05 15:44:50 +00:00
_remove_text ( cursor . line , 0 , cursor . line , text [ cursor . line ] . length ( ) ) ;
2016-03-08 23:00:52 +00:00
2016-03-15 12:02:38 +00:00
backspace_at_cursor ( ) ;
update ( ) ;
2017-03-05 15:44:50 +00:00
cursor_set_line ( cursor . line + 1 ) ;
2017-12-17 15:24:23 +00:00
cut_copy_line = clipboard ;
2016-03-08 23:00:52 +00:00
2016-03-15 12:02:38 +00:00
} else {
2017-03-05 15:44:50 +00:00
String clipboard = _base_get_text ( selection . from_line , selection . from_column , selection . to_line , selection . to_column ) ;
2016-03-15 12:02:38 +00:00
OS : : get_singleton ( ) - > set_clipboard ( clipboard ) ;
2017-08-27 17:07:28 +00:00
_remove_text ( selection . from_line , selection . from_column , selection . to_line , selection . to_column ) ;
2019-08-18 15:56:24 +00:00
cursor_set_line ( selection . from_line ) ; // Set afterwards else it causes the view to be offset.
2016-03-15 12:02:38 +00:00
cursor_set_column ( selection . from_column ) ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
selection . active = false ;
selection . selecting_mode = Selection : : MODE_NONE ;
2016-03-15 12:02:38 +00:00
update ( ) ;
2017-12-17 15:24:23 +00:00
cut_copy_line = " " ;
2016-03-15 12:02:38 +00:00
}
2014-02-10 01:10:30 +00:00
}
void TextEdit : : copy ( ) {
2016-03-08 23:00:52 +00:00
2016-03-15 12:02:38 +00:00
if ( ! selection . active ) {
2018-09-30 15:17:29 +00:00
if ( text [ cursor . line ] . length ( ) ! = 0 ) {
String clipboard = _base_get_text ( cursor . line , 0 , cursor . line , text [ cursor . line ] . length ( ) ) ;
OS : : get_singleton ( ) - > set_clipboard ( clipboard ) ;
cut_copy_line = clipboard ;
}
2016-03-15 12:02:38 +00:00
} else {
2017-03-05 15:44:50 +00:00
String clipboard = _base_get_text ( selection . from_line , selection . from_column , selection . to_line , selection . to_column ) ;
2016-03-15 12:02:38 +00:00
OS : : get_singleton ( ) - > set_clipboard ( clipboard ) ;
2017-12-17 15:24:23 +00:00
cut_copy_line = " " ;
2016-03-15 12:02:38 +00:00
}
2014-02-10 01:10:30 +00:00
}
2016-03-15 12:02:38 +00:00
2014-02-10 01:10:30 +00:00
void TextEdit : : paste ( ) {
2016-03-08 23:00:52 +00:00
2016-03-15 12:02:38 +00:00
String clipboard = OS : : get_singleton ( ) - > get_clipboard ( ) ;
2018-05-09 12:07:06 +00:00
begin_complex_operation ( ) ;
2015-01-02 18:08:40 +00:00
if ( selection . active ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
selection . active = false ;
selection . selecting_mode = Selection : : MODE_NONE ;
_remove_text ( selection . from_line , selection . from_column , selection . to_line , selection . to_column ) ;
2015-01-02 18:08:40 +00:00
cursor_set_line ( selection . from_line ) ;
cursor_set_column ( selection . from_column ) ;
2016-03-08 23:00:52 +00:00
2017-12-17 15:24:23 +00:00
} else if ( ! cut_copy_line . empty ( ) & & cut_copy_line = = clipboard ) {
2016-03-08 23:00:52 +00:00
2016-03-15 12:02:38 +00:00
cursor_set_column ( 0 ) ;
2017-03-05 15:44:50 +00:00
String ins = " \n " ;
2016-03-15 12:02:38 +00:00
clipboard + = ins ;
2015-01-02 18:08:40 +00:00
}
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
_insert_text_at_cursor ( clipboard ) ;
2018-05-09 12:07:06 +00:00
end_complex_operation ( ) ;
2015-01-02 18:08:40 +00:00
update ( ) ;
2014-02-10 01:10:30 +00:00
}
void TextEdit : : select_all ( ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
if ( text . size ( ) = = 1 & & text [ 0 ] . length ( ) = = 0 )
2015-01-02 18:08:40 +00:00
return ;
2017-03-05 15:44:50 +00:00
selection . active = true ;
selection . from_line = 0 ;
selection . from_column = 0 ;
selection . selecting_line = 0 ;
selection . selecting_column = 0 ;
selection . to_line = text . size ( ) - 1 ;
selection . to_column = text [ selection . to_line ] . length ( ) ;
selection . selecting_mode = Selection : : MODE_SHIFT ;
selection . shiftclick_left = true ;
cursor_set_line ( selection . to_line , false ) ;
cursor_set_column ( selection . to_column , false ) ;
2015-01-02 18:08:40 +00:00
update ( ) ;
2014-02-10 01:10:30 +00:00
}
void TextEdit : : deselect ( ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
selection . active = false ;
2015-01-02 18:08:40 +00:00
update ( ) ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
void TextEdit : : select ( int p_from_line , int p_from_column , int p_to_line , int p_to_column ) {
2016-03-08 23:00:52 +00:00
2019-06-20 15:13:16 +00:00
if ( p_from_line < 0 )
p_from_line = 0 ;
else if ( p_from_line > = text . size ( ) )
2017-03-05 15:44:50 +00:00
p_from_line = text . size ( ) - 1 ;
if ( p_from_column > = text [ p_from_line ] . length ( ) )
p_from_column = text [ p_from_line ] . length ( ) ;
2017-12-26 20:13:17 +00:00
if ( p_from_column < 0 )
p_from_column = 0 ;
2016-03-08 23:00:52 +00:00
2019-06-20 15:13:16 +00:00
if ( p_to_line < 0 )
p_to_line = 0 ;
else if ( p_to_line > = text . size ( ) )
2017-03-05 15:44:50 +00:00
p_to_line = text . size ( ) - 1 ;
if ( p_to_column > = text [ p_to_line ] . length ( ) )
p_to_column = text [ p_to_line ] . length ( ) ;
2017-12-26 20:13:17 +00:00
if ( p_to_column < 0 )
p_to_column = 0 ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
selection . from_line = p_from_line ;
selection . from_column = p_from_column ;
selection . to_line = p_to_line ;
selection . to_column = p_to_column ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
selection . active = true ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
if ( selection . from_line = = selection . to_line ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
if ( selection . from_column = = selection . to_column ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
selection . active = false ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
} else if ( selection . from_column > selection . to_column ) {
2016-03-08 23:00:52 +00:00
2015-08-12 22:34:07 +00:00
selection . shiftclick_left = false ;
2017-03-05 15:44:50 +00:00
SWAP ( selection . from_column , selection . to_column ) ;
2015-08-12 22:34:07 +00:00
} else {
selection . shiftclick_left = true ;
2015-01-02 18:08:40 +00:00
}
2017-03-05 15:44:50 +00:00
} else if ( selection . from_line > selection . to_line ) {
2016-03-08 23:00:52 +00:00
2015-08-12 22:34:07 +00:00
selection . shiftclick_left = false ;
2017-03-05 15:44:50 +00:00
SWAP ( selection . from_line , selection . to_line ) ;
SWAP ( selection . from_column , selection . to_column ) ;
2015-08-12 22:34:07 +00:00
} else {
selection . shiftclick_left = true ;
2015-01-02 18:08:40 +00:00
}
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
update ( ) ;
2014-02-10 01:10:30 +00:00
}
2017-11-11 04:07:41 +00:00
void TextEdit : : swap_lines ( int line1 , int line2 ) {
String tmp = get_line ( line1 ) ;
String tmp2 = get_line ( line2 ) ;
set_line ( line2 , tmp ) ;
set_line ( line1 , tmp2 ) ;
}
2014-02-10 01:10:30 +00:00
bool TextEdit : : is_selection_active ( ) const {
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
return selection . active ;
2014-02-10 01:10:30 +00:00
}
int TextEdit : : get_selection_from_line ( ) const {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
ERR_FAIL_COND_V ( ! selection . active , - 1 ) ;
2015-01-02 18:08:40 +00:00
return selection . from_line ;
2014-02-10 01:10:30 +00:00
}
int TextEdit : : get_selection_from_column ( ) const {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
ERR_FAIL_COND_V ( ! selection . active , - 1 ) ;
2015-01-02 18:08:40 +00:00
return selection . from_column ;
2014-02-10 01:10:30 +00:00
}
int TextEdit : : get_selection_to_line ( ) const {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
ERR_FAIL_COND_V ( ! selection . active , - 1 ) ;
2015-01-02 18:08:40 +00:00
return selection . to_line ;
2014-02-10 01:10:30 +00:00
}
int TextEdit : : get_selection_to_column ( ) const {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
ERR_FAIL_COND_V ( ! selection . active , - 1 ) ;
2015-01-02 18:08:40 +00:00
return selection . to_column ;
2014-02-10 01:10:30 +00:00
}
String TextEdit : : get_selection_text ( ) const {
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
if ( ! selection . active )
return " " ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
return _base_get_text ( selection . from_line , selection . from_column , selection . to_line , selection . to_column ) ;
2014-02-10 01:10:30 +00:00
}
2014-05-06 09:36:39 +00:00
String TextEdit : : get_word_under_cursor ( ) const {
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
int prev_cc = cursor . column ;
2017-03-05 15:44:50 +00:00
while ( prev_cc > 0 ) {
bool is_char = _is_text_char ( text [ cursor . line ] [ prev_cc - 1 ] ) ;
2015-01-02 18:08:40 +00:00
if ( ! is_char )
break ;
- - prev_cc ;
}
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
int next_cc = cursor . column ;
2017-03-05 15:44:50 +00:00
while ( next_cc < text [ cursor . line ] . length ( ) ) {
2015-01-02 18:08:40 +00:00
bool is_char = _is_text_char ( text [ cursor . line ] [ next_cc ] ) ;
2017-03-05 15:44:50 +00:00
if ( ! is_char )
2015-01-02 18:08:40 +00:00
break ;
2017-03-05 15:44:50 +00:00
+ + next_cc ;
2015-01-02 18:08:40 +00:00
}
if ( prev_cc = = cursor . column | | next_cc = = cursor . column )
return " " ;
2017-03-05 15:44:50 +00:00
return text [ cursor . line ] . substr ( prev_cc , next_cc - prev_cc ) ;
2014-05-06 09:36:39 +00:00
}
2014-02-10 01:10:30 +00:00
2016-05-28 16:25:45 +00:00
void TextEdit : : set_search_text ( const String & p_search_text ) {
search_text = p_search_text ;
}
void TextEdit : : set_search_flags ( uint32_t p_flags ) {
search_flags = p_flags ;
}
void TextEdit : : set_current_search_result ( int line , int col ) {
search_result_line = line ;
search_result_col = col ;
2016-05-30 16:15:41 +00:00
update ( ) ;
2016-05-28 16:25:45 +00:00
}
2016-03-16 21:20:42 +00:00
void TextEdit : : set_highlight_all_occurrences ( const bool p_enabled ) {
highlight_all_occurrences = p_enabled ;
update ( ) ;
}
2016-07-11 14:20:01 +00:00
bool TextEdit : : is_highlight_all_occurrences_enabled ( ) const {
return highlight_all_occurrences ;
}
2016-05-28 16:25:45 +00:00
int TextEdit : : _get_column_pos_of_word ( const String & p_key , const String & p_search , uint32_t p_search_flags , int p_from_column ) {
2016-03-16 21:20:42 +00:00
int col = - 1 ;
if ( p_key . length ( ) > 0 & & p_search . length ( ) > 0 ) {
if ( p_from_column < 0 | | p_from_column > p_search . length ( ) ) {
p_from_column = 0 ;
}
2016-05-28 16:25:45 +00:00
while ( col = = - 1 & & p_from_column < = p_search . length ( ) ) {
2017-03-05 15:44:50 +00:00
if ( p_search_flags & SEARCH_MATCH_CASE ) {
col = p_search . find ( p_key , p_from_column ) ;
2016-05-28 16:25:45 +00:00
} else {
2017-03-05 15:44:50 +00:00
col = p_search . findn ( p_key , p_from_column ) ;
2016-05-28 16:25:45 +00:00
}
2016-03-17 20:35:04 +00:00
2019-08-18 15:56:24 +00:00
// Whole words only.
2017-03-05 15:44:50 +00:00
if ( col ! = - 1 & & p_search_flags & SEARCH_WHOLE_WORDS ) {
p_from_column = col ;
2016-03-17 20:35:04 +00:00
2017-03-05 15:44:50 +00:00
if ( col > 0 & & _is_text_char ( p_search [ col - 1 ] ) ) {
2016-03-17 20:35:04 +00:00
col = - 1 ;
2017-03-05 15:44:50 +00:00
} else if ( ( col + p_key . length ( ) ) < p_search . length ( ) & & _is_text_char ( p_search [ col + p_key . length ( ) ] ) ) {
2016-03-17 20:35:04 +00:00
col = - 1 ;
}
2016-03-16 21:20:42 +00:00
}
2016-03-17 20:35:04 +00:00
2017-03-05 15:44:50 +00:00
p_from_column + = 1 ;
2016-03-16 21:20:42 +00:00
}
}
return col ;
}
2017-03-05 15:44:50 +00:00
PoolVector < int > TextEdit : : _search_bind ( const String & p_key , uint32_t p_search_flags , int p_from_line , int p_from_column ) const {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
int col , line ;
if ( search ( p_key , p_search_flags , p_from_line , p_from_column , col , line ) ) {
2017-01-07 21:25:37 +00:00
PoolVector < int > result ;
2015-01-02 18:08:40 +00:00
result . resize ( 2 ) ;
2017-03-05 15:44:50 +00:00
result . set ( 0 , line ) ;
result . set ( 1 , col ) ;
2015-01-02 18:08:40 +00:00
return result ;
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
} else {
2016-03-08 23:00:52 +00:00
2017-01-07 21:25:37 +00:00
return PoolVector < int > ( ) ;
2015-01-02 18:08:40 +00:00
}
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
bool TextEdit : : search ( const String & p_key , uint32_t p_search_flags , int p_from_line , int p_from_column , int & r_line , int & r_column ) const {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
if ( p_key . length ( ) = = 0 )
2015-01-02 18:08:40 +00:00
return false ;
2017-03-05 15:44:50 +00:00
ERR_FAIL_INDEX_V ( p_from_line , text . size ( ) , false ) ;
ERR_FAIL_INDEX_V ( p_from_column , text [ p_from_line ] . length ( ) + 1 , false ) ;
2016-03-08 23:00:52 +00:00
2019-08-18 15:56:24 +00:00
// Search through the whole document, but start by current line.
2016-03-08 23:00:52 +00:00
2017-08-21 19:15:36 +00:00
int line = p_from_line ;
2017-03-05 15:44:50 +00:00
int pos = - 1 ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
for ( int i = 0 ; i < text . size ( ) + 1 ; i + + ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
if ( line < 0 ) {
line = text . size ( ) - 1 ;
2015-01-02 18:08:40 +00:00
}
2017-03-05 15:44:50 +00:00
if ( line = = text . size ( ) ) {
line = 0 ;
2015-01-02 18:08:40 +00:00
}
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
String text_line = text [ line ] ;
2017-03-05 15:44:50 +00:00
int from_column = 0 ;
if ( line = = p_from_line ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
if ( i = = text . size ( ) ) {
2019-08-18 15:56:24 +00:00
// Wrapped.
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
if ( p_search_flags & SEARCH_BACKWARDS ) {
from_column = text_line . length ( ) ;
2015-01-02 18:08:40 +00:00
} else {
2017-03-05 15:44:50 +00:00
from_column = 0 ;
2015-01-02 18:08:40 +00:00
}
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
} else {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
from_column = p_from_column ;
2015-01-02 18:08:40 +00:00
}
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
} else {
2017-03-05 15:44:50 +00:00
if ( p_search_flags & SEARCH_BACKWARDS )
from_column = text_line . length ( ) - 1 ;
2015-01-02 18:08:40 +00:00
else
2017-03-05 15:44:50 +00:00
from_column = 0 ;
2015-01-02 18:08:40 +00:00
}
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
pos = - 1 ;
2016-03-08 23:00:52 +00:00
2018-08-27 17:01:12 +00:00
int pos_from = ( p_search_flags & SEARCH_BACKWARDS ) ? text_line . length ( ) : 0 ;
2017-03-05 15:44:50 +00:00
int last_pos = - 1 ;
2016-03-08 23:00:52 +00:00
2018-01-08 21:50:33 +00:00
while ( true ) {
2016-05-28 16:23:49 +00:00
2018-08-27 17:01:12 +00:00
if ( p_search_flags & SEARCH_BACKWARDS ) {
while ( ( last_pos = ( p_search_flags & SEARCH_MATCH_CASE ) ? text_line . rfind ( p_key , pos_from ) : text_line . rfindn ( p_key , pos_from ) ) ! = - 1 ) {
if ( last_pos < = from_column ) {
pos = last_pos ;
2018-01-08 21:50:33 +00:00
break ;
2018-08-27 17:01:12 +00:00
}
pos_from = last_pos - p_key . length ( ) ;
}
} else {
while ( ( last_pos = ( p_search_flags & SEARCH_MATCH_CASE ) ? text_line . find ( p_key , pos_from ) : text_line . findn ( p_key , pos_from ) ) ! = - 1 ) {
2018-01-08 21:50:33 +00:00
if ( last_pos > = from_column ) {
pos = last_pos ;
break ;
}
2018-08-27 17:01:12 +00:00
pos_from = last_pos + p_key . length ( ) ;
2016-05-28 16:23:49 +00:00
}
}
2016-03-08 23:00:52 +00:00
2018-01-08 21:50:33 +00:00
bool is_match = true ;
if ( pos ! = - 1 & & ( p_search_flags & SEARCH_WHOLE_WORDS ) ) {
2019-08-18 15:56:24 +00:00
// Validate for whole words.
2018-01-08 21:50:33 +00:00
if ( pos > 0 & & _is_text_char ( text_line [ pos - 1 ] ) )
is_match = false ;
else if ( pos + p_key . length ( ) < text_line . length ( ) & & _is_text_char ( text_line [ pos + p_key . length ( ) ] ) )
is_match = false ;
}
2018-08-27 17:01:12 +00:00
if ( pos_from = = - 1 ) {
pos = - 1 ;
}
2018-01-08 21:50:33 +00:00
if ( is_match | | last_pos = = - 1 | | pos = = - 1 ) {
break ;
}
2016-03-08 23:00:52 +00:00
2018-08-27 17:01:12 +00:00
pos_from = ( p_search_flags & SEARCH_BACKWARDS ) ? pos - 1 : pos + 1 ;
2018-01-08 21:50:33 +00:00
pos = - 1 ;
2015-01-02 18:08:40 +00:00
}
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
if ( pos ! = - 1 )
2015-01-02 18:08:40 +00:00
break ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
if ( p_search_flags & SEARCH_BACKWARDS )
2015-01-02 18:08:40 +00:00
line - - ;
else
line + + ;
}
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
if ( pos = = - 1 ) {
r_line = - 1 ;
r_column = - 1 ;
2015-01-02 18:08:40 +00:00
return false ;
}
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
r_line = line ;
r_column = pos ;
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
return true ;
2014-02-10 01:10:30 +00:00
}
void TextEdit : : _cursor_changed_emit ( ) {
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
emit_signal ( " cursor_changed " ) ;
2017-03-05 15:44:50 +00:00
cursor_changed_dirty = false ;
2014-02-10 01:10:30 +00:00
}
void TextEdit : : _text_changed_emit ( ) {
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
emit_signal ( " text_changed " ) ;
2017-03-05 15:44:50 +00:00
text_changed_dirty = false ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
void TextEdit : : set_line_as_marked ( int p_line , bool p_marked ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
ERR_FAIL_INDEX ( p_line , text . size ( ) ) ;
text . set_marked ( p_line , p_marked ) ;
2015-01-02 18:08:40 +00:00
update ( ) ;
2014-02-10 01:10:30 +00:00
}
2018-06-05 16:50:21 +00:00
void TextEdit : : set_line_as_safe ( int p_line , bool p_safe ) {
ERR_FAIL_INDEX ( p_line , text . size ( ) ) ;
text . set_safe ( p_line , p_safe ) ;
update ( ) ;
}
bool TextEdit : : is_line_set_as_safe ( int p_line ) const {
ERR_FAIL_INDEX_V ( p_line , text . size ( ) , false ) ;
return text . is_safe ( p_line ) ;
}
2019-04-22 16:20:27 +00:00
void TextEdit : : set_executing_line ( int p_line ) {
ERR_FAIL_INDEX ( p_line , text . size ( ) ) ;
executing_line = p_line ;
update ( ) ;
}
void TextEdit : : clear_executing_line ( ) {
executing_line = - 1 ;
update ( ) ;
}
2019-04-19 23:51:25 +00:00
bool TextEdit : : is_line_set_as_bookmark ( int p_line ) const {
ERR_FAIL_INDEX_V ( p_line , text . size ( ) , false ) ;
return text . is_bookmark ( p_line ) ;
}
void TextEdit : : set_line_as_bookmark ( int p_line , bool p_bookmark ) {
ERR_FAIL_INDEX ( p_line , text . size ( ) ) ;
text . set_bookmark ( p_line , p_bookmark ) ;
update ( ) ;
}
void TextEdit : : get_bookmarks ( List < int > * p_bookmarks ) const {
for ( int i = 0 ; i < text . size ( ) ; i + + ) {
if ( text . is_bookmark ( i ) )
p_bookmarks - > push_back ( i ) ;
}
}
Array TextEdit : : get_bookmarks_array ( ) const {
Array arr ;
for ( int i = 0 ; i < text . size ( ) ; i + + ) {
if ( text . is_bookmark ( i ) )
arr . append ( i ) ;
}
return arr ;
}
2014-02-10 01:10:30 +00:00
bool TextEdit : : is_line_set_as_breakpoint ( int p_line ) const {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
ERR_FAIL_INDEX_V ( p_line , text . size ( ) , false ) ;
2015-01-02 18:08:40 +00:00
return text . is_breakpoint ( p_line ) ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
void TextEdit : : set_line_as_breakpoint ( int p_line , bool p_breakpoint ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
ERR_FAIL_INDEX ( p_line , text . size ( ) ) ;
text . set_breakpoint ( p_line , p_breakpoint ) ;
2015-01-02 18:08:40 +00:00
update ( ) ;
2014-02-10 01:10:30 +00:00
}
void TextEdit : : get_breakpoints ( List < int > * p_breakpoints ) const {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
for ( int i = 0 ; i < text . size ( ) ; i + + ) {
2015-01-02 18:08:40 +00:00
if ( text . is_breakpoint ( i ) )
p_breakpoints - > push_back ( i ) ;
}
2014-02-10 01:10:30 +00:00
}
2018-05-30 12:02:51 +00:00
Array TextEdit : : get_breakpoints_array ( ) const {
Array arr ;
for ( int i = 0 ; i < text . size ( ) ; i + + ) {
if ( text . is_breakpoint ( i ) )
arr . append ( i ) ;
}
return arr ;
}
void TextEdit : : remove_breakpoints ( ) {
for ( int i = 0 ; i < text . size ( ) ; i + + ) {
if ( text . is_breakpoint ( i ) )
/* Should "breakpoint_toggled" be fired when breakpoints are removed this way? */
text . set_breakpoint ( i , false ) ;
}
}
2019-04-20 11:51:25 +00:00
void TextEdit : : set_line_info_icon ( int p_line , Ref < Texture > p_icon , String p_info ) {
ERR_FAIL_INDEX ( p_line , text . size ( ) ) ;
text . set_info_icon ( p_line , p_icon , p_info ) ;
update ( ) ;
}
void TextEdit : : clear_info_icons ( ) {
for ( int i = 0 ; i < text . size ( ) ; i + + ) {
text . set_info_icon ( i , NULL , " " ) ;
}
update ( ) ;
}
2017-11-12 23:12:17 +00:00
void TextEdit : : set_line_as_hidden ( int p_line , bool p_hidden ) {
ERR_FAIL_INDEX ( p_line , text . size ( ) ) ;
if ( is_hiding_enabled ( ) | | ! p_hidden )
text . set_hidden ( p_line , p_hidden ) ;
update ( ) ;
}
bool TextEdit : : is_line_hidden ( int p_line ) const {
ERR_FAIL_INDEX_V ( p_line , text . size ( ) , false ) ;
return text . is_hidden ( p_line ) ;
}
2017-11-16 04:00:27 +00:00
void TextEdit : : fold_all_lines ( ) {
for ( int i = 0 ; i < text . size ( ) ; i + + ) {
fold_line ( i ) ;
}
_update_scrollbars ( ) ;
update ( ) ;
}
2017-11-12 23:12:17 +00:00
void TextEdit : : unhide_all_lines ( ) {
for ( int i = 0 ; i < text . size ( ) ; i + + ) {
text . set_hidden ( i , false ) ;
}
2017-11-16 04:00:27 +00:00
_update_scrollbars ( ) ;
2017-11-12 23:12:17 +00:00
update ( ) ;
}
2018-01-26 01:41:17 +00:00
int TextEdit : : num_lines_from ( int p_line_from , int visible_amount ) const {
2017-11-12 23:12:17 +00:00
2019-08-18 15:56:24 +00:00
// Returns the number of lines (hidden and unhidden) from p_line_from to (p_line_from + visible_amount of unhidden lines).
2018-01-26 01:41:17 +00:00
ERR_FAIL_INDEX_V ( p_line_from , text . size ( ) , ABS ( visible_amount ) ) ;
2017-11-12 23:12:17 +00:00
if ( ! is_hiding_enabled ( ) )
2018-01-26 01:41:17 +00:00
return ABS ( visible_amount ) ;
2017-11-12 23:12:17 +00:00
int num_visible = 0 ;
int num_total = 0 ;
2018-01-26 01:41:17 +00:00
if ( visible_amount > = 0 ) {
2017-11-12 23:12:17 +00:00
for ( int i = p_line_from ; i < text . size ( ) ; i + + ) {
num_total + + ;
2018-01-26 01:41:17 +00:00
if ( ! is_line_hidden ( i ) ) {
2017-11-12 23:12:17 +00:00
num_visible + + ;
2018-01-26 01:41:17 +00:00
}
if ( num_visible > = visible_amount )
2017-11-12 23:12:17 +00:00
break ;
}
} else {
2018-01-26 01:41:17 +00:00
visible_amount = ABS ( visible_amount ) ;
2017-11-12 23:12:17 +00:00
for ( int i = p_line_from ; i > = 0 ; i - - ) {
num_total + + ;
2018-01-26 01:41:17 +00:00
if ( ! is_line_hidden ( i ) ) {
2017-11-12 23:12:17 +00:00
num_visible + + ;
2018-01-26 01:41:17 +00:00
}
if ( num_visible > = visible_amount )
2017-11-12 23:12:17 +00:00
break ;
}
}
return num_total ;
}
2018-01-26 01:41:17 +00:00
int TextEdit : : num_lines_from_rows ( int p_line_from , int p_wrap_index_from , int visible_amount , int & wrap_index ) const {
2017-12-26 06:53:16 +00:00
2019-08-18 15:56:24 +00:00
// Returns the number of lines (hidden and unhidden) from (p_line_from + p_wrap_index_from) row to (p_line_from + visible_amount of unhidden and wrapped rows).
// Wrap index is set to the wrap index of the last line.
2018-01-26 01:41:17 +00:00
wrap_index = 0 ;
ERR_FAIL_INDEX_V ( p_line_from , text . size ( ) , ABS ( visible_amount ) ) ;
2017-12-26 06:53:16 +00:00
2018-01-26 01:41:17 +00:00
if ( ! is_hiding_enabled ( ) & & ! is_wrap_enabled ( ) )
return ABS ( visible_amount ) ;
int num_visible = 0 ;
int num_total = 0 ;
if ( visible_amount = = 0 ) {
num_total = 0 ;
wrap_index = 0 ;
} else if ( visible_amount > 0 ) {
int i ;
num_visible - = p_wrap_index_from ;
for ( i = p_line_from ; i < text . size ( ) ; i + + ) {
num_total + + ;
if ( ! is_line_hidden ( i ) ) {
num_visible + + ;
num_visible + = times_line_wraps ( i ) ;
}
if ( num_visible > = visible_amount )
break ;
}
wrap_index = times_line_wraps ( MIN ( i , text . size ( ) - 1 ) ) - ( num_visible - visible_amount ) ;
} else {
visible_amount = ABS ( visible_amount ) ;
int i ;
num_visible - = times_line_wraps ( p_line_from ) - p_wrap_index_from ;
for ( i = p_line_from ; i > = 0 ; i - - ) {
num_total + + ;
if ( ! is_line_hidden ( i ) ) {
num_visible + + ;
num_visible + = times_line_wraps ( i ) ;
}
if ( num_visible > = visible_amount )
break ;
}
wrap_index = ( num_visible - visible_amount ) ;
}
wrap_index = MAX ( wrap_index , 0 ) ;
return num_total ;
}
int TextEdit : : get_last_unhidden_line ( ) const {
2017-12-26 06:53:16 +00:00
2019-08-18 15:56:24 +00:00
// Returns the last line in the text that is not hidden.
2017-12-26 06:53:16 +00:00
if ( ! is_hiding_enabled ( ) )
2018-01-26 01:41:17 +00:00
return text . size ( ) - 1 ;
2017-12-26 06:53:16 +00:00
2018-01-26 01:41:17 +00:00
int last_line ;
for ( last_line = text . size ( ) - 1 ; last_line > 0 ; last_line - - ) {
if ( ! is_line_hidden ( last_line ) ) {
break ;
}
2017-12-26 06:53:16 +00:00
}
2018-01-26 01:41:17 +00:00
return last_line ;
2017-12-26 06:53:16 +00:00
}
2017-11-28 11:03:46 +00:00
int TextEdit : : get_indent_level ( int p_line ) const {
2017-11-12 23:12:17 +00:00
ERR_FAIL_INDEX_V ( p_line , text . size ( ) , 0 ) ;
2019-08-18 15:56:24 +00:00
// Counts number of tabs and spaces before line starts.
2017-11-28 11:03:46 +00:00
int tab_count = 0 ;
2017-11-12 23:12:17 +00:00
int whitespace_count = 0 ;
int line_length = text [ p_line ] . size ( ) ;
for ( int i = 0 ; i < line_length - 1 ; i + + ) {
if ( text [ p_line ] [ i ] = = ' \t ' ) {
2017-11-28 11:03:46 +00:00
tab_count + + ;
2017-11-12 23:12:17 +00:00
} else if ( text [ p_line ] [ i ] = = ' ' ) {
whitespace_count + + ;
} else {
break ;
}
}
2018-01-26 01:41:17 +00:00
return tab_count * indent_size + whitespace_count ;
2017-11-12 23:12:17 +00:00
}
2018-01-12 05:25:04 +00:00
bool TextEdit : : is_line_comment ( int p_line ) const {
2019-08-18 15:56:24 +00:00
// Checks to see if this line is the start of a comment.
2018-01-12 05:25:04 +00:00
ERR_FAIL_INDEX_V ( p_line , text . size ( ) , false ) ;
const Map < int , Text : : ColorRegionInfo > & cri_map = text . get_color_region_info ( p_line ) ;
int line_length = text [ p_line ] . size ( ) ;
for ( int i = 0 ; i < line_length - 1 ; i + + ) {
if ( _is_symbol ( text [ p_line ] [ i ] ) & & cri_map . has ( i ) ) {
const Text : : ColorRegionInfo & cri = cri_map [ i ] ;
2019-06-26 13:08:25 +00:00
return color_regions [ cri . region ] . begin_key = = " # " | | color_regions [ cri . region ] . begin_key = = " // " ;
2018-01-12 05:25:04 +00:00
} else if ( _is_whitespace ( text [ p_line ] [ i ] ) ) {
continue ;
} else {
break ;
}
}
return false ;
}
2017-11-12 23:12:17 +00:00
bool TextEdit : : can_fold ( int p_line ) const {
ERR_FAIL_INDEX_V ( p_line , text . size ( ) , false ) ;
if ( ! is_hiding_enabled ( ) )
return false ;
2017-11-16 04:00:27 +00:00
if ( p_line + 1 > = text . size ( ) )
2017-11-12 23:12:17 +00:00
return false ;
2018-09-29 17:08:56 +00:00
if ( text [ p_line ] . strip_edges ( ) . size ( ) = = 0 )
2017-11-12 23:12:17 +00:00
return false ;
if ( is_folded ( p_line ) )
return false ;
2017-11-16 04:00:27 +00:00
if ( is_line_hidden ( p_line ) )
return false ;
2018-01-12 05:25:04 +00:00
if ( is_line_comment ( p_line ) )
return false ;
2017-11-12 23:12:17 +00:00
2017-11-28 11:03:46 +00:00
int start_indent = get_indent_level ( p_line ) ;
2017-11-12 23:12:17 +00:00
for ( int i = p_line + 1 ; i < text . size ( ) ; i + + ) {
2018-09-29 17:08:56 +00:00
if ( text [ i ] . strip_edges ( ) . size ( ) = = 0 )
2017-11-12 23:12:17 +00:00
continue ;
2017-11-28 11:03:46 +00:00
int next_indent = get_indent_level ( i ) ;
2018-01-12 05:25:04 +00:00
if ( is_line_comment ( i ) ) {
continue ;
} else if ( next_indent > start_indent ) {
2017-11-12 23:12:17 +00:00
return true ;
2018-01-12 05:25:04 +00:00
} else {
2017-11-12 23:12:17 +00:00
return false ;
2018-01-12 05:25:04 +00:00
}
2017-11-12 23:12:17 +00:00
}
return false ;
}
bool TextEdit : : is_folded ( int p_line ) const {
ERR_FAIL_INDEX_V ( p_line , text . size ( ) , false ) ;
2017-11-23 20:37:08 +00:00
if ( p_line + 1 > = text . size ( ) )
2017-11-12 23:12:17 +00:00
return false ;
2019-06-26 13:08:25 +00:00
return ! is_line_hidden ( p_line ) & & is_line_hidden ( p_line + 1 ) ;
2017-11-12 23:12:17 +00:00
}
2019-04-13 11:43:35 +00:00
Vector < int > TextEdit : : get_folded_lines ( ) const {
Vector < int > folded_lines ;
for ( int i = 0 ; i < text . size ( ) ; i + + ) {
if ( is_folded ( i ) ) {
folded_lines . push_back ( i ) ;
}
}
return folded_lines ;
}
2017-11-12 23:12:17 +00:00
void TextEdit : : fold_line ( int p_line ) {
ERR_FAIL_INDEX ( p_line , text . size ( ) ) ;
if ( ! is_hiding_enabled ( ) )
return ;
if ( ! can_fold ( p_line ) )
return ;
2019-08-18 15:56:24 +00:00
// Hide lines below this one.
2017-11-28 11:03:46 +00:00
int start_indent = get_indent_level ( p_line ) ;
int last_line = start_indent ;
2017-11-12 23:12:17 +00:00
for ( int i = p_line + 1 ; i < text . size ( ) ; i + + ) {
2017-11-28 11:03:46 +00:00
if ( text [ i ] . strip_edges ( ) . size ( ) ! = 0 ) {
2018-01-12 05:25:04 +00:00
if ( is_line_comment ( i ) ) {
continue ;
} else if ( get_indent_level ( i ) > start_indent ) {
2017-11-28 11:03:46 +00:00
last_line = i ;
} else {
break ;
2017-11-12 23:12:17 +00:00
}
}
}
2017-11-28 11:03:46 +00:00
for ( int i = p_line + 1 ; i < = last_line ; i + + ) {
set_line_as_hidden ( i , true ) ;
}
2017-11-16 04:00:27 +00:00
2019-08-18 15:56:24 +00:00
// Fix selection.
2017-11-16 04:00:27 +00:00
if ( is_selection_active ( ) ) {
if ( is_line_hidden ( selection . from_line ) & & is_line_hidden ( selection . to_line ) ) {
deselect ( ) ;
} else if ( is_line_hidden ( selection . from_line ) ) {
select ( p_line , 9999 , selection . to_line , selection . to_column ) ;
} else if ( is_line_hidden ( selection . to_line ) ) {
select ( selection . from_line , selection . from_column , p_line , 9999 ) ;
}
}
2019-08-18 15:56:24 +00:00
// Reset cursor.
2017-11-16 04:00:27 +00:00
if ( is_line_hidden ( cursor . line ) ) {
cursor_set_line ( p_line , false , false ) ;
cursor_set_column ( get_line ( p_line ) . length ( ) , false ) ;
}
_update_scrollbars ( ) ;
2017-11-12 23:12:17 +00:00
update ( ) ;
}
void TextEdit : : unfold_line ( int p_line ) {
ERR_FAIL_INDEX ( p_line , text . size ( ) ) ;
2017-11-16 04:00:27 +00:00
if ( ! is_folded ( p_line ) & & ! is_line_hidden ( p_line ) )
return ;
2017-11-12 23:12:17 +00:00
int fold_start = p_line ;
for ( fold_start = p_line ; fold_start > 0 ; fold_start - - ) {
if ( is_folded ( fold_start ) )
break ;
}
fold_start = is_folded ( fold_start ) ? fold_start : p_line ;
for ( int i = fold_start + 1 ; i < text . size ( ) ; i + + ) {
if ( is_line_hidden ( i ) ) {
set_line_as_hidden ( i , false ) ;
} else {
break ;
}
}
2017-11-16 04:00:27 +00:00
_update_scrollbars ( ) ;
2017-11-12 23:12:17 +00:00
update ( ) ;
}
2017-12-08 18:17:10 +00:00
void TextEdit : : toggle_fold_line ( int p_line ) {
ERR_FAIL_INDEX ( p_line , text . size ( ) ) ;
if ( ! is_folded ( p_line ) )
fold_line ( p_line ) ;
else
unfold_line ( p_line ) ;
}
2014-02-10 01:10:30 +00:00
int TextEdit : : get_line_count ( ) const {
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
return text . size ( ) ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
void TextEdit : : _do_text_op ( const TextOperation & p_op , bool p_reverse ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
ERR_FAIL_COND ( p_op . type = = TextOperation : : TYPE_NONE ) ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
bool insert = p_op . type = = TextOperation : : TYPE_INSERT ;
2015-01-02 18:08:40 +00:00
if ( p_reverse )
2017-03-05 15:44:50 +00:00
insert = ! insert ;
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
if ( insert ) {
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
int check_line ;
int check_column ;
2017-03-05 15:44:50 +00:00
_base_insert_text ( p_op . from_line , p_op . from_column , p_op . text , check_line , check_column ) ;
2019-08-18 15:56:24 +00:00
ERR_FAIL_COND ( check_line ! = p_op . to_line ) ; // BUG.
ERR_FAIL_COND ( check_column ! = p_op . to_column ) ; // BUG.
2015-01-02 18:08:40 +00:00
} else {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
_base_remove_text ( p_op . from_line , p_op . from_column , p_op . to_line , p_op . to_column ) ;
2015-01-02 18:08:40 +00:00
}
2014-02-10 01:10:30 +00:00
}
void TextEdit : : _clear_redo ( ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
if ( undo_stack_pos = = NULL )
2019-08-18 15:56:24 +00:00
return ; // Nothing to clear.
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
_push_current_op ( ) ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
while ( undo_stack_pos ) {
2015-01-02 18:08:40 +00:00
List < TextOperation > : : Element * elem = undo_stack_pos ;
2017-03-05 15:44:50 +00:00
undo_stack_pos = undo_stack_pos - > next ( ) ;
2015-01-02 18:08:40 +00:00
undo_stack . erase ( elem ) ;
}
2014-02-10 01:10:30 +00:00
}
void TextEdit : : undo ( ) {
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
_push_current_op ( ) ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
if ( undo_stack_pos = = NULL ) {
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
if ( ! undo_stack . size ( ) )
2019-08-18 15:56:24 +00:00
return ; // Nothing to undo.
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
undo_stack_pos = undo_stack . back ( ) ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
} else if ( undo_stack_pos = = undo_stack . front ( ) )
2019-08-18 15:56:24 +00:00
return ; // At the bottom of the undo stack.
2015-01-02 18:08:40 +00:00
else
2017-03-05 15:44:50 +00:00
undo_stack_pos = undo_stack_pos - > prev ( ) ;
2016-03-08 23:00:52 +00:00
2018-01-13 10:38:01 +00:00
deselect ( ) ;
2016-03-13 20:08:12 +00:00
TextOperation op = undo_stack_pos - > get ( ) ;
_do_text_op ( op , true ) ;
2019-06-17 01:54:28 +00:00
if ( op . type ! = TextOperation : : TYPE_INSERT & & ( op . from_line ! = op . to_line | | op . to_column ! = op . from_column + 1 ) )
2019-04-21 18:19:31 +00:00
select ( op . from_line , op . from_column , op . to_line , op . to_column ) ;
2017-03-05 15:44:50 +00:00
current_op . version = op . prev_version ;
if ( undo_stack_pos - > get ( ) . chain_backward ) {
while ( true ) {
2016-06-14 11:48:34 +00:00
ERR_BREAK ( ! undo_stack_pos - > prev ( ) ) ;
2015-01-02 18:08:40 +00:00
undo_stack_pos = undo_stack_pos - > prev ( ) ;
2016-03-13 20:08:12 +00:00
op = undo_stack_pos - > get ( ) ;
_do_text_op ( op , true ) ;
current_op . version = op . prev_version ;
2016-06-14 11:48:34 +00:00
if ( undo_stack_pos - > get ( ) . chain_forward ) {
break ;
}
}
2015-01-02 18:08:40 +00:00
}
2016-03-08 23:00:52 +00:00
2016-06-13 19:40:28 +00:00
if ( undo_stack_pos - > get ( ) . type = = TextOperation : : TYPE_REMOVE ) {
cursor_set_line ( undo_stack_pos - > get ( ) . to_line ) ;
cursor_set_column ( undo_stack_pos - > get ( ) . to_column ) ;
2016-06-13 19:55:26 +00:00
_cancel_code_hint ( ) ;
2016-06-13 19:40:28 +00:00
} else {
cursor_set_line ( undo_stack_pos - > get ( ) . from_line ) ;
cursor_set_column ( undo_stack_pos - > get ( ) . from_column ) ;
}
2015-01-02 18:08:40 +00:00
update ( ) ;
2014-02-10 01:10:30 +00:00
}
void TextEdit : : redo ( ) {
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
_push_current_op ( ) ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
if ( undo_stack_pos = = NULL )
2019-08-18 15:56:24 +00:00
return ; // Nothing to do.
2016-03-08 23:00:52 +00:00
2018-01-13 10:38:01 +00:00
deselect ( ) ;
2016-03-13 20:08:12 +00:00
TextOperation op = undo_stack_pos - > get ( ) ;
_do_text_op ( op , false ) ;
current_op . version = op . version ;
2017-03-05 15:44:50 +00:00
if ( undo_stack_pos - > get ( ) . chain_forward ) {
2016-06-14 11:48:34 +00:00
2017-03-05 15:44:50 +00:00
while ( true ) {
2016-06-14 11:48:34 +00:00
ERR_BREAK ( ! undo_stack_pos - > next ( ) ) ;
2017-03-05 15:44:50 +00:00
undo_stack_pos = undo_stack_pos - > next ( ) ;
2016-03-13 20:08:12 +00:00
op = undo_stack_pos - > get ( ) ;
_do_text_op ( op , false ) ;
current_op . version = op . version ;
2016-06-14 11:48:34 +00:00
if ( undo_stack_pos - > get ( ) . chain_backward )
break ;
}
2015-01-02 18:08:40 +00:00
}
2016-03-13 20:08:12 +00:00
cursor_set_line ( undo_stack_pos - > get ( ) . to_line ) ;
cursor_set_column ( undo_stack_pos - > get ( ) . to_column ) ;
2017-03-05 15:44:50 +00:00
undo_stack_pos = undo_stack_pos - > next ( ) ;
2015-01-02 18:08:40 +00:00
update ( ) ;
2014-02-10 01:10:30 +00:00
}
void TextEdit : : clear_undo_history ( ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
saved_version = 0 ;
current_op . type = TextOperation : : TYPE_NONE ;
undo_stack_pos = NULL ;
2015-01-02 18:08:40 +00:00
undo_stack . clear ( ) ;
2014-02-10 01:10:30 +00:00
}
2016-04-06 06:36:29 +00:00
void TextEdit : : begin_complex_operation ( ) {
2015-01-02 18:08:40 +00:00
_push_current_op ( ) ;
2017-03-05 15:44:50 +00:00
next_operation_is_complex = true ;
2014-04-26 12:42:19 +00:00
}
2014-02-10 01:10:30 +00:00
2016-04-06 06:36:29 +00:00
void TextEdit : : end_complex_operation ( ) {
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
_push_current_op ( ) ;
ERR_FAIL_COND ( undo_stack . size ( ) = = 0 ) ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
if ( undo_stack . back ( ) - > get ( ) . chain_forward ) {
undo_stack . back ( ) - > get ( ) . chain_forward = false ;
2015-01-02 18:08:40 +00:00
return ;
}
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
undo_stack . back ( ) - > get ( ) . chain_backward = true ;
2014-04-26 12:42:19 +00:00
}
2014-02-10 01:10:30 +00:00
2014-04-26 12:42:19 +00:00
void TextEdit : : _push_current_op ( ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
if ( current_op . type = = TextOperation : : TYPE_NONE )
2019-08-18 15:56:24 +00:00
return ; // Nothing to do.
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
if ( next_operation_is_complex ) {
current_op . chain_forward = true ;
next_operation_is_complex = false ;
2015-01-02 18:08:40 +00:00
}
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
undo_stack . push_back ( current_op ) ;
2017-03-05 15:44:50 +00:00
current_op . type = TextOperation : : TYPE_NONE ;
current_op . text = " " ;
current_op . chain_forward = false ;
2014-02-10 01:10:30 +00:00
}
2017-04-17 13:24:30 +00:00
void TextEdit : : set_indent_using_spaces ( const bool p_use_spaces ) {
indent_using_spaces = p_use_spaces ;
}
bool TextEdit : : is_indent_using_spaces ( ) const {
return indent_using_spaces ;
}
void TextEdit : : set_indent_size ( const int p_size ) {
2016-03-11 18:10:01 +00:00
ERR_FAIL_COND ( p_size < = 0 ) ;
2017-04-17 13:24:30 +00:00
indent_size = p_size ;
text . set_indent_size ( p_size ) ;
space_indent = " " ;
for ( int i = 0 ; i < p_size ; i + + ) {
space_indent + = " " ;
}
2016-03-11 18:10:01 +00:00
update ( ) ;
}
2018-05-03 17:03:20 +00:00
int TextEdit : : get_indent_size ( ) {
return indent_size ;
}
2014-02-10 01:10:30 +00:00
void TextEdit : : set_draw_tabs ( bool p_draw ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
draw_tabs = p_draw ;
2019-04-24 07:59:17 +00:00
update ( ) ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
bool TextEdit : : is_drawing_tabs ( ) const {
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
return draw_tabs ;
2014-02-10 01:10:30 +00:00
}
2019-04-23 23:33:20 +00:00
void TextEdit : : set_draw_spaces ( bool p_draw ) {
draw_spaces = p_draw ;
}
bool TextEdit : : is_drawing_spaces ( ) const {
return draw_spaces ;
}
2017-09-27 17:24:05 +00:00
void TextEdit : : set_override_selected_font_color ( bool p_override_selected_font_color ) {
override_selected_font_color = p_override_selected_font_color ;
}
2018-01-26 01:41:17 +00:00
2017-09-27 17:24:05 +00:00
bool TextEdit : : is_overriding_selected_font_color ( ) const {
return override_selected_font_color ;
}
2016-03-31 19:49:30 +00:00
void TextEdit : : set_insert_mode ( bool p_enabled ) {
insert_mode = p_enabled ;
update ( ) ;
}
bool TextEdit : : is_insert_mode ( ) const {
return insert_mode ;
}
2017-09-06 01:02:51 +00:00
bool TextEdit : : is_insert_text_operation ( ) {
return ( current_op . type = = TextOperation : : TYPE_INSERT ) ;
}
2014-02-10 01:10:30 +00:00
uint32_t TextEdit : : get_version ( ) const {
2015-01-02 18:08:40 +00:00
return current_op . version ;
2014-02-10 01:10:30 +00:00
}
2018-01-26 01:41:17 +00:00
2014-02-10 01:10:30 +00:00
uint32_t TextEdit : : get_saved_version ( ) const {
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
return saved_version ;
2014-02-10 01:10:30 +00:00
}
2018-01-26 01:41:17 +00:00
2014-02-10 01:10:30 +00:00
void TextEdit : : tag_saved_version ( ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
saved_version = get_version ( ) ;
2014-02-10 01:10:30 +00:00
}
2018-01-26 01:41:17 +00:00
double TextEdit : : get_scroll_pos_for_line ( int p_line , int p_wrap_index ) const {
2016-03-08 23:00:52 +00:00
2018-01-26 01:41:17 +00:00
if ( ! is_wrap_enabled ( ) & & ! is_hiding_enabled ( ) )
return p_line ;
2016-03-08 23:00:52 +00:00
2019-08-18 15:56:24 +00:00
// Count the number of visible lines up to this line.
2018-01-26 01:41:17 +00:00
double new_line_scroll_pos = 0 ;
int to = CLAMP ( p_line , 0 , text . size ( ) - 1 ) ;
for ( int i = 0 ; i < to ; i + + ) {
if ( ! text . is_hidden ( i ) ) {
new_line_scroll_pos + + ;
new_line_scroll_pos + = times_line_wraps ( i ) ;
2017-10-08 13:29:27 +00:00
}
}
2018-01-26 01:41:17 +00:00
new_line_scroll_pos + = p_wrap_index ;
return new_line_scroll_pos ;
}
void TextEdit : : set_line_as_first_visible ( int p_line , int p_wrap_index ) {
set_v_scroll ( get_scroll_pos_for_line ( p_line , p_wrap_index ) ) ;
}
void TextEdit : : set_line_as_center_visible ( int p_line , int p_wrap_index ) {
int visible_rows = get_visible_rows ( ) ;
int wi ;
int first_line = p_line - num_lines_from_rows ( p_line , p_wrap_index , - visible_rows / 2 , wi ) + 1 ;
set_v_scroll ( get_scroll_pos_for_line ( first_line , wi ) ) ;
}
void TextEdit : : set_line_as_last_visible ( int p_line , int p_wrap_index ) {
int wi ;
int first_line = p_line - num_lines_from_rows ( p_line , p_wrap_index , - get_visible_rows ( ) - 1 , wi ) + 1 ;
set_v_scroll ( get_scroll_pos_for_line ( first_line , wi ) + get_visible_rows_offset ( ) ) ;
}
int TextEdit : : get_first_visible_line ( ) const {
return CLAMP ( cursor . line_ofs , 0 , text . size ( ) - 1 ) ;
}
int TextEdit : : get_last_visible_line ( ) const {
int first_vis_line = get_first_visible_line ( ) ;
int last_vis_line = 0 ;
int wi ;
last_vis_line = first_vis_line + num_lines_from_rows ( first_vis_line , cursor . wrap_ofs , get_visible_rows ( ) + 1 , wi ) - 1 ;
last_vis_line = CLAMP ( last_vis_line , 0 , text . size ( ) - 1 ) ;
return last_vis_line ;
}
int TextEdit : : get_last_visible_line_wrap_index ( ) const {
int first_vis_line = get_first_visible_line ( ) ;
int wi ;
2018-03-25 23:36:34 +00:00
num_lines_from_rows ( first_vis_line , cursor . wrap_ofs , get_visible_rows ( ) + 1 , wi ) ;
2018-01-26 01:41:17 +00:00
return wi ;
}
double TextEdit : : get_visible_rows_offset ( ) const {
2019-08-25 13:49:13 +00:00
double total = _get_control_height ( ) ;
2018-01-26 01:41:17 +00:00
total / = ( double ) get_row_height ( ) ;
total = total - floor ( total ) ;
total = - CLAMP ( total , 0.001 , 1 ) + 1 ;
return total ;
}
double TextEdit : : get_v_scroll_offset ( ) const {
double val = get_v_scroll ( ) - floor ( get_v_scroll ( ) ) ;
return CLAMP ( val , 0 , 1 ) ;
}
double TextEdit : : get_v_scroll ( ) const {
return v_scroll - > get_value ( ) ;
}
void TextEdit : : set_v_scroll ( double p_scroll ) {
2017-01-04 04:16:14 +00:00
v_scroll - > set_value ( p_scroll ) ;
2018-01-26 01:41:17 +00:00
int max_v_scroll = v_scroll - > get_max ( ) - v_scroll - > get_page ( ) ;
if ( p_scroll > = max_v_scroll - 1.0 )
_scroll_moved ( v_scroll - > get_value ( ) ) ;
2014-02-10 01:10:30 +00:00
}
int TextEdit : : get_h_scroll ( ) const {
2016-03-08 23:00:52 +00:00
2017-01-04 04:16:14 +00:00
return h_scroll - > get_value ( ) ;
2014-02-10 01:10:30 +00:00
}
2018-01-26 01:41:17 +00:00
2014-02-10 01:10:30 +00:00
void TextEdit : : set_h_scroll ( int p_scroll ) {
2016-03-08 23:00:52 +00:00
2018-01-26 01:41:17 +00:00
if ( p_scroll < 0 ) {
p_scroll = 0 ;
}
2017-01-04 04:16:14 +00:00
h_scroll - > set_value ( p_scroll ) ;
2014-02-10 01:10:30 +00:00
}
2017-08-19 14:23:45 +00:00
void TextEdit : : set_smooth_scroll_enabled ( bool p_enable ) {
2018-01-26 01:41:17 +00:00
2017-08-19 14:23:45 +00:00
v_scroll - > set_smooth_scroll_enabled ( p_enable ) ;
smooth_scroll_enabled = p_enable ;
}
bool TextEdit : : is_smooth_scroll_enabled ( ) const {
2018-01-26 01:41:17 +00:00
2017-08-19 14:23:45 +00:00
return smooth_scroll_enabled ;
}
2017-08-21 18:13:24 +00:00
void TextEdit : : set_v_scroll_speed ( float p_speed ) {
2018-01-26 01:41:17 +00:00
2017-08-21 18:13:24 +00:00
v_scroll_speed = p_speed ;
}
float TextEdit : : get_v_scroll_speed ( ) const {
2018-01-26 01:41:17 +00:00
2017-08-21 18:13:24 +00:00
return v_scroll_speed ;
}
2017-03-05 15:44:50 +00:00
void TextEdit : : set_completion ( bool p_enabled , const Vector < String > & p_prefixes ) {
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
completion_prefixes . clear ( ) ;
2017-03-05 15:44:50 +00:00
completion_enabled = p_enabled ;
for ( int i = 0 ; i < p_prefixes . size ( ) ; i + + )
2015-01-02 18:08:40 +00:00
completion_prefixes . insert ( p_prefixes [ i ] ) ;
2014-02-10 01:10:30 +00:00
}
void TextEdit : : _confirm_completion ( ) {
2016-03-08 23:00:52 +00:00
2016-06-13 19:55:26 +00:00
begin_complex_operation ( ) ;
2016-03-08 23:00:52 +00:00
2016-06-13 19:55:26 +00:00
_remove_text ( cursor . line , cursor . column - completion_base . length ( ) , cursor . line , cursor . column ) ;
cursor_set_column ( cursor . column - completion_base . length ( ) , false ) ;
2019-06-13 09:32:03 +00:00
insert_text_at_cursor ( completion_current . insert_text ) ;
2016-06-13 19:55:26 +00:00
2019-04-05 02:06:44 +00:00
// When inserted into the middle of an existing string/method, don't add an unnecessary quote/bracket.
2018-05-26 21:13:42 +00:00
String line = text [ cursor . line ] ;
CharType next_char = line [ cursor . column ] ;
2019-06-13 09:32:03 +00:00
CharType last_completion_char = completion_current . insert_text [ completion_current . insert_text . length ( ) - 1 ] ;
2018-05-26 21:13:42 +00:00
2019-04-05 02:06:44 +00:00
if ( ( last_completion_char = = ' " ' | | last_completion_char = = ' \' ' ) & & last_completion_char = = next_char ) {
2018-05-26 21:13:42 +00:00
_base_remove_text ( cursor . line , cursor . column , cursor . line , cursor . column + 1 ) ;
}
2019-04-05 02:06:44 +00:00
if ( last_completion_char = = ' ( ' ) {
if ( next_char = = last_completion_char ) {
_base_remove_text ( cursor . line , cursor . column - 1 , cursor . line , cursor . column ) ;
} else if ( auto_brace_completion_enabled ) {
insert_text_at_cursor ( " ) " ) ;
cursor . column - - ;
}
} else if ( last_completion_char = = ' ) ' & & next_char = = ' ( ' ) {
_base_remove_text ( cursor . line , cursor . column - 2 , cursor . line , cursor . column ) ;
if ( line [ cursor . column + 1 ] ! = ' ) ' ) {
cursor . column - - ;
}
2015-01-02 18:08:40 +00:00
}
2016-03-08 23:00:52 +00:00
2016-06-13 19:55:26 +00:00
end_complex_operation ( ) ;
2015-01-02 18:08:40 +00:00
_cancel_completion ( ) ;
2018-08-14 16:02:33 +00:00
if ( last_completion_char = = ' ( ' ) {
query_code_comple ( ) ;
}
2014-02-10 01:10:30 +00:00
}
2014-12-17 01:31:57 +00:00
void TextEdit : : _cancel_code_hint ( ) {
2016-10-05 08:19:08 +00:00
2017-03-05 15:44:50 +00:00
completion_hint = " " ;
2014-12-17 01:31:57 +00:00
update ( ) ;
}
2014-02-10 01:10:30 +00:00
void TextEdit : : _cancel_completion ( ) {
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
if ( ! completion_active )
return ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
completion_active = false ;
2017-08-24 03:06:56 +00:00
completion_forced = false ;
2015-01-02 18:08:40 +00:00
update ( ) ;
2014-02-10 01:10:30 +00:00
}
2014-12-17 01:31:57 +00:00
static bool _is_completable ( CharType c ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
return ! _is_symbol ( c ) | | c = = ' " ' | | c = = ' \' ' ;
2014-12-17 01:31:57 +00:00
}
2014-02-10 01:10:30 +00:00
void TextEdit : : _update_completion_candidates ( ) {
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
String l = text [ cursor . line ] ;
2017-03-05 15:44:50 +00:00
int cofs = CLAMP ( cursor . column , 0 , l . length ( ) ) ;
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
String s ;
2015-01-03 16:03:13 +00:00
2019-08-18 15:56:24 +00:00
// Look for keywords first.
2015-01-03 16:03:13 +00:00
2017-03-05 15:44:50 +00:00
bool inquote = false ;
int first_quote = - 1 ;
2019-02-24 17:18:09 +00:00
int restore_quotes = - 1 ;
2015-06-26 04:14:31 +00:00
2017-03-05 15:44:50 +00:00
int c = cofs - 1 ;
while ( c > = 0 ) {
if ( l [ c ] = = ' " ' | | l [ c ] = = ' \' ' ) {
inquote = ! inquote ;
if ( first_quote = = - 1 )
first_quote = c ;
2019-02-24 17:18:09 +00:00
restore_quotes = 0 ;
} else if ( restore_quotes = = 0 & & l [ c ] = = ' $ ' ) {
restore_quotes = 1 ;
} else if ( restore_quotes = = 0 & & ! _is_whitespace ( l [ c ] ) ) {
restore_quotes = - 1 ;
2015-06-26 04:14:31 +00:00
}
c - - ;
}
2015-01-03 16:03:13 +00:00
2017-03-05 15:44:50 +00:00
bool pre_keyword = false ;
bool cancel = false ;
2015-06-26 04:14:31 +00:00
2017-03-05 15:44:50 +00:00
if ( ! inquote & & first_quote = = cofs - 1 ) {
2019-08-18 15:56:24 +00:00
// No completion here.
2017-03-05 15:44:50 +00:00
cancel = true ;
} else if ( inquote & & first_quote ! = - 1 ) {
2015-06-26 04:14:31 +00:00
2017-03-05 15:44:50 +00:00
s = l . substr ( first_quote , cofs - first_quote ) ;
} else if ( cofs > 0 & & l [ cofs - 1 ] = = ' ' ) {
int kofs = cofs - 1 ;
2015-01-03 16:03:13 +00:00
String kw ;
2017-03-05 15:44:50 +00:00
while ( kofs > = 0 & & l [ kofs ] = = ' ' )
2015-01-03 16:03:13 +00:00
kofs - - ;
2017-03-05 15:44:50 +00:00
while ( kofs > = 0 & & l [ kofs ] > 32 & & _is_completable ( l [ kofs ] ) ) {
kw = String : : chr ( l [ kofs ] ) + kw ;
2015-01-03 16:03:13 +00:00
kofs - - ;
}
2017-03-05 15:44:50 +00:00
pre_keyword = keywords . has ( kw ) ;
2015-01-03 16:03:13 +00:00
} else {
2018-03-06 18:25:14 +00:00
while ( cofs > 0 & & l [ cofs - 1 ] > 32 & & ( l [ cofs - 1 ] = = ' / ' | | _is_completable ( l [ cofs - 1 ] ) ) ) {
2017-03-05 15:44:50 +00:00
s = String : : chr ( l [ cofs - 1 ] ) + s ;
if ( l [ cofs - 1 ] = = ' \' ' | | l [ cofs - 1 ] = = ' " ' | | l [ cofs - 1 ] = = ' $ ' )
2015-01-03 16:03:13 +00:00
break ;
cofs - - ;
}
2015-01-02 18:08:40 +00:00
}
2015-01-03 16:03:13 +00:00
2017-08-24 03:06:56 +00:00
if ( cursor . column > 0 & & l [ cursor . column - 1 ] = = ' ( ' & & ! pre_keyword & & ! completion_forced ) {
2016-05-07 11:01:56 +00:00
cancel = true ;
}
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
update ( ) ;
2016-03-08 23:00:52 +00:00
2017-08-24 03:06:56 +00:00
bool prev_is_prefix = false ;
if ( cofs > 0 & & completion_prefixes . has ( String : : chr ( l [ cofs - 1 ] ) ) )
prev_is_prefix = true ;
2019-08-18 15:56:24 +00:00
// Check with one space before prefix, to allow indent.
if ( cofs > 1 & & l [ cofs - 1 ] = = ' ' & & completion_prefixes . has ( String : : chr ( l [ cofs - 2 ] ) ) )
2017-08-24 03:06:56 +00:00
prev_is_prefix = true ;
if ( cancel | | ( ! pre_keyword & & s = = " " & & ( cofs = = 0 | | ! prev_is_prefix ) ) ) {
2019-08-18 15:56:24 +00:00
// None to complete, cancel.
2015-01-02 18:08:40 +00:00
_cancel_completion ( ) ;
return ;
}
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
completion_options . clear ( ) ;
2017-03-05 15:44:50 +00:00
completion_index = 0 ;
completion_base = s ;
2016-06-13 19:55:26 +00:00
Vector < float > sim_cache ;
2017-10-28 21:46:20 +00:00
bool single_quote = s . begins_with ( " ' " ) ;
2019-06-13 09:32:03 +00:00
Vector < ScriptCodeCompletionOption > completion_options_casei ;
for ( List < ScriptCodeCompletionOption > : : Element * E = completion_sources . front ( ) ; E ; E = E - > next ( ) ) {
ScriptCodeCompletionOption & option = E - > get ( ) ;
2017-10-28 21:46:20 +00:00
2019-06-13 09:32:03 +00:00
if ( single_quote & & option . display . is_quoted ( ) ) {
option . display = option . display . unquote ( ) . quote ( " ' " ) ;
2017-10-28 21:46:20 +00:00
}
2019-06-13 09:32:03 +00:00
if ( inquote & & restore_quotes = = 1 & & ! option . display . is_quoted ( ) ) {
2019-02-24 17:18:09 +00:00
String quote = single_quote ? " ' " : " \" " ;
2019-06-13 09:32:03 +00:00
option . display = option . display . quote ( quote ) ;
2019-02-24 17:18:09 +00:00
}
2019-06-13 09:32:03 +00:00
if ( option . display . begins_with ( s ) ) {
completion_options . push_back ( option ) ;
} else if ( option . display . to_lower ( ) . begins_with ( s . to_lower ( ) ) ) {
completion_options_casei . push_back ( option ) ;
2016-07-03 14:34:23 +00:00
}
2018-08-14 16:02:33 +00:00
}
2017-01-08 06:01:52 +00:00
2018-11-26 15:36:46 +00:00
completion_options . append_array ( completion_options_casei ) ;
2018-08-14 16:02:33 +00:00
if ( completion_options . size ( ) = = 0 ) {
2019-06-13 09:32:03 +00:00
for ( int i = 0 ; i < completion_sources . size ( ) ; i + + ) {
if ( s . is_subsequence_of ( completion_sources [ i ] . display ) ) {
completion_options . push_back ( completion_sources [ i ] ) ;
2016-07-03 14:32:30 +00:00
}
2018-08-14 16:02:33 +00:00
}
}
2016-07-03 14:32:30 +00:00
2018-08-14 16:02:33 +00:00
if ( completion_options . size ( ) = = 0 ) {
2019-06-13 09:32:03 +00:00
for ( int i = 0 ; i < completion_sources . size ( ) ; i + + ) {
if ( s . is_subsequence_ofi ( completion_sources [ i ] . display ) ) {
completion_options . push_back ( completion_sources [ i ] ) ;
2015-01-02 18:08:40 +00:00
}
}
}
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
if ( completion_options . size ( ) = = 0 ) {
2019-08-18 15:56:24 +00:00
// No options to complete, cancel.
2015-01-02 18:08:40 +00:00
_cancel_completion ( ) ;
2018-08-14 16:02:33 +00:00
return ;
}
2017-01-08 06:01:52 +00:00
2019-06-13 09:32:03 +00:00
if ( completion_options . size ( ) = = 1 & & s = = completion_options [ 0 ] . display ) {
2019-08-18 15:56:24 +00:00
// A perfect match, stop completion.
2018-08-14 16:02:33 +00:00
_cancel_completion ( ) ;
2015-01-02 18:08:40 +00:00
return ;
}
2016-03-08 23:00:52 +00:00
2019-08-18 15:56:24 +00:00
// The top of the list is the best match.
2017-03-05 15:44:50 +00:00
completion_current = completion_options [ 0 ] ;
completion_enabled = true ;
2014-02-10 01:10:30 +00:00
}
void TextEdit : : query_code_comple ( ) {
2016-03-08 23:00:52 +00:00
2014-12-17 01:31:57 +00:00
String l = text [ cursor . line ] ;
2017-03-05 15:44:50 +00:00
int ofs = CLAMP ( cursor . column , 0 , l . length ( ) ) ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
bool inquote = false ;
2015-06-26 04:14:31 +00:00
2017-03-05 15:44:50 +00:00
int c = ofs - 1 ;
while ( c > = 0 ) {
if ( l [ c ] = = ' " ' | | l [ c ] = = ' \' ' )
inquote = ! inquote ;
2015-06-26 04:14:31 +00:00
c - - ;
}
2019-08-18 05:36:42 +00:00
bool ignored = completion_active & & ! completion_options . empty ( ) ;
if ( ignored ) {
ScriptCodeCompletionOption : : Kind kind = ScriptCodeCompletionOption : : KIND_PLAIN_TEXT ;
const ScriptCodeCompletionOption * previous_option = NULL ;
for ( int i = 0 ; i < completion_options . size ( ) ; i + + ) {
const ScriptCodeCompletionOption & current_option = completion_options [ i ] ;
if ( ! previous_option ) {
previous_option = & current_option ;
kind = current_option . kind ;
}
if ( previous_option - > kind ! = current_option . kind ) {
ignored = false ;
break ;
}
}
ignored = ignored & & ( kind = = ScriptCodeCompletionOption : : KIND_FILE_PATH | | kind = = ScriptCodeCompletionOption : : KIND_NODE_PATH | | kind = = ScriptCodeCompletionOption : : KIND_SIGNAL ) ;
}
if ( ! ignored ) {
if ( ofs > 0 & & ( inquote | | _is_completable ( l [ ofs - 1 ] ) | | completion_prefixes . has ( String : : chr ( l [ ofs - 1 ] ) ) ) )
emit_signal ( " request_completion " ) ;
else if ( ofs > 1 & & l [ ofs - 1 ] = = ' ' & & completion_prefixes . has ( String : : chr ( l [ ofs - 2 ] ) ) ) // Make it work with a space too, it's good enough.
emit_signal ( " request_completion " ) ;
}
2014-12-17 01:31:57 +00:00
}
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
void TextEdit : : set_code_hint ( const String & p_hint ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
completion_hint = p_hint ;
completion_hint_offset = - 0xFFFF ;
2014-12-17 01:31:57 +00:00
update ( ) ;
2014-02-10 01:10:30 +00:00
}
2019-06-13 09:32:03 +00:00
void TextEdit : : code_complete ( const List < ScriptCodeCompletionOption > & p_strings , bool p_forced ) {
2016-03-08 23:00:52 +00:00
2019-06-13 09:32:03 +00:00
completion_sources = p_strings ;
2017-03-05 15:44:50 +00:00
completion_active = true ;
2017-08-24 03:06:56 +00:00
completion_forced = p_forced ;
2019-06-13 09:32:03 +00:00
completion_current = ScriptCodeCompletionOption ( ) ;
2017-03-05 15:44:50 +00:00
completion_index = 0 ;
2015-01-02 18:08:40 +00:00
_update_completion_candidates ( ) ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
String TextEdit : : get_word_at_pos ( const Vector2 & p_pos ) const {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
int row , col ;
2016-09-12 13:52:29 +00:00
_get_mouse_pos ( p_pos , row , col ) ;
String s = text [ row ] ;
2017-03-05 15:44:50 +00:00
if ( s . length ( ) = = 0 )
2016-09-12 13:52:29 +00:00
return " " ;
2017-12-23 08:59:54 +00:00
int beg , end ;
if ( select_word ( s , col , beg , end ) ) {
2016-09-12 13:52:29 +00:00
2017-08-02 17:20:27 +00:00
bool inside_quotes = false ;
2018-09-28 04:31:15 +00:00
CharType selected_quote = ' \0 ' ;
2017-09-01 20:33:39 +00:00
int qbegin = 0 , qend = 0 ;
2017-08-02 17:20:27 +00:00
for ( int i = 0 ; i < s . length ( ) ; i + + ) {
2018-03-11 12:59:48 +00:00
if ( s [ i ] = = ' " ' | | s [ i ] = = ' \' ' ) {
if ( i = = 0 | | s [ i - 1 ] ! = ' \\ ' ) {
if ( inside_quotes & & selected_quote = = s [ i ] ) {
qend = i ;
inside_quotes = false ;
selected_quote = ' \0 ' ;
if ( col > = qbegin & & col < = qend ) {
return s . substr ( qbegin , qend - qbegin ) ;
}
} else if ( ! inside_quotes ) {
qbegin = i + 1 ;
inside_quotes = true ;
selected_quote = s [ i ] ;
2017-08-02 17:20:27 +00:00
}
}
}
}
2017-03-05 15:44:50 +00:00
return s . substr ( beg , end - beg ) ;
2016-09-12 13:52:29 +00:00
}
return String ( ) ;
}
2017-03-05 15:44:50 +00:00
String TextEdit : : get_tooltip ( const Point2 & p_pos ) const {
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
if ( ! tooltip_obj )
return Control : : get_tooltip ( p_pos ) ;
2017-03-05 15:44:50 +00:00
int row , col ;
2015-11-07 12:39:03 +00:00
_get_mouse_pos ( p_pos , row , col ) ;
2015-01-02 18:08:40 +00:00
String s = text [ row ] ;
2017-03-05 15:44:50 +00:00
if ( s . length ( ) = = 0 )
2015-01-02 18:08:40 +00:00
return Control : : get_tooltip ( p_pos ) ;
2017-12-23 08:59:54 +00:00
int beg , end ;
if ( select_word ( s , col , beg , end ) ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
String tt = tooltip_obj - > call ( tooltip_func , s . substr ( beg , end - beg ) , tooltip_ud ) ;
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
return tt ;
}
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
return Control : : get_tooltip ( p_pos ) ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
void TextEdit : : set_tooltip_request_func ( Object * p_obj , const StringName & p_function , const Variant & p_udata ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
tooltip_obj = p_obj ;
tooltip_func = p_function ;
tooltip_ud = p_udata ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
void TextEdit : : set_line ( int line , String new_text ) {
2015-01-02 18:08:40 +00:00
if ( line < 0 | | line > text . size ( ) )
return ;
_remove_text ( line , 0 , line , text [ line ] . length ( ) ) ;
_insert_text ( line , 0 , new_text ) ;
2017-03-05 15:44:50 +00:00
if ( cursor . line = = line ) {
cursor . column = MIN ( cursor . column , new_text . length ( ) ) ;
2015-05-05 03:17:22 +00:00
}
2018-09-08 12:20:45 +00:00
if ( is_selection_active ( ) & & line = = selection . to_line & & selection . to_column > text [ line ] . length ( ) ) {
selection . to_column = text [ line ] . length ( ) ;
}
2014-11-12 14:39:21 +00:00
}
2017-03-05 15:44:50 +00:00
void TextEdit : : insert_at ( const String & p_text , int at ) {
_insert_text ( at , 0 , p_text + " \n " ) ;
2019-08-20 07:38:57 +00:00
if ( cursor . line > = at ) {
// offset cursor when located after inserted line
+ + cursor . line ;
}
if ( is_selection_active ( ) ) {
if ( selection . from_line > = at ) {
// offset selection when located after inserted line
+ + selection . from_line ;
+ + selection . to_line ;
} else if ( selection . to_line > = at ) {
// extend selection that includes inserted line
+ + selection . to_line ;
}
}
2014-11-12 14:39:21 +00:00
}
2014-02-10 01:10:30 +00:00
void TextEdit : : set_show_line_numbers ( bool p_show ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
line_numbers = p_show ;
2015-01-02 18:08:40 +00:00
update ( ) ;
2014-02-10 01:10:30 +00:00
}
2016-10-10 11:43:09 +00:00
void TextEdit : : set_line_numbers_zero_padded ( bool p_zero_padded ) {
2017-03-05 15:44:50 +00:00
line_numbers_zero_padded = p_zero_padded ;
2016-10-10 11:43:09 +00:00
update ( ) ;
}
2016-07-11 14:11:35 +00:00
bool TextEdit : : is_show_line_numbers_enabled ( ) const {
return line_numbers ;
}
2016-10-10 08:34:51 +00:00
void TextEdit : : set_show_line_length_guideline ( bool p_show ) {
2017-03-05 15:44:50 +00:00
line_length_guideline = p_show ;
2016-10-10 08:34:51 +00:00
update ( ) ;
}
void TextEdit : : set_line_length_guideline_column ( int p_column ) {
2017-03-05 15:44:50 +00:00
line_length_guideline_col = p_column ;
2016-10-10 08:34:51 +00:00
update ( ) ;
}
2019-04-19 23:51:25 +00:00
void TextEdit : : set_bookmark_gutter_enabled ( bool p_draw ) {
draw_bookmark_gutter = p_draw ;
update ( ) ;
}
bool TextEdit : : is_bookmark_gutter_enabled ( ) const {
return draw_bookmark_gutter ;
}
2018-05-30 12:02:51 +00:00
void TextEdit : : set_breakpoint_gutter_enabled ( bool p_draw ) {
2016-05-26 13:17:14 +00:00
draw_breakpoint_gutter = p_draw ;
update ( ) ;
}
2018-05-30 12:02:51 +00:00
bool TextEdit : : is_breakpoint_gutter_enabled ( ) const {
2016-05-26 13:17:14 +00:00
return draw_breakpoint_gutter ;
}
void TextEdit : : set_breakpoint_gutter_width ( int p_gutter_width ) {
breakpoint_gutter_width = p_gutter_width ;
update ( ) ;
}
int TextEdit : : get_breakpoint_gutter_width ( ) const {
return cache . breakpoint_gutter_width ;
}
2017-11-12 23:12:17 +00:00
void TextEdit : : set_draw_fold_gutter ( bool p_draw ) {
draw_fold_gutter = p_draw ;
update ( ) ;
}
bool TextEdit : : is_drawing_fold_gutter ( ) const {
return draw_fold_gutter ;
}
void TextEdit : : set_fold_gutter_width ( int p_gutter_width ) {
fold_gutter_width = p_gutter_width ;
update ( ) ;
}
int TextEdit : : get_fold_gutter_width ( ) const {
return cache . fold_gutter_width ;
}
2019-04-20 11:51:25 +00:00
void TextEdit : : set_draw_info_gutter ( bool p_draw ) {
draw_info_gutter = p_draw ;
update ( ) ;
}
bool TextEdit : : is_drawing_info_gutter ( ) const {
return draw_info_gutter ;
}
void TextEdit : : set_info_gutter_width ( int p_gutter_width ) {
info_gutter_width = p_gutter_width ;
update ( ) ;
}
int TextEdit : : get_info_gutter_width ( ) const {
return info_gutter_width ;
}
2019-08-11 18:31:19 +00:00
void TextEdit : : set_draw_minimap ( bool p_draw ) {
draw_minimap = p_draw ;
update ( ) ;
}
bool TextEdit : : is_drawing_minimap ( ) const {
return draw_minimap ;
}
void TextEdit : : set_minimap_width ( int p_minimap_width ) {
minimap_width = p_minimap_width ;
update ( ) ;
}
int TextEdit : : get_minimap_width ( ) const {
return minimap_width ;
}
2019-06-08 11:37:57 +00:00
void TextEdit : : set_hiding_enabled ( bool p_enabled ) {
2017-11-16 04:00:27 +00:00
if ( ! p_enabled )
unhide_all_lines ( ) ;
2017-11-12 23:12:17 +00:00
hiding_enabled = p_enabled ;
update ( ) ;
}
2019-06-08 11:37:57 +00:00
bool TextEdit : : is_hiding_enabled ( ) const {
2017-11-12 23:12:17 +00:00
return hiding_enabled ;
}
2017-10-22 12:38:00 +00:00
void TextEdit : : set_highlight_current_line ( bool p_enabled ) {
highlight_current_line = p_enabled ;
update ( ) ;
}
bool TextEdit : : is_highlight_current_line_enabled ( ) const {
return highlight_current_line ;
}
2015-10-17 13:29:54 +00:00
bool TextEdit : : is_text_field ( ) const {
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
return true ;
2015-10-17 13:29:54 +00:00
}
2016-05-16 23:25:17 +00:00
void TextEdit : : menu_option ( int p_option ) {
2017-03-05 15:44:50 +00:00
switch ( p_option ) {
2016-05-16 23:25:17 +00:00
case MENU_CUT : {
2016-09-19 12:17:48 +00:00
if ( ! readonly ) {
cut ( ) ;
}
2016-05-16 23:25:17 +00:00
} break ;
case MENU_COPY : {
copy ( ) ;
} break ;
case MENU_PASTE : {
2016-09-19 12:17:48 +00:00
if ( ! readonly ) {
paste ( ) ;
}
2016-05-16 23:25:17 +00:00
} break ;
case MENU_CLEAR : {
2016-09-19 12:17:48 +00:00
if ( ! readonly ) {
clear ( ) ;
}
2016-05-16 23:25:17 +00:00
} break ;
case MENU_SELECT_ALL : {
select_all ( ) ;
} break ;
case MENU_UNDO : {
undo ( ) ;
} break ;
2018-11-29 17:21:43 +00:00
case MENU_REDO : {
redo ( ) ;
}
}
2016-05-16 23:25:17 +00:00
}
2016-09-12 13:52:29 +00:00
void TextEdit : : set_select_identifiers_on_hover ( bool p_enable ) {
2017-03-05 15:44:50 +00:00
select_identifiers_enabled = p_enable ;
2016-09-12 13:52:29 +00:00
}
bool TextEdit : : is_selecting_identifiers_on_hover_enabled ( ) const {
return select_identifiers_enabled ;
}
2016-09-29 07:12:45 +00:00
void TextEdit : : set_context_menu_enabled ( bool p_enable ) {
context_menu_enabled = p_enable ;
}
2016-09-12 13:52:29 +00:00
2017-11-09 20:46:29 +00:00
bool TextEdit : : is_context_menu_enabled ( ) {
return context_menu_enabled ;
}
2016-05-16 23:25:17 +00:00
PopupMenu * TextEdit : : get_menu ( ) const {
return menu ;
}
2014-02-10 01:10:30 +00:00
void TextEdit : : _bind_methods ( ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
ClassDB : : bind_method ( D_METHOD ( " _gui_input " ) , & TextEdit : : _gui_input ) ;
ClassDB : : bind_method ( D_METHOD ( " _scroll_moved " ) , & TextEdit : : _scroll_moved ) ;
ClassDB : : bind_method ( D_METHOD ( " _cursor_changed_emit " ) , & TextEdit : : _cursor_changed_emit ) ;
ClassDB : : bind_method ( D_METHOD ( " _text_changed_emit " ) , & TextEdit : : _text_changed_emit ) ;
ClassDB : : bind_method ( D_METHOD ( " _push_current_op " ) , & TextEdit : : _push_current_op ) ;
ClassDB : : bind_method ( D_METHOD ( " _click_selection_held " ) , & TextEdit : : _click_selection_held ) ;
ClassDB : : bind_method ( D_METHOD ( " _toggle_draw_caret " ) , & TextEdit : : _toggle_draw_caret ) ;
2017-08-22 19:02:08 +00:00
ClassDB : : bind_method ( D_METHOD ( " _v_scroll_input " ) , & TextEdit : : _v_scroll_input ) ;
2019-01-05 16:58:54 +00:00
ClassDB : : bind_method ( D_METHOD ( " _update_wrap_at " ) , & TextEdit : : _update_wrap_at ) ;
2016-03-08 23:00:52 +00:00
2017-08-20 15:45:01 +00:00
BIND_ENUM_CONSTANT ( SEARCH_MATCH_CASE ) ;
BIND_ENUM_CONSTANT ( SEARCH_WHOLE_WORDS ) ;
BIND_ENUM_CONSTANT ( SEARCH_BACKWARDS ) ;
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
/*
2017-11-05 15:54:00 +00:00
ClassDB : : bind_method ( D_METHOD ( " delete_char " ) , & TextEdit : : delete_char ) ;
ClassDB : : bind_method ( D_METHOD ( " delete_line " ) , & TextEdit : : delete_line ) ;
2014-02-10 01:10:30 +00:00
*/
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_text " , " text " ) , & TextEdit : : set_text ) ;
ClassDB : : bind_method ( D_METHOD ( " insert_text_at_cursor " , " text " ) , & TextEdit : : insert_text_at_cursor ) ;
2017-02-13 11:47:24 +00:00
2017-03-05 15:44:50 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_line_count " ) , & TextEdit : : get_line_count ) ;
ClassDB : : bind_method ( D_METHOD ( " get_text " ) , & TextEdit : : get_text ) ;
ClassDB : : bind_method ( D_METHOD ( " get_line " , " line " ) , & TextEdit : : get_line ) ;
2017-02-13 11:47:24 +00:00
2019-01-04 12:09:01 +00:00
ClassDB : : bind_method ( D_METHOD ( " center_viewport_to_cursor " ) , & TextEdit : : center_viewport_to_cursor ) ;
2017-04-15 17:48:10 +00:00
ClassDB : : bind_method ( D_METHOD ( " cursor_set_column " , " column " , " adjust_viewport " ) , & TextEdit : : cursor_set_column , DEFVAL ( true ) ) ;
2018-05-15 19:53:42 +00:00
ClassDB : : bind_method ( D_METHOD ( " cursor_set_line " , " line " , " adjust_viewport " , " can_be_hidden " , " wrap_index " ) , & TextEdit : : cursor_set_line , DEFVAL ( true ) , DEFVAL ( true ) , DEFVAL ( 0 ) ) ;
2017-02-13 11:47:24 +00:00
2017-03-05 15:44:50 +00:00
ClassDB : : bind_method ( D_METHOD ( " cursor_get_column " ) , & TextEdit : : cursor_get_column ) ;
ClassDB : : bind_method ( D_METHOD ( " cursor_get_line " ) , & TextEdit : : cursor_get_line ) ;
ClassDB : : bind_method ( D_METHOD ( " cursor_set_blink_enabled " , " enable " ) , & TextEdit : : cursor_set_blink_enabled ) ;
ClassDB : : bind_method ( D_METHOD ( " cursor_get_blink_enabled " ) , & TextEdit : : cursor_get_blink_enabled ) ;
ClassDB : : bind_method ( D_METHOD ( " cursor_set_blink_speed " , " blink_speed " ) , & TextEdit : : cursor_set_blink_speed ) ;
ClassDB : : bind_method ( D_METHOD ( " cursor_get_blink_speed " ) , & TextEdit : : cursor_get_blink_speed ) ;
2017-02-13 11:47:24 +00:00
ClassDB : : bind_method ( D_METHOD ( " cursor_set_block_mode " , " enable " ) , & TextEdit : : cursor_set_block_mode ) ;
ClassDB : : bind_method ( D_METHOD ( " cursor_is_block_mode " ) , & TextEdit : : cursor_is_block_mode ) ;
2017-12-20 01:36:47 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_right_click_moves_caret " , " enable " ) , & TextEdit : : set_right_click_moves_caret ) ;
ClassDB : : bind_method ( D_METHOD ( " is_right_click_moving_caret " ) , & TextEdit : : is_right_click_moving_caret ) ;
2017-03-05 15:44:50 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_readonly " , " enable " ) , & TextEdit : : set_readonly ) ;
2017-11-11 18:07:17 +00:00
ClassDB : : bind_method ( D_METHOD ( " is_readonly " ) , & TextEdit : : is_readonly ) ;
2018-01-26 01:41:17 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_wrap_enabled " , " enable " ) , & TextEdit : : set_wrap_enabled ) ;
ClassDB : : bind_method ( D_METHOD ( " is_wrap_enabled " ) , & TextEdit : : is_wrap_enabled ) ;
2017-11-09 20:46:29 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_context_menu_enabled " , " enable " ) , & TextEdit : : set_context_menu_enabled ) ;
ClassDB : : bind_method ( D_METHOD ( " is_context_menu_enabled " ) , & TextEdit : : is_context_menu_enabled ) ;
2017-03-05 15:44:50 +00:00
ClassDB : : bind_method ( D_METHOD ( " cut " ) , & TextEdit : : cut ) ;
ClassDB : : bind_method ( D_METHOD ( " copy " ) , & TextEdit : : copy ) ;
ClassDB : : bind_method ( D_METHOD ( " paste " ) , & TextEdit : : paste ) ;
2017-12-16 19:34:16 +00:00
2017-03-05 15:44:50 +00:00
ClassDB : : bind_method ( D_METHOD ( " select " , " from_line " , " from_column " , " to_line " , " to_column " ) , & TextEdit : : select ) ;
2017-12-16 19:34:16 +00:00
ClassDB : : bind_method ( D_METHOD ( " select_all " ) , & TextEdit : : select_all ) ;
ClassDB : : bind_method ( D_METHOD ( " deselect " ) , & TextEdit : : deselect ) ;
2017-03-05 15:44:50 +00:00
ClassDB : : bind_method ( D_METHOD ( " is_selection_active " ) , & TextEdit : : is_selection_active ) ;
ClassDB : : bind_method ( D_METHOD ( " get_selection_from_line " ) , & TextEdit : : get_selection_from_line ) ;
ClassDB : : bind_method ( D_METHOD ( " get_selection_from_column " ) , & TextEdit : : get_selection_from_column ) ;
ClassDB : : bind_method ( D_METHOD ( " get_selection_to_line " ) , & TextEdit : : get_selection_to_line ) ;
ClassDB : : bind_method ( D_METHOD ( " get_selection_to_column " ) , & TextEdit : : get_selection_to_column ) ;
ClassDB : : bind_method ( D_METHOD ( " get_selection_text " ) , & TextEdit : : get_selection_text ) ;
ClassDB : : bind_method ( D_METHOD ( " get_word_under_cursor " ) , & TextEdit : : get_word_under_cursor ) ;
2017-08-09 11:54:55 +00:00
ClassDB : : bind_method ( D_METHOD ( " search " , " key " , " flags " , " from_line " , " from_column " ) , & TextEdit : : _search_bind ) ;
2017-03-05 15:44:50 +00:00
ClassDB : : bind_method ( D_METHOD ( " undo " ) , & TextEdit : : undo ) ;
ClassDB : : bind_method ( D_METHOD ( " redo " ) , & TextEdit : : redo ) ;
ClassDB : : bind_method ( D_METHOD ( " clear_undo_history " ) , & TextEdit : : clear_undo_history ) ;
2017-02-13 11:47:24 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_show_line_numbers " , " enable " ) , & TextEdit : : set_show_line_numbers ) ;
ClassDB : : bind_method ( D_METHOD ( " is_show_line_numbers_enabled " ) , & TextEdit : : is_show_line_numbers_enabled ) ;
2019-04-24 07:59:17 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_draw_tabs " ) , & TextEdit : : set_draw_tabs ) ;
ClassDB : : bind_method ( D_METHOD ( " is_drawing_tabs " ) , & TextEdit : : is_drawing_tabs ) ;
2019-05-01 09:31:10 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_draw_spaces " ) , & TextEdit : : set_draw_spaces ) ;
ClassDB : : bind_method ( D_METHOD ( " is_drawing_spaces " ) , & TextEdit : : is_drawing_spaces ) ;
2018-05-30 12:02:51 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_breakpoint_gutter_enabled " , " enable " ) , & TextEdit : : set_breakpoint_gutter_enabled ) ;
ClassDB : : bind_method ( D_METHOD ( " is_breakpoint_gutter_enabled " ) , & TextEdit : : is_breakpoint_gutter_enabled ) ;
2019-04-24 07:59:17 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_draw_fold_gutter " ) , & TextEdit : : set_draw_fold_gutter ) ;
ClassDB : : bind_method ( D_METHOD ( " is_drawing_fold_gutter " ) , & TextEdit : : is_drawing_fold_gutter ) ;
2017-02-13 11:47:24 +00:00
2017-11-12 23:12:17 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_hiding_enabled " , " enable " ) , & TextEdit : : set_hiding_enabled ) ;
ClassDB : : bind_method ( D_METHOD ( " is_hiding_enabled " ) , & TextEdit : : is_hiding_enabled ) ;
ClassDB : : bind_method ( D_METHOD ( " set_line_as_hidden " , " line " , " enable " ) , & TextEdit : : set_line_as_hidden ) ;
2017-12-25 14:19:56 +00:00
ClassDB : : bind_method ( D_METHOD ( " is_line_hidden " , " line " ) , & TextEdit : : is_line_hidden ) ;
2017-11-16 04:00:27 +00:00
ClassDB : : bind_method ( D_METHOD ( " fold_all_lines " ) , & TextEdit : : fold_all_lines ) ;
2017-11-12 23:12:17 +00:00
ClassDB : : bind_method ( D_METHOD ( " unhide_all_lines " ) , & TextEdit : : unhide_all_lines ) ;
ClassDB : : bind_method ( D_METHOD ( " fold_line " , " line " ) , & TextEdit : : fold_line ) ;
ClassDB : : bind_method ( D_METHOD ( " unfold_line " , " line " ) , & TextEdit : : unfold_line ) ;
2017-12-08 18:17:10 +00:00
ClassDB : : bind_method ( D_METHOD ( " toggle_fold_line " , " line " ) , & TextEdit : : toggle_fold_line ) ;
2017-11-12 23:12:17 +00:00
ClassDB : : bind_method ( D_METHOD ( " can_fold " , " line " ) , & TextEdit : : can_fold ) ;
ClassDB : : bind_method ( D_METHOD ( " is_folded " , " line " ) , & TextEdit : : is_folded ) ;
2017-02-13 11:47:24 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_highlight_all_occurrences " , " enable " ) , & TextEdit : : set_highlight_all_occurrences ) ;
ClassDB : : bind_method ( D_METHOD ( " is_highlight_all_occurrences_enabled " ) , & TextEdit : : is_highlight_all_occurrences_enabled ) ;
2017-09-27 17:24:05 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_override_selected_font_color " , " override " ) , & TextEdit : : set_override_selected_font_color ) ;
ClassDB : : bind_method ( D_METHOD ( " is_overriding_selected_font_color " ) , & TextEdit : : is_overriding_selected_font_color ) ;
2017-03-05 15:44:50 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_syntax_coloring " , " enable " ) , & TextEdit : : set_syntax_coloring ) ;
ClassDB : : bind_method ( D_METHOD ( " is_syntax_coloring_enabled " ) , & TextEdit : : is_syntax_coloring_enabled ) ;
2017-02-13 11:47:24 +00:00
2017-10-22 12:38:00 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_highlight_current_line " , " enabled " ) , & TextEdit : : set_highlight_current_line ) ;
ClassDB : : bind_method ( D_METHOD ( " is_highlight_current_line_enabled " ) , & TextEdit : : is_highlight_current_line_enabled ) ;
2017-08-19 14:23:45 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_smooth_scroll_enable " , " enable " ) , & TextEdit : : set_smooth_scroll_enabled ) ;
ClassDB : : bind_method ( D_METHOD ( " is_smooth_scroll_enabled " ) , & TextEdit : : is_smooth_scroll_enabled ) ;
2017-08-21 18:13:24 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_v_scroll_speed " , " speed " ) , & TextEdit : : set_v_scroll_speed ) ;
ClassDB : : bind_method ( D_METHOD ( " get_v_scroll_speed " ) , & TextEdit : : get_v_scroll_speed ) ;
2017-08-19 14:23:45 +00:00
2017-03-05 15:44:50 +00:00
ClassDB : : bind_method ( D_METHOD ( " add_keyword_color " , " keyword " , " color " ) , & TextEdit : : add_keyword_color ) ;
2018-04-02 11:41:44 +00:00
ClassDB : : bind_method ( D_METHOD ( " has_keyword_color " , " keyword " ) , & TextEdit : : has_keyword_color ) ;
ClassDB : : bind_method ( D_METHOD ( " get_keyword_color " , " keyword " ) , & TextEdit : : get_keyword_color ) ;
2017-03-05 15:44:50 +00:00
ClassDB : : bind_method ( D_METHOD ( " add_color_region " , " begin_key " , " end_key " , " color " , " line_only " ) , & TextEdit : : add_color_region , DEFVAL ( false ) ) ;
ClassDB : : bind_method ( D_METHOD ( " clear_colors " ) , & TextEdit : : clear_colors ) ;
2017-07-22 10:11:42 +00:00
ClassDB : : bind_method ( D_METHOD ( " menu_option " , " option " ) , & TextEdit : : menu_option ) ;
2017-08-09 11:19:41 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_menu " ) , & TextEdit : : get_menu ) ;
2016-03-08 23:00:52 +00:00
2018-05-30 12:02:51 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_breakpoints " ) , & TextEdit : : get_breakpoints_array ) ;
ClassDB : : bind_method ( D_METHOD ( " remove_breakpoints " ) , & TextEdit : : remove_breakpoints ) ;
2019-08-11 18:31:19 +00:00
ClassDB : : bind_method ( D_METHOD ( " draw_minimap " , " draw " ) , & TextEdit : : set_draw_minimap ) ;
ClassDB : : bind_method ( D_METHOD ( " is_drawing_minimap " ) , & TextEdit : : is_drawing_minimap ) ;
ClassDB : : bind_method ( D_METHOD ( " set_minimap_width " , " width " ) , & TextEdit : : set_minimap_width ) ;
ClassDB : : bind_method ( D_METHOD ( " get_minimap_width " ) , & TextEdit : : get_minimap_width ) ;
2017-11-12 15:10:26 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : STRING , " text " , PROPERTY_HINT_MULTILINE_TEXT ) , " set_text " , " get_text " ) ;
2017-11-11 18:07:17 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " readonly " ) , " set_readonly " , " is_readonly " ) ;
2017-10-22 12:38:00 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " highlight_current_line " ) , " set_highlight_current_line " , " is_highlight_current_line_enabled " ) ;
2017-02-12 00:11:37 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " syntax_highlighting " ) , " set_syntax_coloring " , " is_syntax_coloring_enabled " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " show_line_numbers " ) , " set_show_line_numbers " , " is_show_line_numbers_enabled " ) ;
2019-04-24 07:59:17 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " draw_tabs " ) , " set_draw_tabs " , " is_drawing_tabs " ) ;
2019-05-01 09:31:10 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " draw_spaces " ) , " set_draw_spaces " , " is_drawing_spaces " ) ;
2018-05-30 12:02:51 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " breakpoint_gutter " ) , " set_breakpoint_gutter_enabled " , " is_breakpoint_gutter_enabled " ) ;
2019-04-24 07:59:17 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " fold_gutter " ) , " set_draw_fold_gutter " , " is_drawing_fold_gutter " ) ;
2017-02-12 00:11:37 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " highlight_all_occurrences " ) , " set_highlight_all_occurrences " , " is_highlight_all_occurrences_enabled " ) ;
2017-09-27 17:24:05 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " override_selected_font_color " ) , " set_override_selected_font_color " , " is_overriding_selected_font_color " ) ;
2017-11-09 20:46:29 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " context_menu_enabled " ) , " set_context_menu_enabled " , " is_context_menu_enabled " ) ;
2017-08-19 14:23:45 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " smooth_scrolling " ) , " set_smooth_scroll_enable " , " is_smooth_scroll_enabled " ) ;
2017-08-21 18:13:24 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : REAL , " v_scroll_speed " ) , " set_v_scroll_speed " , " get_v_scroll_speed " ) ;
2017-11-12 23:12:17 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " hiding_enabled " ) , " set_hiding_enabled " , " is_hiding_enabled " ) ;
2018-01-26 01:41:17 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " wrap_enabled " ) , " set_wrap_enabled " , " is_wrap_enabled " ) ;
2016-07-11 14:11:35 +00:00
2019-08-11 18:31:19 +00:00
ADD_GROUP ( " Minimap " , " minimap_ " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " minimap_draw " ) , " draw_minimap " , " is_drawing_minimap " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " minimap_width " ) , " set_minimap_width " , " get_minimap_width " ) ;
2017-03-05 15:44:50 +00:00
ADD_GROUP ( " Caret " , " caret_ " ) ;
2017-02-12 00:11:37 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " caret_block_mode " ) , " cursor_set_block_mode " , " cursor_is_block_mode " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " caret_blink " ) , " cursor_set_blink_enabled " , " cursor_get_blink_enabled " ) ;
2018-11-08 14:30:02 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : REAL , " caret_blink_speed " , PROPERTY_HINT_RANGE , " 0.1,10,0.01 " ) , " cursor_set_blink_speed " , " cursor_get_blink_speed " ) ;
2017-12-20 01:36:47 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " caret_moving_by_right_click " ) , " set_right_click_moves_caret " , " is_right_click_moving_caret " ) ;
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
ADD_SIGNAL ( MethodInfo ( " cursor_changed " ) ) ;
ADD_SIGNAL ( MethodInfo ( " text_changed " ) ) ;
ADD_SIGNAL ( MethodInfo ( " request_completion " ) ) ;
2017-03-05 15:44:50 +00:00
ADD_SIGNAL ( MethodInfo ( " breakpoint_toggled " , PropertyInfo ( Variant : : INT , " row " ) ) ) ;
ADD_SIGNAL ( MethodInfo ( " symbol_lookup " , PropertyInfo ( Variant : : STRING , " symbol " ) , PropertyInfo ( Variant : : INT , " row " ) , PropertyInfo ( Variant : : INT , " column " ) ) ) ;
2019-04-20 11:51:25 +00:00
ADD_SIGNAL ( MethodInfo ( " info_clicked " , PropertyInfo ( Variant : : INT , " row " ) , PropertyInfo ( Variant : : STRING , " info " ) ) ) ;
2016-05-16 23:25:17 +00:00
2017-08-20 15:45:01 +00:00
BIND_ENUM_CONSTANT ( MENU_CUT ) ;
BIND_ENUM_CONSTANT ( MENU_COPY ) ;
BIND_ENUM_CONSTANT ( MENU_PASTE ) ;
BIND_ENUM_CONSTANT ( MENU_CLEAR ) ;
BIND_ENUM_CONSTANT ( MENU_SELECT_ALL ) ;
BIND_ENUM_CONSTANT ( MENU_UNDO ) ;
2018-11-29 17:21:43 +00:00
BIND_ENUM_CONSTANT ( MENU_REDO ) ;
2017-08-20 15:45:01 +00:00
BIND_ENUM_CONSTANT ( MENU_MAX ) ;
2016-05-16 23:25:17 +00:00
2017-03-05 15:44:50 +00:00
GLOBAL_DEF ( " gui/timers/text_edit_idle_detect_sec " , 3 ) ;
2019-08-18 15:56:24 +00:00
ProjectSettings : : get_singleton ( ) - > set_custom_property_info ( " gui/timers/text_edit_idle_detect_sec " , PropertyInfo ( Variant : : REAL , " gui/timers/text_edit_idle_detect_sec " , PROPERTY_HINT_RANGE , " 0,10,0.01,or_greater " ) ) ; // No negative numbers.
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
TextEdit : : TextEdit ( ) {
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
setting_row = false ;
draw_tabs = false ;
2019-04-23 23:33:20 +00:00
draw_spaces = false ;
2017-09-27 17:24:05 +00:00
override_selected_font_color = false ;
2017-03-05 15:44:50 +00:00
draw_caret = true ;
max_chars = 0 ;
2015-01-02 18:08:40 +00:00
clear ( ) ;
2018-01-26 01:41:17 +00:00
wrap_enabled = false ;
wrap_right_offset = 10 ;
2015-01-02 18:08:40 +00:00
set_focus_mode ( FOCUS_ALL ) ;
2018-04-02 11:41:44 +00:00
syntax_highlighter = NULL ;
2015-01-02 18:08:40 +00:00
_update_caches ( ) ;
2017-03-05 15:44:50 +00:00
cache . row_height = 1 ;
cache . line_spacing = 1 ;
cache . line_number_w = 1 ;
cache . breakpoint_gutter_width = 0 ;
2016-05-26 13:17:14 +00:00
breakpoint_gutter_width = 0 ;
2017-11-12 23:12:17 +00:00
cache . fold_gutter_width = 0 ;
fold_gutter_width = 0 ;
2019-04-20 11:51:25 +00:00
info_gutter_width = 0 ;
cache . info_gutter_width = 0 ;
2018-09-11 16:25:40 +00:00
set_default_cursor_shape ( CURSOR_IBEAM ) ;
2015-06-07 01:06:58 +00:00
2017-04-17 13:24:30 +00:00
indent_size = 4 ;
text . set_indent_size ( indent_size ) ;
2015-01-02 18:08:40 +00:00
text . clear ( ) ;
text . set_color_regions ( & color_regions ) ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
h_scroll = memnew ( HScrollBar ) ;
v_scroll = memnew ( VScrollBar ) ;
2016-03-08 23:00:52 +00:00
2015-01-02 18:08:40 +00:00
add_child ( h_scroll ) ;
add_child ( v_scroll ) ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
updating_scrolls = false ;
selection . active = false ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
h_scroll - > connect ( " value_changed " , this , " _scroll_moved " ) ;
v_scroll - > connect ( " value_changed " , this , " _scroll_moved " ) ;
2016-03-08 23:00:52 +00:00
2017-08-22 19:02:08 +00:00
v_scroll - > connect ( " scrolling " , this , " _v_scroll_input " ) ;
2017-03-05 15:44:50 +00:00
cursor_changed_dirty = false ;
text_changed_dirty = false ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
selection . selecting_mode = Selection : : MODE_NONE ;
selection . selecting_line = 0 ;
selection . selecting_column = 0 ;
selection . selecting_text = false ;
selection . active = false ;
syntax_coloring = false ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
block_caret = false ;
caret_blink_enabled = false ;
2016-05-09 18:21:55 +00:00
caret_blink_timer = memnew ( Timer ) ;
add_child ( caret_blink_timer ) ;
caret_blink_timer - > set_wait_time ( 0.65 ) ;
2017-03-05 15:44:50 +00:00
caret_blink_timer - > connect ( " timeout " , this , " _toggle_draw_caret " ) ;
2016-05-09 18:21:55 +00:00
cursor_set_blink_enabled ( false ) ;
2017-12-20 01:36:47 +00:00
right_click_moves_caret = true ;
2016-05-09 18:21:55 +00:00
2017-03-05 15:44:50 +00:00
idle_detect = memnew ( Timer ) ;
2015-01-02 18:08:40 +00:00
add_child ( idle_detect ) ;
idle_detect - > set_one_shot ( true ) ;
2017-01-05 12:16:00 +00:00
idle_detect - > set_wait_time ( GLOBAL_GET ( " gui/timers/text_edit_idle_detect_sec " ) ) ;
2017-03-05 15:44:50 +00:00
idle_detect - > connect ( " timeout " , this , " _push_current_op " ) ;
2015-12-09 18:56:41 +00:00
2017-03-05 15:44:50 +00:00
click_select_held = memnew ( Timer ) ;
2015-12-09 18:56:41 +00:00
add_child ( click_select_held ) ;
click_select_held - > set_wait_time ( 0.05 ) ;
2017-03-05 15:44:50 +00:00
click_select_held - > connect ( " timeout " , this , " _click_selection_held " ) ;
2016-03-08 23:00:52 +00:00
2017-03-05 15:44:50 +00:00
current_op . type = TextOperation : : TYPE_NONE ;
undo_enabled = true ;
undo_stack_pos = NULL ;
setting_text = false ;
last_dblclk = 0 ;
current_op . version = 0 ;
version = 0 ;
saved_version = 0 ;
completion_enabled = false ;
completion_active = false ;
completion_line_ofs = 0 ;
tooltip_obj = NULL ;
line_numbers = false ;
line_numbers_zero_padded = false ;
line_length_guideline = false ;
line_length_guideline_col = 80 ;
2019-04-19 23:51:25 +00:00
draw_bookmark_gutter = false ;
2017-03-05 15:44:50 +00:00
draw_breakpoint_gutter = false ;
2017-11-12 23:12:17 +00:00
draw_fold_gutter = false ;
2019-04-20 11:51:25 +00:00
draw_info_gutter = false ;
2017-11-12 23:12:17 +00:00
hiding_enabled = false ;
2017-03-05 15:44:50 +00:00
next_operation_is_complex = false ;
scroll_past_end_of_file_enabled = false ;
auto_brace_completion_enabled = false ;
brace_matching_enabled = false ;
highlight_all_occurrences = false ;
2017-10-22 12:38:00 +00:00
highlight_current_line = false ;
2017-04-17 13:24:30 +00:00
indent_using_spaces = false ;
space_indent = " " ;
2017-03-05 15:44:50 +00:00
auto_indent = false ;
2016-03-31 19:49:30 +00:00
insert_mode = false ;
2017-03-05 15:44:50 +00:00
window_has_focus = true ;
select_identifiers_enabled = false ;
2017-08-19 14:23:45 +00:00
smooth_scroll_enabled = false ;
scrolling = false ;
2019-08-11 18:31:19 +00:00
minimap_clicked = false ;
dragging_minimap = false ;
2019-08-25 13:38:14 +00:00
can_drag_minimap = false ;
minimap_scroll_ratio = 0 ;
minimap_scroll_click_pos = 0 ;
2019-08-11 18:31:19 +00:00
dragging_selection = false ;
2017-08-19 14:23:45 +00:00
target_v_scroll = 0 ;
2017-08-21 18:13:24 +00:00
v_scroll_speed = 80 ;
2019-08-11 18:31:19 +00:00
draw_minimap = false ;
minimap_width = 80 ;
minimap_char_size = Point2 ( 1 , 2 ) ;
minimap_line_spacing = 1 ;
2016-05-16 23:25:17 +00:00
2017-03-05 15:44:50 +00:00
context_menu_enabled = true ;
menu = memnew ( PopupMenu ) ;
2016-05-16 23:25:17 +00:00
add_child ( menu ) ;
2019-08-18 15:56:24 +00:00
readonly = true ; // Initialise to opposite first, so we get past the early-out in set_readonly.
2019-04-21 23:09:52 +00:00
set_readonly ( false ) ;
2017-03-05 15:44:50 +00:00
menu - > connect ( " id_pressed " , this , " menu_option " ) ;
2019-04-12 02:21:48 +00:00
first_draw = true ;
2019-04-22 16:20:27 +00:00
executing_line = - 1 ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
TextEdit : : ~ TextEdit ( ) {
2017-12-16 19:34:16 +00:00
}
2018-04-02 11:41:44 +00:00
///////////////////////////////////////////////////////////////////////////////
Map < int , TextEdit : : HighlighterInfo > TextEdit : : _get_line_syntax_highlighting ( int p_line ) {
2019-08-11 18:29:09 +00:00
if ( syntax_highlighting_cache . has ( p_line ) ) {
return syntax_highlighting_cache [ p_line ] ;
}
2018-04-02 11:41:44 +00:00
if ( syntax_highlighter ! = NULL ) {
2019-08-11 18:29:09 +00:00
Map < int , HighlighterInfo > color_map = syntax_highlighter - > _get_line_syntax_highlighting ( p_line ) ;
syntax_highlighting_cache [ p_line ] = color_map ;
return color_map ;
2018-04-02 11:41:44 +00:00
}
Map < int , HighlighterInfo > color_map ;
bool prev_is_char = false ;
bool prev_is_number = false ;
bool in_keyword = false ;
bool in_word = false ;
bool in_function_name = false ;
bool in_member_variable = false ;
bool is_hex_notation = false ;
Color keyword_color ;
Color color ;
2018-04-07 13:14:19 +00:00
int in_region = _is_line_in_region ( p_line ) ;
2018-04-02 11:41:44 +00:00
int deregion = 0 ;
const Map < int , TextEdit : : Text : : ColorRegionInfo > cri_map = text . get_color_region_info ( p_line ) ;
const String & str = text [ p_line ] ;
Color prev_color ;
for ( int j = 0 ; j < str . length ( ) ; j + + ) {
HighlighterInfo highlighter_info ;
if ( deregion > 0 ) {
deregion - - ;
if ( deregion = = 0 ) {
in_region = - 1 ;
}
}
if ( deregion ! = 0 ) {
if ( color ! = prev_color ) {
prev_color = color ;
highlighter_info . color = color ;
color_map [ j ] = highlighter_info ;
}
continue ;
}
color = cache . font_color ;
bool is_char = _is_text_char ( str [ j ] ) ;
bool is_symbol = _is_symbol ( str [ j ] ) ;
bool is_number = _is_number ( str [ j ] ) ;
2019-08-18 15:56:24 +00:00
// Allow ABCDEF in hex notation.
2018-04-02 11:41:44 +00:00
if ( is_hex_notation & & ( _is_hex_symbol ( str [ j ] ) | | is_number ) ) {
is_number = true ;
} else {
is_hex_notation = false ;
}
2019-08-18 15:56:24 +00:00
// Check for dot or underscore or 'x' for hex notation in floating point number or 'e' for scientific notation.
2018-09-08 12:41:46 +00:00
if ( ( str [ j ] = = ' . ' | | str [ j ] = = ' x ' | | str [ j ] = = ' _ ' | | str [ j ] = = ' f ' | | str [ j ] = = ' e ' ) & & ! in_word & & prev_is_number & & ! is_number ) {
2018-04-02 11:41:44 +00:00
is_number = true ;
is_symbol = false ;
is_char = false ;
if ( str [ j ] = = ' x ' & & str [ j - 1 ] = = ' 0 ' ) {
is_hex_notation = true ;
}
}
if ( ! in_word & & _is_char ( str [ j ] ) & & ! is_number ) {
in_word = true ;
}
if ( ( in_keyword | | in_word ) & & ! is_hex_notation ) {
is_number = false ;
}
if ( is_symbol & & str [ j ] ! = ' . ' & & in_word ) {
in_word = false ;
}
if ( is_symbol & & cri_map . has ( j ) ) {
const TextEdit : : Text : : ColorRegionInfo & cri = cri_map [ j ] ;
if ( in_region = = - 1 ) {
if ( ! cri . end ) {
in_region = cri . region ;
}
2019-08-18 15:56:24 +00:00
} else if ( in_region = = cri . region & & ! color_regions [ cri . region ] . line_only ) { // Ignore otherwise.
2018-04-02 11:41:44 +00:00
if ( cri . end | | color_regions [ cri . region ] . eq ) {
deregion = color_regions [ cri . region ] . eq ? color_regions [ cri . region ] . begin_key . length ( ) : color_regions [ cri . region ] . end_key . length ( ) ;
}
}
}
if ( ! is_char ) {
in_keyword = false ;
}
if ( in_region = = - 1 & & ! in_keyword & & is_char & & ! prev_is_char ) {
int to = j ;
while ( to < str . length ( ) & & _is_text_char ( str [ to ] ) )
to + + ;
uint32_t hash = String : : hash ( & str [ j ] , to - j ) ;
StrRange range ( & str [ j ] , to - j ) ;
const Color * col = keywords . custom_getptr ( range , hash ) ;
if ( ! col ) {
col = member_keywords . custom_getptr ( range , hash ) ;
if ( col ) {
for ( int k = j - 1 ; k > = 0 ; k - - ) {
if ( str [ k ] = = ' . ' ) {
2019-08-18 15:56:24 +00:00
col = NULL ; // Member indexing not allowed.
2018-04-02 11:41:44 +00:00
break ;
} else if ( str [ k ] > 32 ) {
break ;
}
}
}
}
if ( col ) {
in_keyword = true ;
keyword_color = * col ;
}
}
if ( ! in_function_name & & in_word & & ! in_keyword ) {
int k = j ;
while ( k < str . length ( ) & & ! _is_symbol ( str [ k ] ) & & str [ k ] ! = ' \t ' & & str [ k ] ! = ' ' ) {
k + + ;
}
2019-08-18 15:56:24 +00:00
// Check for space between name and bracket.
2018-04-02 11:41:44 +00:00
while ( k < str . length ( ) & & ( str [ k ] = = ' \t ' | | str [ k ] = = ' ' ) ) {
k + + ;
}
if ( str [ k ] = = ' ( ' ) {
in_function_name = true ;
}
}
if ( ! in_function_name & & ! in_member_variable & & ! in_keyword & & ! is_number & & in_word ) {
int k = j ;
while ( k > 0 & & ! _is_symbol ( str [ k ] ) & & str [ k ] ! = ' \t ' & & str [ k ] ! = ' ' ) {
k - - ;
}
if ( str [ k ] = = ' . ' ) {
in_member_variable = true ;
}
}
if ( is_symbol ) {
in_function_name = false ;
in_member_variable = false ;
}
if ( in_region > = 0 )
color = color_regions [ in_region ] . color ;
else if ( in_keyword )
color = keyword_color ;
else if ( in_member_variable )
color = cache . member_variable_color ;
else if ( in_function_name )
color = cache . function_color ;
else if ( is_symbol )
color = cache . symbol_color ;
else if ( is_number )
color = cache . number_color ;
prev_is_char = is_char ;
prev_is_number = is_number ;
if ( color ! = prev_color ) {
prev_color = color ;
highlighter_info . color = color ;
color_map [ j ] = highlighter_info ;
}
}
2019-08-11 18:29:09 +00:00
syntax_highlighting_cache [ p_line ] = color_map ;
2018-04-02 11:41:44 +00:00
return color_map ;
}
void SyntaxHighlighter : : set_text_editor ( TextEdit * p_text_editor ) {
text_editor = p_text_editor ;
}
TextEdit * SyntaxHighlighter : : get_text_editor ( ) {
return text_editor ;
}