2014-02-10 02:10:30 +01:00
/*************************************************************************/
/* text_edit.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
2017-08-27 14:16:55 +02:00
/* https://godotengine.org */
2014-02-10 02:10:30 +01:00
/*************************************************************************/
2019-01-01 12:53:14 +01:00
/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
2014-02-10 02:10:30 +01: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-05 00:50:27 +01:00
2014-02-10 02:10:30 +01:00
# include "text_edit.h"
2017-08-27 21:07:15 +02:00
2018-09-11 18:13:45 +02: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"
2016-09-11 16:28:01 +02:00
# include "scene/main/viewport.h"
2014-02-10 02:10:30 +01:00
2017-09-17 06:33:44 +02:00
# ifdef TOOLS_ENABLED
# include "editor/editor_scale.h"
# endif
2014-04-27 12:34:37 +02:00
# define TAB_PIXELS
2014-02-10 02:10:30 +01:00
2017-12-23 09:59:54 +01:00
inline bool _is_symbol ( CharType c ) {
2016-03-09 00:00:52 +01:00
2017-12-23 09:59:54 +01:00
return is_symbol ( c ) ;
2014-02-10 02:10:30 +01:00
}
2017-12-23 09:59:54 +01:00
static bool _is_text_char ( CharType c ) {
2016-03-09 00:00:52 +01:00
2017-12-23 09:59:54 +01:00
return ( c > = ' a ' & & c < = ' z ' ) | | ( c > = ' A ' & & c < = ' Z ' ) | | ( c > = ' 0 ' & & c < = ' 9 ' ) | | c = = ' _ ' ;
2014-02-10 02:10:30 +01:00
}
2017-08-01 19:40:43 +02:00
static bool _is_whitespace ( CharType c ) {
return c = = ' \t ' | | c = = ' ' ;
}
2016-03-21 16:45:38 +01:00
static bool _is_char ( CharType c ) {
2017-03-05 16:44:50 +01:00
return ( c > = ' a ' & & c < = ' z ' ) | | ( c > = ' A ' & & c < = ' Z ' ) | | c = = ' _ ' ;
2016-03-21 16:45:38 +01:00
}
static bool _is_number ( CharType c ) {
return ( c > = ' 0 ' & & c < = ' 9 ' ) ;
}
2016-05-15 02:32:43 +02:00
static bool _is_hex_symbol ( CharType c ) {
return ( ( c > = ' a ' & & c < = ' f ' ) | | ( c > = ' A ' & & c < = ' F ' ) ) ;
}
2014-04-27 12:34:37 +02:00
static bool _is_pair_right_symbol ( CharType c ) {
2017-03-05 16:44:50 +01:00
return c = = ' " ' | |
c = = ' \' ' | |
c = = ' ) ' | |
c = = ' ] ' | |
c = = ' } ' ;
2014-04-27 12:34:37 +02:00
}
static bool _is_pair_left_symbol ( CharType c ) {
2017-03-05 16:44:50 +01:00
return c = = ' " ' | |
c = = ' \' ' | |
c = = ' ( ' | |
c = = ' [ ' | |
c = = ' { ' ;
2014-04-27 12:34:37 +02:00
}
static bool _is_pair_symbol ( CharType c ) {
2015-01-02 19:08:40 +01:00
return _is_pair_left_symbol ( c ) | | _is_pair_right_symbol ( c ) ;
2014-04-27 12:34:37 +02:00
}
static CharType _get_right_pair_symbol ( CharType c ) {
2017-03-05 16:44:50 +01:00
if ( c = = ' " ' )
2015-01-02 19:08:40 +01:00
return ' " ' ;
2017-03-05 16:44:50 +01:00
if ( c = = ' \' ' )
2015-01-02 19:08:40 +01:00
return ' \' ' ;
2017-03-05 16:44:50 +01:00
if ( c = = ' ( ' )
2015-01-02 19:08:40 +01:00
return ' ) ' ;
2017-03-05 16:44:50 +01:00
if ( c = = ' [ ' )
2015-01-02 19:08:40 +01:00
return ' ] ' ;
2017-03-05 16:44:50 +01:00
if ( c = = ' { ' )
2015-01-02 19:08:40 +01:00
return ' } ' ;
return 0 ;
2014-04-27 12:34:37 +02:00
}
2017-03-05 16:44:50 +01:00
void TextEdit : : Text : : set_font ( const Ref < Font > & p_font ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
font = p_font ;
2014-02-10 02:10:30 +01:00
}
2017-04-17 15:24:30 +02:00
void TextEdit : : Text : : set_indent_size ( int p_indent_size ) {
2016-03-09 00:00:52 +01:00
2017-04-17 15:24:30 +02:00
indent_size = p_indent_size ;
2014-02-10 02:10:30 +01:00
}
void TextEdit : : Text : : _update_line_cache ( int p_line ) const {
2016-03-09 00:00:52 +01:00
2015-09-09 18:44:31 +02:00
int w = 0 ;
2015-01-02 19:08:40 +01:00
int len = text [ p_line ] . data . length ( ) ;
const CharType * str = text [ p_line ] . data . c_str ( ) ;
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
//update width
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < len ; i + + ) {
2018-01-26 02:41:17 +01:00
w + = get_char_width ( str [ i ] , str [ i + 1 ] , w ) ;
2015-01-02 19:08:40 +01:00
}
2016-03-09 00:00:52 +01:00
2018-07-25 03:11:03 +02:00
text . write [ p_line ] . width_cache = w ;
2016-03-09 00:00:52 +01:00
2018-07-25 03:11:03 +02:00
text . write [ p_line ] . wrap_amount_cache = - 1 ;
2018-01-26 02:41:17 +01:00
2015-01-02 19:08:40 +01:00
//update regions
2016-03-09 00:00:52 +01:00
2018-07-25 03:11:03 +02:00
text . write [ p_line ] . region_info . clear ( ) ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < len ; i + + ) {
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
if ( ! _is_symbol ( str [ i ] ) )
continue ;
2017-03-05 16:44:50 +01:00
if ( str [ i ] = = ' \\ ' ) {
2015-01-02 19:08:40 +01:00
i + + ; //skip quoted anything
continue ;
}
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
int left = len - i ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
for ( int j = 0 ; j < color_regions - > size ( ) ; j + + ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
const ColorRegion & cr = color_regions - > operator [ ] ( j ) ;
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
/* BEGIN */
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
int lr = cr . begin_key . length ( ) ;
if ( lr = = 0 | | lr > left )
2015-01-02 19:08:40 +01:00
continue ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
const CharType * kc = cr . begin_key . c_str ( ) ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
bool match = true ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
for ( int k = 0 ; k < lr ; k + + ) {
if ( kc [ k ] ! = str [ i + k ] ) {
match = false ;
2015-01-02 19:08:40 +01:00
break ;
}
}
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
if ( match ) {
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
ColorRegionInfo cri ;
2017-03-05 16:44:50 +01:00
cri . end = false ;
cri . region = j ;
2018-07-25 03:11:03 +02:00
text . write [ p_line ] . region_info [ i ] = cri ;
2017-03-05 16:44:50 +01:00
i + = lr - 1 ;
2018-04-02 13:41:44 +02:00
2015-01-02 19:08:40 +01:00
break ;
}
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
/* END */
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
lr = cr . end_key . length ( ) ;
if ( lr = = 0 | | lr > left )
2015-01-02 19:08:40 +01:00
continue ;
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
kc = cr . end_key . c_str ( ) ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
match = true ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
for ( int k = 0 ; k < lr ; k + + ) {
if ( kc [ k ] ! = str [ i + k ] ) {
match = false ;
2015-01-02 19:08:40 +01:00
break ;
}
}
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
if ( match ) {
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
ColorRegionInfo cri ;
2017-03-05 16:44:50 +01:00
cri . end = true ;
cri . region = j ;
2018-07-25 03:11:03 +02:00
text . write [ p_line ] . region_info [ i ] = cri ;
2017-03-05 16:44:50 +01:00
i + = lr - 1 ;
2018-04-02 13:41:44 +02:00
2015-01-02 19:08:40 +01:00
break ;
}
}
}
2014-02-10 02:10:30 +01:00
}
2018-01-12 06:25:04 +01:00
const Map < int , TextEdit : : Text : : ColorRegionInfo > & TextEdit : : Text : : get_color_region_info ( int p_line ) const {
2016-03-09 00:00:52 +01:00
2017-08-25 17:14:33 +02:00
static Map < int , ColorRegionInfo > cri ;
ERR_FAIL_INDEX_V ( p_line , text . size ( ) , cri ) ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( text [ p_line ] . width_cache = = - 1 ) {
2015-01-02 19:08:40 +01:00
_update_line_cache ( p_line ) ;
}
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
return text [ p_line ] . region_info ;
2014-02-10 02:10:30 +01:00
}
int TextEdit : : Text : : get_line_width ( int p_line ) const {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
ERR_FAIL_INDEX_V ( p_line , text . size ( ) , - 1 ) ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( text [ p_line ] . width_cache = = - 1 ) {
2015-01-02 19:08:40 +01:00
_update_line_cache ( p_line ) ;
}
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
return text [ p_line ] . width_cache ;
2014-02-10 02:10:30 +01:00
}
2018-01-26 02:41:17 +01: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 03:11:03 +02:00
text . write [ p_line ] . wrap_amount_cache = p_wrap_amount ;
2018-01-26 02:41:17 +01: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-09 00:00:52 +01:00
2018-01-26 02:41:17 +01:00
for ( int i = 0 ; i < text . size ( ) ; i + + ) {
2018-07-25 03:11:03 +02:00
text . write [ i ] . width_cache = - 1 ;
2018-01-26 02:41:17 +01:00
}
}
void TextEdit : : Text : : clear_wrap_cache ( ) {
for ( int i = 0 ; i < text . size ( ) ; i + + ) {
2018-07-25 03:11:03 +02:00
text . write [ i ] . wrap_amount_cache = - 1 ;
2018-01-26 02:41:17 +01:00
}
2014-02-10 02:10:30 +01:00
}
void TextEdit : : Text : : clear ( ) {
2016-03-09 00:00:52 +01:00
2017-01-14 18:03:38 +01:00
text . clear ( ) ;
2017-03-05 16:44:50 +01:00
insert ( 0 , " " ) ;
2014-02-10 02:10:30 +01:00
}
2017-12-07 00:31:09 +01:00
int TextEdit : : Text : : get_max_width ( bool p_exclude_hidden ) const {
2015-01-02 19:08:40 +01:00
//quite some work.. but should be fast enough.
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
int max = 0 ;
2017-12-07 00:31:09 +01: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 19:08:40 +01:00
return max ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
void TextEdit : : Text : : set ( int p_line , const String & p_text ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
ERR_FAIL_INDEX ( p_line , text . size ( ) ) ;
2016-03-09 00:00:52 +01:00
2018-07-25 03:11:03 +02: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 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
void TextEdit : : Text : : insert ( int p_at , const String & p_text ) {
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
Line line ;
2017-03-05 16:44:50 +01:00
line . marked = false ;
2018-06-05 18:50:21 +02:00
line . safe = false ;
2017-03-05 16:44:50 +01:00
line . breakpoint = false ;
2017-11-13 00:12:17 +01:00
line . hidden = false ;
2017-03-05 16:44:50 +01:00
line . width_cache = - 1 ;
2018-01-26 02:41:17 +01:00
line . wrap_amount_cache = - 1 ;
2017-03-05 16:44:50 +01:00
line . data = p_text ;
text . insert ( p_at , line ) ;
2014-02-10 02:10:30 +01:00
}
void TextEdit : : Text : : remove ( int p_at ) {
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
text . remove ( p_at ) ;
2014-02-10 02:10:30 +01:00
}
2018-05-15 21:32:09 +02:00
int TextEdit : : Text : : get_char_width ( CharType c , CharType next_c , int px ) const {
2018-01-26 02:41:17 +01: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
w = tab_w - px % tab_w ; // is right...
} else {
w = font - > get_char_size ( c , next_c ) . width ;
}
return w ;
}
2014-02-10 02:10:30 +01:00
void TextEdit : : _update_scrollbars ( ) {
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
Size2 size = get_size ( ) ;
Size2 hmin = h_scroll - > get_combined_minimum_size ( ) ;
Size2 vmin = v_scroll - > get_combined_minimum_size ( ) ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01: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-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
h_scroll - > set_begin ( Point2 ( 0 , size . height - hmin . height ) ) ;
h_scroll - > set_end ( Point2 ( size . width - vmin . width , size . height ) ) ;
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
int visible_rows = get_visible_rows ( ) ;
2018-01-26 02:41:17 +01:00
int total_rows = get_total_visible_rows ( ) ;
2016-03-07 03:32:51 +01:00
if ( scroll_past_end_of_file_enabled ) {
2017-11-16 05:00:27 +01:00
total_rows + = visible_rows - 1 ;
2016-03-07 03:32:51 +01:00
}
2015-01-02 19:08:40 +01:00
int visible_width = size . width - cache . style_normal - > get_minimum_size ( ) . width ;
2017-12-07 00:31:09 +01:00
int total_width = text . get_max_width ( true ) + vmin . x ;
2016-03-09 00:00:52 +01:00
2015-09-09 18:44:31 +02:00
if ( line_numbers )
total_width + = cache . line_number_w ;
2016-03-09 00:00:52 +01:00
2016-05-26 15:17:14 +02:00
if ( draw_breakpoint_gutter ) {
total_width + = cache . breakpoint_gutter_width ;
}
2019-04-20 13:51:25 +02:00
if ( draw_info_gutter ) {
total_width + = cache . info_gutter_width ;
}
2017-11-13 00:12:17 +01:00
if ( draw_fold_gutter ) {
total_width + = cache . fold_gutter_width ;
}
2017-03-05 16:44:50 +01:00
bool use_hscroll = true ;
bool use_vscroll = true ;
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
if ( total_rows < = visible_rows & & total_width < = visible_width ) {
//thanks yessopie for this clever bit of logic
2017-03-05 16:44:50 +01:00
use_hscroll = false ;
use_vscroll = false ;
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
} else {
2016-03-09 00:00:52 +01:00
2018-10-05 00:38:08 +02:00
if ( total_rows > visible_rows & & total_width < = visible_width ) {
2015-01-02 19:08:40 +01:00
//thanks yessopie for this clever bit of logic
2017-03-05 16:44:50 +01:00
use_hscroll = false ;
2015-01-02 19:08:40 +01:00
}
2016-03-09 00:00:52 +01:00
2018-10-05 00:38:08 +02:00
if ( total_rows < = visible_rows & & total_width > visible_width ) {
2015-01-02 19:08:40 +01:00
//thanks yessopie for this clever bit of logic
2017-03-05 16:44:50 +01:00
use_vscroll = false ;
2015-01-02 19:08:40 +01:00
}
}
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
updating_scrolls = true ;
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
if ( use_vscroll ) {
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
v_scroll - > show ( ) ;
2018-01-26 02:41:17 +01:00
v_scroll - > set_max ( total_rows + get_visible_rows_offset ( ) ) ;
v_scroll - > set_page ( visible_rows + get_visible_rows_offset ( ) ) ;
2017-08-19 16:23:45 +02:00
if ( smooth_scroll_enabled ) {
v_scroll - > set_step ( 0.25 ) ;
} else {
v_scroll - > set_step ( 1 ) ;
}
2018-01-26 02:41:17 +01:00
set_v_scroll ( get_v_scroll ( ) ) ;
2017-11-21 15:34:04 +01:00
2017-03-05 16:44:50 +01:00
} else {
2017-12-07 00:31:09 +01:00
2015-01-02 19:08:40 +01:00
cursor . line_ofs = 0 ;
2018-01-26 02:41:17 +01:00
cursor . wrap_ofs = 0 ;
2017-11-16 05:00:27 +01:00
v_scroll - > set_value ( 0 ) ;
2015-01-02 19:08:40 +01:00
v_scroll - > hide ( ) ;
}
2016-03-09 00:00:52 +01:00
2018-01-26 02:41:17 +01:00
if ( use_hscroll & & ! is_wrap_enabled ( ) ) {
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
h_scroll - > show ( ) ;
h_scroll - > set_max ( total_width ) ;
h_scroll - > set_page ( visible_width ) ;
2017-12-07 00:31:09 +01:00
if ( cursor . x_ofs > ( total_width - visible_width ) )
cursor . x_ofs = ( total_width - visible_width ) ;
2017-02-21 23:45:31 +01:00
if ( fabs ( h_scroll - > get_value ( ) - ( double ) cursor . x_ofs ) > = 1 ) {
h_scroll - > set_value ( cursor . x_ofs ) ;
}
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
} else {
2016-03-09 00:00:52 +01:00
2017-12-07 00:31:09 +01:00
cursor . x_ofs = 0 ;
h_scroll - > set_value ( 0 ) ;
2015-01-02 19:08:40 +01:00
h_scroll - > hide ( ) ;
}
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
updating_scrolls = false ;
2014-02-10 02:10:30 +01:00
}
2015-12-09 19:56:41 +01:00
void TextEdit : : _click_selection_held ( ) {
2018-06-14 09:10:30 +02: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 16:44:50 +01:00
if ( Input : : get_singleton ( ) - > is_mouse_button_pressed ( BUTTON_LEFT ) & & selection . selecting_mode ! = Selection : : MODE_NONE ) {
2017-11-05 16:54:00 +01: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 19:56:41 +01:00
2017-11-05 16:54:00 +01:00
void TextEdit : : _update_selection_mode_pointer ( ) {
2018-04-19 20:05:18 +02:00
Point2 mp = get_local_mouse_position ( ) ;
2015-12-09 19:56:41 +01:00
2017-11-05 16:54:00 +01:00
int row , col ;
_get_mouse_pos ( Point2i ( mp . x , mp . y ) , row , col ) ;
2015-12-09 19:56:41 +01:00
2017-11-05 16:54:00 +01:00
select ( selection . selecting_line , selection . selecting_column , row , col ) ;
2015-12-09 19:56:41 +01:00
2018-01-26 02:41:17 +01:00
cursor_set_line ( row , false ) ;
2018-06-14 09:10:30 +02:00
cursor_set_column ( col ) ;
2017-11-05 16:54:00 +01:00
update ( ) ;
click_select_held - > start ( ) ;
}
void TextEdit : : _update_selection_mode_word ( ) {
2018-04-19 20:05:18 +02:00
Point2 mp = get_local_mouse_position ( ) ;
2017-11-05 16:54:00 +01:00
int row , col ;
_get_mouse_pos ( Point2i ( mp . x , mp . y ) , row , col ) ;
2015-12-09 19:56:41 +01:00
2017-11-05 16:54:00 +01:00
String line = text [ row ] ;
int beg = CLAMP ( col , 0 , line . length ( ) ) ;
// if its the first selection and on whitespace make sure we grab the word instead..
if ( ! selection . active ) {
while ( beg > 0 & & line [ beg ] < = 32 ) {
beg - - ;
}
}
int end = beg ;
bool symbol = beg < line . length ( ) & & _is_symbol ( line [ beg ] ) ;
// get the word end and begin points
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 19:56:41 +01:00
2018-01-18 21:37:17 +01:00
// initial selection
2017-11-05 16:54:00 +01: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 09:10:30 +02:00
cursor_set_line ( selection . to_line , false ) ;
2017-11-05 16:54:00 +01:00
cursor_set_column ( selection . to_column ) ;
2015-12-09 19:56:41 +01:00
} else {
2017-11-05 16:54:00 +01: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 09:10:30 +02:00
cursor_set_line ( selection . from_line , false ) ;
2017-11-05 16:54:00 +01: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 09:10:30 +02:00
cursor_set_line ( selection . to_line , false ) ;
2017-11-05 16:54:00 +01:00
cursor_set_column ( selection . to_column ) ;
}
}
2015-12-09 19:56:41 +01:00
2017-11-05 16:54:00 +01:00
update ( ) ;
2018-06-14 09:10:30 +02:00
2017-11-05 16:54:00 +01:00
click_select_held - > start ( ) ;
}
void TextEdit : : _update_selection_mode_line ( ) {
2018-04-19 20:05:18 +02:00
Point2 mp = get_local_mouse_position ( ) ;
2017-11-05 16:54:00 +01:00
int row , col ;
_get_mouse_pos ( Point2i ( mp . x , mp . y ) , row , col ) ;
col = 0 ;
if ( row < selection . selecting_line ) {
// cursor is above us
2018-01-26 02:41:17 +01:00
cursor_set_line ( row - 1 , false ) ;
2017-11-05 16:54:00 +01:00
selection . selecting_column = text [ selection . selecting_line ] . length ( ) ;
} else {
// cursor is below us
2018-01-26 02:41:17 +01:00
cursor_set_line ( row + 1 , false ) ;
2017-11-05 16:54:00 +01:00
selection . selecting_column = 0 ;
col = text [ row ] . length ( ) ;
2015-12-09 19:56:41 +01:00
}
2018-06-14 09:10:30 +02:00
cursor_set_column ( 0 ) ;
2017-11-05 16:54:00 +01:00
select ( selection . selecting_line , selection . selecting_column , row , col ) ;
update ( ) ;
click_select_held - > start ( ) ;
2015-12-09 19:56:41 +01:00
}
2014-02-10 02:10:30 +01:00
void TextEdit : : _notification ( int p_what ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
switch ( p_what ) {
2014-12-17 02:31:57 +01:00
case NOTIFICATION_ENTER_TREE : {
2016-03-09 00:00:52 +01:00
2014-12-17 02:31:57 +01:00
_update_caches ( ) ;
if ( cursor_changed_dirty )
2017-03-05 16:44:50 +01:00
MessageQueue : : get_singleton ( ) - > push_call ( this , " _cursor_changed_emit " ) ;
2014-12-17 02:31:57 +01:00
if ( text_changed_dirty )
2017-03-05 16:44:50 +01:00
MessageQueue : : get_singleton ( ) - > push_call ( this , " _text_changed_emit " ) ;
2019-01-05 17:58:54 +01:00
_update_wrap_at ( ) ;
2014-12-17 02:31:57 +01:00
} break ;
case NOTIFICATION_RESIZED : {
2016-03-09 00:00:52 +01:00
2018-01-26 02:41:17 +01:00
_update_scrollbars ( ) ;
2019-01-05 17:58:54 +01:00
call_deferred ( " _update_wrap_at " ) ;
2014-12-17 02:31:57 +01:00
} break ;
case NOTIFICATION_THEME_CHANGED : {
2016-03-09 00:00:52 +01:00
2014-12-17 02:31:57 +01:00
_update_caches ( ) ;
2019-01-05 17:58:54 +01:00
_update_wrap_at ( ) ;
2016-05-29 16:37:26 +02:00
} break ;
2016-06-19 17:11:16 +02: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 09:28:14 +02:00
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS : {
2018-01-26 02:41:17 +01:00
if ( scrolling & & get_v_scroll ( ) ! = target_v_scroll ) {
double target_y = target_v_scroll - get_v_scroll ( ) ;
2017-08-19 16:23:45 +02:00
double dist = sqrt ( target_y * target_y ) ;
2017-09-30 16:19:07 +02:00
double vel = ( ( target_y / dist ) * v_scroll_speed ) * get_physics_process_delta_time ( ) ;
2017-08-19 16:23:45 +02:00
2017-09-02 14:17:13 +02:00
if ( Math : : abs ( vel ) > = dist ) {
2018-01-26 02:41:17 +01:00
set_v_scroll ( target_v_scroll ) ;
2017-08-19 16:23:45 +02:00
scrolling = false ;
2018-04-11 09:28:14 +02:00
set_physics_process_internal ( false ) ;
2017-08-19 16:23:45 +02:00
} else {
2018-01-26 02:41:17 +01:00
set_v_scroll ( get_v_scroll ( ) + vel ) ;
2017-08-19 16:23:45 +02:00
}
} else {
scrolling = false ;
2018-04-11 09:28:14 +02:00
set_physics_process_internal ( false ) ;
2017-08-19 16:23:45 +02:00
}
} break ;
2014-12-17 02:31:57 +01:00
case NOTIFICATION_DRAW : {
2019-04-12 04:21:48 +02:00
if ( first_draw ) {
//size may not be the final one, so attempts to ensure cursor was visible may have failed
adjust_viewport_to_cursor ( ) ;
first_draw = false ;
}
2018-08-20 13:36:34 +02:00
Size2 size = get_size ( ) ;
2016-06-19 17:11:16 +02:00
if ( ( ! has_focus ( ) & & ! menu - > has_focus ( ) ) | | ! window_has_focus ) {
draw_caret = false ;
}
2016-05-26 15:17:14 +02:00
if ( draw_breakpoint_gutter ) {
2016-06-07 17:59:16 +02:00
breakpoint_gutter_width = ( get_row_height ( ) * 55 ) / 100 ;
2016-05-26 15:17:14 +02:00
cache . breakpoint_gutter_width = breakpoint_gutter_width ;
} else {
cache . breakpoint_gutter_width = 0 ;
}
2019-04-20 13:51:25 +02: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-13 00:12:17 +01: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 ;
}
2017-03-05 16:44:50 +01:00
int line_number_char_count = 0 ;
2016-03-09 00:00:52 +01:00
2014-12-17 02:31:57 +01:00
{
2017-08-18 22:11:45 +02:00
int lc = text . size ( ) ;
2017-03-05 16:44:50 +01:00
cache . line_number_w = 0 ;
while ( lc ) {
cache . line_number_w + = 1 ;
lc / = 10 ;
2014-12-17 02:31:57 +01:00
} ;
2016-03-09 00:00:52 +01:00
2014-12-17 02:31:57 +01:00
if ( line_numbers ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01: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 02:31:57 +01:00
} else {
2017-03-05 16:44:50 +01:00
cache . line_number_w = 0 ;
2014-12-17 02:31:57 +01:00
}
}
_update_scrollbars ( ) ;
2016-03-09 00:00:52 +01:00
2014-12-17 02:31:57 +01:00
RID ci = get_canvas_item ( ) ;
2017-08-19 16:23:45 +02:00
VisualServer : : get_singleton ( ) - > canvas_item_set_clip ( get_canvas_item ( ) , true ) ;
2019-04-20 13:51:25 +02: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 ;
2018-08-20 13:36:34 +02:00
int xmargin_end = size . width - cache . style_normal - > get_margin ( MARGIN_RIGHT ) ;
2014-12-17 02:31:57 +01:00
//let's do it easy for now:
2018-08-20 13:36:34 +02:00
cache . style_normal - > draw ( ci , Rect2 ( Point2 ( ) , size ) ) ;
2017-11-30 04:53:15 +01:00
float readonly_alpha = 1.0 ; // used to set the input text color when in read-only mode
if ( readonly ) {
2018-08-20 13:36:34 +02:00
cache . style_readonly - > draw ( ci , Rect2 ( Point2 ( ) , size ) ) ;
2017-11-30 04:53:15 +01:00
readonly_alpha = .5 ;
draw_caret = false ;
}
2014-12-17 02:31:57 +01:00
if ( has_focus ( ) )
2018-08-20 13:36:34 +02:00
cache . style_focus - > draw ( ci , Rect2 ( Point2 ( ) , size ) ) ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
int ascent = cache . font - > get_ascent ( ) ;
2016-03-09 00:00:52 +01:00
2017-08-20 15:07:54 +02:00
int visible_rows = get_visible_rows ( ) + 1 ;
2016-03-09 00:00:52 +01:00
2014-12-17 02:31:57 +01:00
Color color = cache . font_color ;
2017-11-30 04:53:15 +01:00
color . a * = readonly_alpha ;
2014-12-17 02:31:57 +01:00
if ( syntax_coloring ) {
2017-03-05 16:44:50 +01:00
if ( cache . background_color . a > 0.01 ) {
2017-09-29 16:59:38 +02:00
VisualServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( Point2i ( ) , get_size ( ) ) , cache . background_color ) ;
2014-12-17 02:31:57 +01:00
}
}
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01: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-09 00:00:52 +01:00
2019-03-05 16:23:25 +01:00
if ( brace_matching_enabled & & cursor . line > = 0 & & cursor . line < text . size ( ) & & cursor . column > = 0 ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( cursor . column < text [ cursor . line ] . length ( ) ) {
2014-12-17 05:53:34 +01:00
//check for open
CharType c = text [ cursor . line ] [ cursor . column ] ;
2017-03-05 16:44:50 +01:00
CharType closec = 0 ;
if ( c = = ' [ ' ) {
closec = ' ] ' ;
} else if ( c = = ' { ' ) {
closec = ' } ' ;
} else if ( c = = ' ( ' ) {
closec = ' ) ' ;
2014-12-17 05:53:34 +01:00
}
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( closec ! = 0 ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
int stack = 1 ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
for ( int i = cursor . line ; i < text . size ( ) ; i + + ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
int from = i = = cursor . line ? cursor . column + 1 : 0 ;
for ( int j = from ; j < text [ i ] . length ( ) ; j + + ) {
2016-03-09 00:00:52 +01:00
2014-12-17 05:53:34 +01:00
CharType cc = text [ i ] [ j ] ;
2015-02-20 01:34:04 +01:00
//ignore any brackets inside a string
2017-03-05 16:44:50 +01:00
if ( cc = = ' " ' | | cc = = ' \' ' ) {
2015-02-20 01:34:04 +01:00
CharType quotation = cc ;
do {
j + + ;
2017-03-05 16:44:50 +01:00
if ( ! ( j < text [ i ] . length ( ) ) ) {
2015-02-20 01:34:04 +01:00
break ;
}
2017-03-05 16:44:50 +01:00
cc = text [ i ] [ j ] ;
2015-02-20 01:34:04 +01:00
//skip over escaped quotation marks inside strings
2017-03-05 16:44:50 +01:00
if ( cc = = ' \\ ' ) {
2015-02-20 01:34:04 +01:00
bool escaped = true ;
2017-03-05 16:44:50 +01:00
while ( j + 1 < text [ i ] . length ( ) & & text [ i ] [ j + 1 ] = = ' \\ ' ) {
escaped = ! escaped ;
2015-02-20 01:34:04 +01:00
j + + ;
}
if ( escaped ) {
j + + ;
continue ;
}
}
2017-03-05 16:44:50 +01:00
} while ( cc ! = quotation ) ;
} else if ( cc = = c )
2014-12-17 05:53:34 +01:00
stack + + ;
2017-03-05 16:44:50 +01:00
else if ( cc = = closec )
2014-12-17 05:53:34 +01:00
stack - - ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( stack = = 0 ) {
brace_open_match_line = i ;
brace_open_match_column = j ;
brace_open_matching = true ;
2016-03-09 00:00:52 +01:00
2014-12-17 05:53:34 +01:00
break ;
}
}
2017-03-05 16:44:50 +01:00
if ( brace_open_match_line ! = - 1 )
2014-12-17 05:53:34 +01:00
break ;
}
2016-03-09 00:00:52 +01:00
2014-12-17 05:53:34 +01:00
if ( ! brace_open_matching )
2017-03-05 16:44:50 +01:00
brace_open_mismatch = true ;
2014-12-17 05:53:34 +01:00
}
}
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( cursor . column > 0 ) {
CharType c = text [ cursor . line ] [ cursor . column - 1 ] ;
CharType closec = 0 ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( c = = ' ] ' ) {
closec = ' [ ' ;
} else if ( c = = ' } ' ) {
closec = ' { ' ;
} else if ( c = = ' ) ' ) {
closec = ' ( ' ;
2014-12-17 05:53:34 +01:00
}
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( closec ! = 0 ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
int stack = 1 ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
for ( int i = cursor . line ; i > = 0 ; i - - ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
int from = i = = cursor . line ? cursor . column - 2 : text [ i ] . length ( ) - 1 ;
for ( int j = from ; j > = 0 ; j - - ) {
2016-03-09 00:00:52 +01:00
2014-12-17 05:53:34 +01:00
CharType cc = text [ i ] [ j ] ;
2015-02-20 01:34:04 +01:00
//ignore any brackets inside a string
2017-03-05 16:44:50 +01:00
if ( cc = = ' " ' | | cc = = ' \' ' ) {
2015-02-20 01:34:04 +01:00
CharType quotation = cc ;
do {
j - - ;
2017-03-05 16:44:50 +01:00
if ( ! ( j > = 0 ) ) {
2015-02-20 01:34:04 +01:00
break ;
}
2017-03-05 16:44:50 +01:00
cc = text [ i ] [ j ] ;
2015-02-20 01:34:04 +01:00
//skip over escaped quotation marks inside strings
2017-03-05 16:44:50 +01:00
if ( cc = = quotation ) {
2015-02-20 01:34:04 +01:00
bool escaped = false ;
2017-03-05 16:44:50 +01:00
while ( j - 1 > = 0 & & text [ i ] [ j - 1 ] = = ' \\ ' ) {
escaped = ! escaped ;
2015-02-20 01:34:04 +01:00
j - - ;
}
if ( escaped ) {
2017-03-05 16:44:50 +01:00
cc = ' \\ ' ;
2015-02-20 01:34:04 +01:00
continue ;
}
}
2017-03-05 16:44:50 +01:00
} while ( cc ! = quotation ) ;
} else if ( cc = = c )
2014-12-17 05:53:34 +01:00
stack + + ;
2017-03-05 16:44:50 +01:00
else if ( cc = = closec )
2014-12-17 05:53:34 +01:00
stack - - ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( stack = = 0 ) {
brace_close_match_line = i ;
brace_close_match_column = j ;
brace_close_matching = true ;
2016-03-09 00:00:52 +01:00
2014-12-17 05:53:34 +01:00
break ;
}
}
2017-03-05 16:44:50 +01:00
if ( brace_close_match_line ! = - 1 )
2014-12-17 05:53:34 +01:00
break ;
}
2016-03-09 00:00:52 +01:00
2014-12-17 05:53:34 +01:00
if ( ! brace_close_matching )
2017-03-05 16:44:50 +01:00
brace_close_mismatch = true ;
2014-12-17 05:53:34 +01:00
}
}
}
2016-03-09 00:00:52 +01:00
2014-12-17 02:31:57 +01:00
Point2 cursor_pos ;
2018-12-31 12:38:42 +01:00
int cursor_insert_offset_y = 0 ;
2016-03-09 00:00:52 +01:00
2016-03-16 22:20:42 +01:00
// get the highlighted words
2016-03-17 20:37:19 +01:00
String highlighted_text = get_selection_text ( ) ;
2016-03-16 22:20:42 +01:00
2019-04-27 18:02:09 +02:00
// check if highlighted words contains only whitespaces (tabs or spaces)
bool only_whitespaces_highlighted = highlighted_text . strip_edges ( ) = = String ( ) ;
2016-10-10 13:43:09 +02:00
String line_num_padding = line_numbers_zero_padded ? " 0 " : " " ;
2018-01-26 02:41:17 +01:00
int cursor_wrap_index = get_cursor_wrap_index ( ) ;
2018-03-17 09:44:34 +01:00
FontDrawer drawer ( cache . font , Color ( 1 , 1 , 1 ) ) ;
2018-01-26 02:41:17 +01:00
int line = get_first_visible_line ( ) - 1 ;
int draw_amount = visible_rows + ( smooth_scroll_enabled ? 1 : 0 ) ;
draw_amount + = times_line_wraps ( line + 1 ) ;
2017-11-21 15:34:04 +01:00
for ( int i = 0 ; i < draw_amount ; i + + ) {
2016-03-09 00:00:52 +01:00
2017-11-13 00:12:17 +01: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-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( line < 0 | | line > = ( int ) text . size ( ) )
2014-12-17 02:31:57 +01:00
continue ;
2016-03-09 00:00:52 +01:00
2018-01-26 02:41:17 +01:00
const String & fullstr = text [ line ] ;
2016-03-09 00:00:52 +01:00
2018-01-26 02:41:17 +01:00
Map < int , HighlighterInfo > color_map ;
if ( syntax_coloring ) {
color_map = _get_line_syntax_highlighting ( line ) ;
}
// ensure we at least use the font color
Color current_color = cache . font_color ;
2017-11-30 04:53:15 +01:00
if ( readonly ) {
2018-01-26 02:41:17 +01:00
current_color . a * = readonly_alpha ;
2017-08-19 16:23:45 +02:00
}
2017-03-05 16:44:50 +01:00
bool underlined = false ;
2016-03-09 00:00:52 +01:00
2018-12-18 14:01:15 +01:00
Vector < String > wrap_rows = get_wrap_rows_text ( line ) ;
2018-01-26 02:41:17 +01:00
int line_wrap_amount = times_line_wraps ( line ) ;
int last_wrap_column = 0 ;
2016-05-28 18:25:45 +02:00
2018-01-26 02:41:17 +01: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 18:25:45 +02:00
2018-01-26 02:41:17 +01: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 14:52:33 +01:00
if ( indent_px > = wrap_at ) {
indent_px = 0 ;
}
2016-03-09 00:00:52 +01:00
2018-01-26 02:41:17 +01: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 13:41:44 +02:00
}
2016-03-09 00:00:52 +01:00
2018-01-26 02:41:17 +01: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-09 00:00:52 +01:00
2018-01-26 02:41:17 +01:00
// check if line contains highlighted word
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-09 00:00:52 +01:00
2018-01-26 02:41:17 +01: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 22:35:50 +02:00
}
2018-01-26 02:41:17 +01: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 22:35:50 +02:00
}
2018-01-26 02:41:17 +01:00
if ( str . length ( ) = = 0 ) {
// draw line background if empty as we won't loop at at all
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 ) ;
}
// give visual indication of empty selected line
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 {
// 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.
if ( line = = cursor . line & & cursor_wrap_index = = line_wrap_index & & highlight_current_line ) {
VisualServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( 0 , ofs_y , xmargin_beg , get_row_height ( ) ) , cache . current_line_color ) ;
}
2017-12-04 22:48:20 +01:00
}
2017-10-21 22:35:50 +02:00
2018-01-26 02:41:17 +01:00
if ( line_wrap_index = = 0 ) {
// only do these if we are on the first wrapped part of a line
if ( text . is_breakpoint ( line ) & & ! draw_breakpoint_gutter ) {
2017-09-17 06:33:44 +02:00
# ifdef TOOLS_ENABLED
2018-01-26 02:41:17 +01: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 06:33:44 +02:00
# else
2018-01-26 02:41:17 +01: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 06:33:44 +02:00
# endif
2018-01-26 02:41:17 +01:00
}
2017-09-15 03:02:05 +02:00
2018-01-26 02:41:17 +01:00
// draw breakpoint marker
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 ) ;
// no transparency on marker
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-09 00:00:52 +01:00
2019-04-20 13:51:25 +02:00
// draw info icons
if ( draw_info_gutter & & text . has_info_icon ( line ) ) {
2019-04-24 08:13:25 +02:00
int vertical_gap = ( get_row_height ( ) * 40 ) / 100 ;
2019-04-20 13:51:25 +02: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 ) ;
// ensure the icon fits the gutter size
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 08:13:25 +02:00
int xofs = horizontal_gap - ( info_icon - > get_width ( ) / 4 ) ;
int yofs = vertical_gap - ( info_icon - > get_height ( ) / 4 ) ;
2019-04-20 13:51:25 +02: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-04-22 18:20:27 +02:00
// draw execution marker
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
}
}
2018-01-26 02:41:17 +01:00
// draw fold markers
if ( draw_fold_gutter ) {
int horizontal_gap = ( cache . fold_gutter_width * 30 ) / 100 ;
2019-04-20 13:51:25 +02: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 02:41:17 +01: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 ) ;
}
}
// draw line numbers
if ( cache . line_number_w ) {
2018-06-23 17:19:27 +02:00
int yofs = ofs_y + ( get_row_height ( ) - cache . font - > get_height ( ) ) / 2 ;
2018-01-26 02:41:17 +01:00
String fc = String : : num ( line + 1 ) ;
while ( fc . length ( ) < line_number_char_count ) {
fc = line_num_padding + fc ;
}
2017-11-13 00:12:17 +01:00
2019-04-20 13:51:25 +02: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 02:41:17 +01:00
}
2016-05-30 17:28:31 +02:00
}
2016-03-09 00:00:52 +01:00
2018-01-26 02:41:17 +01:00
//loop through characters in one line
for ( int j = 0 ; j < str . length ( ) ; j + + ) {
2016-03-09 00:00:52 +01:00
2018-01-26 02:41:17 +01:00
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 * = readonly_alpha ;
}
}
color = current_color ;
}
2016-04-02 21:46:42 +02:00
2018-01-26 02:41:17 +01:00
int char_w ;
//handle tabulator
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 16:50:54 +02:00
2018-01-26 02:41:17 +01:00
// line highlighting handle horizontal clipping
if ( line = = cursor . line & & cursor_wrap_index = = line_wrap_index & & highlight_current_line ) {
if ( j = = str . length ( ) - 1 ) {
// end of line when last char is skipped
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 ) {
// char next to margin is skipped
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 16:50:54 +02:00
}
2018-01-26 02:41:17 +01:00
continue ;
2016-04-05 16:50:54 +02:00
}
2016-03-09 00:00:52 +01:00
2018-01-26 02:41:17 +01:00
if ( ( char_ofs + char_margin + char_w ) > = xmargin_end ) {
if ( syntax_coloring )
continue ;
else
break ;
}
2016-03-09 00:00:52 +01:00
2018-01-26 02:41:17 +01:00
bool in_search_result = false ;
2016-03-09 00:00:52 +01:00
2018-01-26 02:41:17 +01:00
if ( search_text_col ! = - 1 ) {
// if we are at the end check for new search result on same line
if ( j > = search_text_col + search_text . length ( ) )
search_text_col = _get_column_pos_of_word ( search_text , str , search_flags , j ) ;
2016-03-09 00:00:52 +01:00
2018-01-26 02:41:17 +01:00
in_search_result = j > = search_text_col & & j < search_text_col + search_text . length ( ) ;
2018-01-14 17:06:27 +01:00
2018-01-26 02:41:17 +01: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 17:06:27 +01:00
2018-01-26 02:41:17 +01:00
//current line highlighting
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 18:59:19 +01:00
2018-01-26 02:41:17 +01:00
if ( line = = cursor . line & & cursor_wrap_index = = line_wrap_index & & highlight_current_line ) {
// draw the wrap indent offset highlight
if ( line_wrap_index ! = 0 & & j = = 0 ) {
VisualServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( char_ofs + char_margin - indent_px , ofs_y , ( char_ofs + char_margin ) , get_row_height ( ) ) , cache . current_line_color ) ;
}
// if its the last char draw to end of the line
if ( j = = str . length ( ) - 1 ) {
VisualServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( char_ofs + char_margin + char_w , ofs_y , xmargin_end - ( char_ofs + char_margin + char_w ) , get_row_height ( ) ) , cache . current_line_color ) ;
}
// actual text
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 17:06:27 +01:00
}
}
2016-03-09 00:00:52 +01:00
2018-01-26 02:41:17 +01: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 18:25:45 +02:00
2018-01-26 02:41:17 +01: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-09 00:00:52 +01:00
2018-01-26 02:41:17 +01: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 18:25:45 +02:00
2018-01-26 02:41:17 +01: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 18:25:45 +02:00
}
2019-04-27 18:02:09 +02:00
if ( highlight_all_occurrences & & ! only_whitespaces_highlighted ) {
2018-01-26 02:41:17 +01:00
if ( highlighted_text_col ! = - 1 ) {
// if we are at the end check for new word on same line
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-09 00:00:52 +01:00
2018-01-26 02:41:17 +01:00
// if this is the original highlighted text we don't want to highlight it again
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 22:35:50 +02:00
}
2018-01-26 02:41:17 +01: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 22:35:50 +02:00
}
2017-09-28 16:10:30 +02:00
2018-01-26 02:41:17 +01:00
if ( brace_matching_enabled ) {
2018-06-23 17:19:27 +02:00
int yofs = ofs_y + ( get_row_height ( ) - cache . font - > get_height ( ) ) / 2 ;
2018-01-26 02:41:17 +01: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-09 00:00:52 +01:00
2018-01-26 02:41:17 +01:00
if ( brace_open_mismatch )
color = cache . brace_mismatch_color ;
2018-06-23 17:19:27 +02: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_selected_color : color ) ;
2018-01-26 02:41:17 +01:00
}
2016-05-28 18:25:45 +02:00
2018-01-26 02:41:17 +01: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 18:25:45 +02:00
2018-01-26 02:41:17 +01:00
if ( brace_close_mismatch )
color = cache . brace_mismatch_color ;
2018-06-23 17:19:27 +02: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_selected_color : color ) ;
2018-01-26 02:41:17 +01:00
}
}
if ( cursor . column = = last_wrap_column + j & & cursor . line = = line & & cursor_wrap_index = = line_wrap_index ) {
2016-05-28 18:25:45 +02:00
2018-01-26 02:41:17 +01:00
cursor_pos = Point2i ( char_ofs + char_margin + ofs_x , ofs_y ) ;
2018-12-22 20:35:58 +01:00
cursor_pos . y + = ( get_row_height ( ) - cache . font - > get_height ( ) ) / 2 ;
2016-03-16 22:20:42 +01:00
2018-01-26 02:41:17 +01:00
if ( insert_mode ) {
2018-12-31 12:38:42 +01:00
cursor_insert_offset_y = ( cache . font - > get_height ( ) - 3 ) ;
cursor_pos . y + = cursor_insert_offset_y ;
2016-03-16 22:20:42 +01:00
}
2018-01-26 02:41:17 +01: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 17:10:54 +02:00
2018-01-26 02:41:17 +01: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 17:10:54 +02:00
2018-01-26 02:41:17 +01: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 23:41:47 +02:00
# ifdef TOOLS_ENABLED
int caret_h = ( block_caret ) ? 4 : 2 * EDSCALE ;
# else
int caret_h = ( block_caret ) ? 4 : 2 ;
# endif
2018-01-26 02:41:17 +01:00
VisualServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( cursor_pos , Size2i ( caret_w , caret_h ) ) , cache . caret_color ) ;
} else {
2018-07-26 23:41:47 +02:00
# ifdef TOOLS_ENABLED
caret_w = ( block_caret ) ? caret_w : 2 * EDSCALE ;
# else
caret_w = ( block_caret ) ? caret_w : 2 ;
# endif
2018-12-22 20:35:58 +01: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 02:41:17 +01:00
}
}
2016-03-16 22:20:42 +01:00
}
}
2016-03-09 00:00:52 +01:00
2018-01-26 02:41:17 +01: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 ) {
color = cache . font_color ;
color . a * = readonly_alpha ;
2018-04-02 13:41:44 +02:00
}
2016-03-09 00:00:52 +01:00
2018-01-26 02:41:17 +01:00
if ( str [ j ] > = 32 ) {
2018-06-23 17:19:27 +02:00
int yofs = ofs_y + ( get_row_height ( ) - cache . font - > get_height ( ) ) / 2 ;
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_selected_color : color ) ;
2018-01-26 02:41:17 +01:00
if ( underlined ) {
2018-08-02 09:38:56 +02:00
float line_width = 1.0 ;
# ifdef TOOLS_ENABLED
line_width * = EDSCALE ;
# endif
draw_rect ( Rect2 ( char_ofs + char_margin + ofs_x , yofs + ascent + 2 , w , line_width ) , in_selection & & override_selected_font_color ? cache . font_selected_color : color ) ;
2018-01-26 02:41:17 +01:00
}
2018-12-13 19:57:34 +01:00
} else if ( draw_tabs & & str [ j ] = = ' \t ' ) {
2018-01-26 02:41:17 +01:00
int yofs = ( get_row_height ( ) - cache . tab_icon - > get_height ( ) ) / 2 ;
2018-12-13 19:57:34 +01:00
cache . tab_icon - > draw ( ci , Point2 ( char_ofs + char_margin + ofs_x , ofs_y + yofs ) , in_selection & & override_selected_font_color ? cache . font_selected_color : color ) ;
2014-12-17 05:53:34 +01:00
}
2016-03-09 00:00:52 +01:00
2018-01-26 02:41:17 +01:00
char_ofs + = char_w ;
2016-03-09 00:00:52 +01:00
2018-01-26 02:41:17 +01: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 05:53:34 +01:00
}
}
2016-03-09 00:00:52 +01:00
2018-01-26 02:41:17 +01: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-09 00:00:52 +01:00
2017-11-30 04:53:15 +01:00
cursor_pos = Point2i ( char_ofs + char_margin + ofs_x , ofs_y ) ;
2018-12-22 20:35:58 +01:00
cursor_pos . y + = ( get_row_height ( ) - cache . font - > get_height ( ) ) / 2 ;
2016-05-09 20:21:55 +02:00
2016-03-31 21:49:30 +02:00
if ( insert_mode ) {
2018-12-31 12:38:42 +01:00
cursor_insert_offset_y = cache . font - > get_height ( ) - 3 ;
cursor_pos . y + = cursor_insert_offset_y ;
2016-03-31 21:49:30 +02:00
}
2017-08-07 13:09:56 +02: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 09:44:34 +01:00
drawer . draw_char ( ci , Point2 ( char_ofs + char_margin + ofs_x , ofs_y + ascent ) , cchar , next , color ) ;
2017-08-07 13:09:56 +02:00
char_ofs + = im_char_width ;
ofs + + ;
}
}
if ( ime_text . length ( ) = = 0 ) {
if ( draw_caret ) {
if ( insert_mode ) {
2018-01-26 02:41:17 +01:00
int char_w = cache . font - > get_char_size ( ' ' ) . width ;
2018-07-26 23:41:47 +02:00
# ifdef TOOLS_ENABLED
int caret_h = ( block_caret ) ? 4 : 2 * EDSCALE ;
# else
int caret_h = ( block_caret ) ? 4 : 2 ;
# endif
2018-01-26 02:41:17 +01:00
VisualServer : : get_singleton ( ) - > canvas_item_add_rect ( ci , Rect2 ( cursor_pos , Size2i ( char_w , caret_h ) ) , cache . caret_color ) ;
2017-08-07 13:09:56 +02:00
} else {
2018-01-26 02:41:17 +01:00
int char_w = cache . font - > get_char_size ( ' ' ) . width ;
2018-07-26 23:41:47 +02: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 20:35:58 +01: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 13:09:56 +02:00
}
2016-05-09 20:21:55 +02:00
}
}
2014-12-17 02:31:57 +01:00
}
}
}
2016-03-09 00:00:52 +01:00
2017-01-28 15:36:57 +01:00
if ( line_length_guideline ) {
2017-03-05 16:44:50 +01:00
int x = xmargin_beg + cache . font - > get_char_size ( ' 0 ' ) . width * line_length_guideline_col - cursor . x_ofs ;
if ( x > xmargin_beg & & x < xmargin_end ) {
2018-08-20 13:36:34 +02:00
VisualServer : : get_singleton ( ) - > canvas_item_add_line ( ci , Point2 ( x , 0 ) , Point2 ( x , size . height ) , cache . line_length_guideline_color ) ;
2017-01-28 15:36:57 +01:00
}
}
2016-03-04 11:05:42 +01:00
bool completion_below = false ;
2014-12-17 02:31:57 +01:00
if ( completion_active ) {
// code completion box
Ref < StyleBox > csb = get_stylebox ( " completion " ) ;
int maxlines = get_constant ( " completion_lines " ) ;
2017-03-05 16:44:50 +01:00
int cmax_width = get_constant ( " completion_max_width " ) * cache . font - > get_char_size ( ' x ' ) . x ;
2014-12-17 02:31:57 +01:00
int scrollw = get_constant ( " completion_scroll_width " ) ;
Color scrollc = get_color ( " completion_scroll_color " ) ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
int lines = MIN ( completion_options . size ( ) , maxlines ) ;
int w = 0 ;
int h = lines * get_row_height ( ) ;
2014-12-17 02:31:57 +01:00
int nofs = cache . font - > get_string_size ( completion_base ) . width ;
2016-03-09 00:00:52 +01:00
2014-12-17 02:31:57 +01:00
if ( completion_options . size ( ) < 50 ) {
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < completion_options . size ( ) ; i + + ) {
int w2 = MIN ( cache . font - > get_string_size ( completion_options [ i ] ) . x , cmax_width ) ;
if ( w2 > w )
w = w2 ;
2014-12-17 02:31:57 +01:00
}
} else {
2017-03-05 16:44:50 +01:00
w = cmax_width ;
2014-12-17 02:31:57 +01:00
}
2016-03-09 00:00:52 +01:00
2014-12-17 02:31:57 +01:00
int th = h + csb - > get_minimum_size ( ) . y ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( cursor_pos . y + get_row_height ( ) + th > get_size ( ) . height ) {
2018-12-31 12:38:42 +01:00
completion_rect . position . y = cursor_pos . y - th - ( cache . line_spacing / 2.0f ) - cursor_insert_offset_y ;
2014-12-17 02:31:57 +01:00
} else {
2018-12-31 12:38:42 +01: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 11:05:42 +01:00
completion_below = true ;
2014-12-17 02:31:57 +01:00
}
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( cursor_pos . x - nofs + w + scrollw > get_size ( ) . width ) {
2017-06-04 00:25:13 +02:00
completion_rect . position . x = get_size ( ) . width - w - scrollw ;
2014-12-17 02:31:57 +01:00
} else {
2017-06-04 00:25:13 +02:00
completion_rect . position . x = cursor_pos . x - nofs ;
2014-12-17 02:31:57 +01:00
}
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
completion_rect . size . width = w + 2 ;
completion_rect . size . height = h ;
if ( completion_options . size ( ) < = maxlines )
scrollw = 0 ;
2016-03-09 00:00:52 +01:00
2017-06-04 00:25:13 +02:00
draw_style_box ( csb , Rect2 ( completion_rect . position - csb - > get_offset ( ) , completion_rect . size + csb - > get_minimum_size ( ) + Size2 ( scrollw , 0 ) ) ) ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( cache . completion_background_color . a > 0.01 ) {
2017-06-04 00:25:13 +02: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 17:50:31 +02:00
}
2017-03-05 16:44:50 +01:00
int line_from = CLAMP ( completion_index - lines / 2 , 0 , completion_options . size ( ) - lines ) ;
2017-06-04 00:25:13 +02: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 ) ;
draw_rect ( Rect2 ( completion_rect . position , Size2 ( nofs , completion_rect . size . height ) ) , cache . completion_existing_color ) ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < lines ; i + + ) {
2016-03-09 00:00:52 +01:00
2014-12-17 02:31:57 +01:00
int l = line_from + i ;
2017-03-05 16:44:50 +01:00
ERR_CONTINUE ( l < 0 | | l > = completion_options . size ( ) ) ;
2016-07-11 15:25:56 +02:00
Color text_color = cache . completion_font_color ;
2017-03-05 16:44:50 +01:00
for ( int j = 0 ; j < color_regions . size ( ) ; j + + ) {
2014-12-18 04:56:33 +01:00
if ( completion_options [ l ] . begins_with ( color_regions [ j ] . begin_key ) ) {
2017-03-05 16:44:50 +01:00
text_color = color_regions [ j ] . color ;
2014-12-18 04:56:33 +01:00
}
}
2018-12-23 12:19:27 +01:00
int yofs = ( get_row_height ( ) - cache . font - > get_height ( ) ) / 2 ;
draw_string ( cache . font , Point2 ( completion_rect . position . x , completion_rect . position . y + i * get_row_height ( ) + cache . font - > get_ascent ( ) + yofs ) , completion_options [ l ] , text_color , completion_rect . size . width ) ;
2014-12-17 02:31:57 +01:00
}
2016-03-09 00:00:52 +01:00
2014-12-17 02:31:57 +01:00
if ( scrollw ) {
//draw a small scroll rectangle to show a position in the options
float r = maxlines / ( float ) completion_options . size ( ) ;
float o = line_from / ( float ) completion_options . size ( ) ;
2017-06-04 00:25:13 +02: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 02:31:57 +01:00
}
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
completion_line_ofs = line_from ;
2014-12-17 02:31:57 +01:00
}
2016-03-04 11:05:42 +01:00
// check to see if the hint should be drawn
bool show_hint = false ;
2017-03-05 16:44:50 +01:00
if ( completion_hint ! = " " ) {
2016-03-04 11:05:42 +01:00
if ( completion_active ) {
if ( completion_below & & ! callhint_below ) {
show_hint = true ;
2017-03-05 16:44:50 +01:00
} else if ( ! completion_below & & callhint_below ) {
2016-03-04 11:05:42 +01:00
show_hint = true ;
}
2017-03-05 16:44:50 +01:00
} else {
2016-03-04 11:05:42 +01:00
show_hint = true ;
}
}
2016-03-09 00:00:52 +01:00
2016-03-04 11:05:42 +01:00
if ( show_hint ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
Ref < StyleBox > sb = get_stylebox ( " panel " , " TooltipPanel " ) ;
2014-12-17 02:31:57 +01:00
Ref < Font > font = cache . font ;
2017-03-05 16:44:50 +01:00
Color font_color = get_color ( " font_color " , " TooltipLabel " ) ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
int max_w = 0 ;
2014-12-17 02:31:57 +01:00
int sc = completion_hint . get_slice_count ( " \n " ) ;
2017-03-05 16:44:50 +01: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 02:31:57 +01:00
} else {
2017-03-05 16:44:50 +01:00
spacing + = cache . line_spacing ;
2014-12-17 02:31:57 +01:00
}
}
2016-03-09 00:00:52 +01:00
2019-02-12 21:10:08 +01:00
Size2 size2 = Size2 ( max_w , sc * font - > get_height ( ) + spacing ) ;
Size2 minsize = size2 + sb - > get_minimum_size ( ) ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( completion_hint_offset = = - 0xFFFF ) {
completion_hint_offset = cursor_pos . x - offset ;
2014-12-17 02:31:57 +01:00
}
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
Point2 hint_ofs = Vector2 ( completion_hint_offset , cursor_pos . y ) + callhint_offset ;
2016-03-04 11:05:42 +01:00
if ( callhint_below ) {
hint_ofs . y + = get_row_height ( ) + sb - > get_offset ( ) . y ;
2017-03-05 16:44:50 +01:00
} else {
2016-03-04 11:05:42 +01:00
hint_ofs . y - = minsize . y + sb - > get_offset ( ) . y ;
}
2017-03-05 16:44:50 +01:00
draw_style_box ( sb , Rect2 ( hint_ofs , minsize ) ) ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01: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-09 00:00:52 +01:00
2017-03-05 16:44:50 +01: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 02:31:57 +01:00
}
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01: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 02:31:57 +01:00
}
2017-03-05 16:44:50 +01:00
spacing + = cache . line_spacing ;
2014-12-17 02:31:57 +01:00
}
}
2016-03-09 00:00:52 +01:00
2017-06-25 17:50:45 +02:00
if ( has_focus ( ) ) {
2018-06-07 22:16:57 +02:00
OS : : get_singleton ( ) - > set_ime_active ( true ) ;
2017-06-25 17:50:45 +02:00
OS : : get_singleton ( ) - > set_ime_position ( get_global_position ( ) + cursor_pos + Point2 ( 0 , get_row_height ( ) ) ) ;
}
2018-04-02 13:41:44 +02:00
2014-12-17 02:31:57 +01:00
} break ;
case NOTIFICATION_FOCUS_ENTER : {
2016-03-09 00:00:52 +01:00
2016-06-20 21:29:58 +02:00
if ( ! caret_blink_enabled ) {
draw_caret = true ;
}
2017-06-25 17:50:45 +02:00
2018-06-07 22:16:57 +02:00
OS : : get_singleton ( ) - > set_ime_active ( true ) ;
2017-06-25 17:50:45 +02: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 02:31:57 +01:00
if ( OS : : get_singleton ( ) - > has_virtual_keyboard ( ) )
2017-03-05 16:44:50 +01:00
OS : : get_singleton ( ) - > show_virtual_keyboard ( get_text ( ) , get_global_rect ( ) ) ;
2014-12-17 02:31:57 +01:00
} break ;
case NOTIFICATION_FOCUS_EXIT : {
2016-03-09 00:00:52 +01:00
2017-06-25 17:50:45 +02:00
OS : : get_singleton ( ) - > set_ime_position ( Point2 ( ) ) ;
2018-06-07 22:16:57 +02:00
OS : : get_singleton ( ) - > set_ime_active ( false ) ;
2017-08-07 13:09:56 +02:00
ime_text = " " ;
ime_selection = Point2 ( ) ;
2017-06-25 17:50:45 +02:00
2014-12-17 02:31:57 +01:00
if ( OS : : get_singleton ( ) - > has_virtual_keyboard ( ) )
OS : : get_singleton ( ) - > hide_virtual_keyboard ( ) ;
} break ;
2018-11-23 13:07:48 +01:00
case MainLoop : : NOTIFICATION_OS_IME_UPDATE : {
2014-02-10 02:10:30 +01:00
2019-01-08 21:52:56 +01:00
if ( has_focus ( ) ) {
ime_text = OS : : get_singleton ( ) - > get_ime_text ( ) ;
ime_selection = OS : : get_singleton ( ) - > get_ime_selection ( ) ;
update ( ) ;
}
2018-11-23 13:07:48 +01:00
} break ;
}
2017-08-07 13:09:56 +02:00
}
2014-04-27 12:34:37 +02:00
void TextEdit : : _consume_pair_symbol ( CharType ch ) {
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
int cursor_position_to_move = cursor_get_column ( ) + 1 ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01: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-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( is_selection_active ( ) ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
int new_column , new_line ;
2016-03-09 00:00:52 +01:00
2016-04-06 08:36:29 +02:00
begin_complex_operation ( ) ;
2015-01-02 19:08:40 +01:00
_insert_text ( get_selection_from_line ( ) , get_selection_from_column ( ) ,
2017-03-05 16:44:50 +01:00
ch_single ,
& new_line , & new_column ) ;
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
int to_col_offset = 0 ;
2017-03-05 16:44:50 +01:00
if ( get_selection_from_line ( ) = = get_selection_to_line ( ) )
2015-01-02 19:08:40 +01:00
to_col_offset = 1 ;
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
_insert_text ( get_selection_to_line ( ) ,
2017-03-05 16:44:50 +01:00
get_selection_to_column ( ) + to_col_offset ,
ch_single_pair ,
& new_line , & new_column ) ;
2016-04-06 08:36:29 +02:00
end_complex_operation ( ) ;
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
cursor_set_line ( get_selection_to_line ( ) ) ;
cursor_set_column ( get_selection_to_column ( ) + to_col_offset ) ;
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
deselect ( ) ;
update ( ) ;
return ;
}
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( ( ch = = ' \' ' | | ch = = ' " ' ) & &
2019-01-26 17:35:26 +01: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 19:08:40 +01:00
insert_text_at_cursor ( ch_single ) ;
cursor_set_column ( cursor_position_to_move ) ;
return ;
}
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( cursor_get_column ( ) < text [ cursor . line ] . length ( ) ) {
if ( _is_text_char ( text [ cursor . line ] [ cursor_get_column ( ) ] ) ) {
2015-01-02 19:08:40 +01:00
insert_text_at_cursor ( ch_single ) ;
cursor_set_column ( cursor_position_to_move ) ;
return ;
}
2017-03-05 16:44:50 +01:00
if ( _is_pair_right_symbol ( ch ) & &
text [ cursor . line ] [ cursor_get_column ( ) ] = = ch ) {
2015-01-02 19:08:40 +01:00
cursor_set_column ( cursor_position_to_move ) ;
return ;
}
}
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
insert_text_at_cursor ( ch_pair ) ;
cursor_set_column ( cursor_position_to_move ) ;
return ;
2014-04-27 12:34:37 +02:00
}
void TextEdit : : _consume_backspace_for_pair_symbol ( int prev_line , int prev_column ) {
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
bool remove_right_symbol = false ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( cursor . column < text [ cursor . line ] . length ( ) & & cursor . column > 0 ) {
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
CharType left_char = text [ cursor . line ] [ cursor . column - 1 ] ;
CharType right_char = text [ cursor . line ] [ cursor . column ] ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( right_char = = _get_right_pair_symbol ( left_char ) ) {
2015-01-02 19:08:40 +01:00
remove_right_symbol = true ;
}
}
2017-03-05 16:44:50 +01:00
if ( remove_right_symbol ) {
_remove_text ( prev_line , prev_column , cursor . line , cursor . column + 1 ) ;
2015-01-02 19:08:40 +01:00
} else {
2017-03-05 16:44:50 +01:00
_remove_text ( prev_line , prev_column , cursor . line , cursor . column ) ;
2015-01-02 19:08:40 +01:00
}
2014-04-27 12:34:37 +02:00
}
2014-02-10 02:10:30 +01:00
void TextEdit : : backspace_at_cursor ( ) {
2015-01-02 19:08:40 +01:00
if ( readonly )
return ;
2017-03-05 16:44:50 +01:00
if ( cursor . column = = 0 & & cursor . line = = 0 )
2015-01-02 19:08:40 +01:00
return ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01: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-13 00:12:17 +01:00
if ( is_line_hidden ( cursor . line ) )
set_line_as_hidden ( prev_line , true ) ;
2018-05-30 14:02:51 +02:00
if ( is_line_set_as_breakpoint ( cursor . line ) ) {
if ( ! text . is_breakpoint ( prev_line ) )
emit_signal ( " breakpoint_toggled " , prev_line ) ;
2017-11-13 00:12:17 +01:00
set_line_as_breakpoint ( prev_line , true ) ;
2018-05-30 14:02:51 +02:00
}
2017-11-13 00:12:17 +01:00
2019-04-20 13:51:25 +02: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 16:44:50 +01:00
if ( auto_brace_completion_enabled & &
2015-01-02 19:08:40 +01:00
cursor . column > 0 & &
_is_pair_left_symbol ( text [ cursor . line ] [ cursor . column - 1 ] ) ) {
_consume_backspace_for_pair_symbol ( prev_line , prev_column ) ;
} else {
2017-04-17 15:24:30 +02:00
// handle space indentation
if ( cursor . column - indent_size > = 0 & & indent_using_spaces ) {
// if there is enough spaces to count as a tab
bool unindent = true ;
for ( int i = 1 ; i < = indent_size ; i + + ) {
if ( text [ cursor . line ] [ cursor . column - i ] ! = ' ' ) {
unindent = false ;
break ;
}
}
// and it is before the first character
int i = 0 ;
while ( i < cursor . column & & i < text [ cursor . line ] . length ( ) ) {
if ( text [ cursor . line ] [ i ] ! = ' ' & & text [ cursor . line ] [ i ] ! = ' \t ' ) {
unindent = false ;
break ;
}
i + + ;
}
// then we can remove it as a single character.
if ( unindent ) {
_remove_text ( cursor . line , cursor . column - indent_size , cursor . line , cursor . column ) ;
prev_column = cursor . column - indent_size ;
} 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 19:08:40 +01:00
}
2016-03-09 00:00:52 +01:00
2017-11-13 00:12:17 +01:00
cursor_set_line ( prev_line , true , true ) ;
2015-01-02 19:08:40 +01:00
cursor_set_column ( prev_column ) ;
2014-02-10 02:10:30 +01:00
}
2017-12-14 10:10:53 +01:00
void TextEdit : : indent_right ( ) {
2016-04-06 17:37:03 +02:00
2017-12-14 10:10:53 +01:00
int start_line ;
int end_line ;
2016-04-06 17:37:03 +02:00
begin_complex_operation ( ) ;
2017-12-14 10:10:53 +01: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 17:37:03 +02:00
// ignore if the cursor is not past the first column
2017-12-14 10:10:53 +01:00
if ( is_selection_active ( ) & & get_selection_to_column ( ) = = 0 ) {
2016-04-06 17:37:03 +02:00
end_line - - ;
}
for ( int i = start_line ; i < = end_line ; i + + ) {
String line_text = get_line ( i ) ;
2017-04-17 15:24:30 +02:00
if ( indent_using_spaces ) {
line_text = space_indent + line_text ;
} else {
line_text = ' \t ' + line_text ;
}
2016-04-06 17:37:03 +02:00
set_line ( i , line_text ) ;
}
2017-12-14 10:10:53 +01:00
// fix selection and cursor being off by one on the last line
if ( is_selection_active ( ) ) {
2017-12-26 21:13:17 +01:00
select ( selection . from_line , selection . from_column + 1 , selection . to_line , selection . to_column + 1 ) ;
2017-12-14 10:10:53 +01:00
}
2017-12-26 21:13:17 +01:00
cursor_set_column ( cursor . column + 1 , false ) ;
2016-04-06 17:37:03 +02:00
end_complex_operation ( ) ;
update ( ) ;
}
2017-12-14 10:10:53 +01:00
void TextEdit : : indent_left ( ) {
2016-04-06 17:37:03 +02:00
2017-12-14 10:10:53 +01:00
int start_line ;
int end_line ;
2016-04-06 17:37:03 +02:00
begin_complex_operation ( ) ;
2017-12-14 10:10:53 +01: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 17:37:03 +02:00
// ignore if the cursor is not past the first column
2017-12-14 10:10:53 +01:00
if ( is_selection_active ( ) & & get_selection_to_column ( ) = = 0 ) {
2016-04-06 17:37:03 +02: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 ) ;
2017-04-17 15:24:30 +02:00
} else if ( line_text . begins_with ( space_indent ) ) {
line_text = line_text . substr ( indent_size , line_text . length ( ) ) ;
2016-04-06 17:37:03 +02:00
set_line ( i , line_text ) ;
}
}
2017-12-14 10:10:53 +01:00
// fix selection and cursor being off by one on the last line
if ( is_selection_active ( ) & & last_line_text ! = get_line ( end_line ) ) {
2017-12-26 21:13:17 +01:00
select ( selection . from_line , selection . from_column - 1 , selection . to_line , selection . to_column - 1 ) ;
2016-04-06 17:37:03 +02:00
}
2017-12-26 21:13:17 +01:00
cursor_set_column ( cursor . column - 1 , false ) ;
2016-04-06 17:37:03 +02:00
end_complex_operation ( ) ;
update ( ) ;
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
void TextEdit : : _get_mouse_pos ( const Point2i & p_mouse , int & r_row , int & r_col ) const {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
float rows = p_mouse . y ;
2018-01-18 15:52:51 +01:00
rows - = cache . style_normal - > get_margin ( MARGIN_TOP ) ;
2017-03-05 16:44:50 +01:00
rows / = get_row_height ( ) ;
2018-01-26 02:41:17 +01:00
rows + = get_v_scroll_offset ( ) ;
int first_vis_line = get_first_visible_line ( ) ;
2018-01-18 15:52:51 +01:00
int row = first_vis_line + Math : : floor ( rows ) ;
2018-01-26 02:41:17 +01:00
int wrap_index = 0 ;
if ( is_wrap_enabled ( ) | | is_hiding_enabled ( ) ) {
2015-11-07 13:39:03 +01:00
2018-06-14 09:10:30 +02: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-13 00:12:17 +01:00
}
2017-03-05 16:44:50 +01:00
if ( row < 0 )
2017-11-13 00:12:17 +01:00
row = 0 ; //todo
2015-11-07 13:39:03 +01:00
2017-03-05 16:44:50 +01:00
int col = 0 ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( row > = text . size ( ) ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
row = text . size ( ) - 1 ;
col = text [ row ] . size ( ) ;
2015-01-02 19:08:40 +01:00
} else {
2016-03-09 00:00:52 +01:00
2019-04-20 13:51:25 +02: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 02:41:17 +01: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 ) ) {
// move back one if we are at the end of the row
2019-02-12 21:10:08 +01:00
Vector < String > rows2 = get_wrap_rows_text ( row ) ;
2018-01-26 02:41:17 +01:00
int row_end_col = 0 ;
for ( int i = 0 ; i < wrap_index + 1 ; i + + ) {
2019-02-12 21:10:08 +01:00
row_end_col + = rows2 [ i ] . length ( ) ;
2018-01-26 02:41:17 +01:00
}
if ( col > = row_end_col )
col - = 1 ;
}
2015-01-02 19:08:40 +01:00
}
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
r_row = row ;
r_col = col ;
2014-02-10 02:10:30 +01:00
}
2017-05-20 17:38:03 +02:00
void TextEdit : : _gui_input ( const Ref < InputEvent > & p_gui_input ) {
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
Ref < InputEventMouseButton > mb = p_gui_input ;
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
if ( mb . is_valid ( ) ) {
2017-06-03 10:54:24 +02:00
if ( completion_active & & completion_rect . has_point ( mb - > get_position ( ) ) ) {
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
if ( ! mb - > is_pressed ( ) )
return ;
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02: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 19:08:40 +01:00
}
2017-05-20 17:38:03 +02:00
}
if ( mb - > get_button_index ( ) = = BUTTON_WHEEL_DOWN ) {
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
if ( completion_index < completion_options . size ( ) - 1 ) {
completion_index + + ;
completion_current = completion_options [ completion_index ] ;
update ( ) ;
2015-01-02 19:08:40 +01:00
}
2017-05-20 17:38:03 +02:00
}
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
if ( mb - > get_button_index ( ) = = BUTTON_LEFT ) {
2016-03-09 00:00:52 +01:00
2017-06-04 00:25:13 +02: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-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
completion_current = completion_options [ completion_index ] ;
update ( ) ;
if ( mb - > is_doubleclick ( ) )
_confirm_completion ( ) ;
2015-01-02 19:08:40 +01:00
}
2017-05-20 17:38:03 +02:00
return ;
} else {
_cancel_completion ( ) ;
_cancel_code_hint ( ) ;
}
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
if ( mb - > is_pressed ( ) ) {
2017-02-21 23:45:31 +01:00
2017-05-20 17:38:03 +02:00
if ( mb - > get_button_index ( ) = = BUTTON_WHEEL_UP & & ! mb - > get_command ( ) ) {
2018-01-03 23:21:54 +01: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 17:38:03 +02:00
}
if ( mb - > get_button_index ( ) = = BUTTON_WHEEL_DOWN & & ! mb - > get_command ( ) ) {
2018-01-03 23:21:54 +01: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 17:38:03 +02: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 15:52:29 +02:00
2017-05-20 17:38:03 +02:00
_reset_caret_blink_timer ( ) ;
int row , col ;
2017-06-03 10:54:24 +02:00
_get_mouse_pos ( Point2i ( mb - > get_position ( ) . x , mb - > get_position ( ) . y ) , row , col ) ;
2016-05-09 20:21:55 +02:00
2017-05-20 17:38:03 +02:00
if ( mb - > get_command ( ) & & highlighted_word ! = String ( ) ) {
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
emit_signal ( " symbol_lookup " , highlighted_word , row , col ) ;
return ;
}
2016-09-12 15:52:29 +02:00
2017-05-20 17:38:03 +02:00
// toggle breakpoint on gutter click
if ( draw_breakpoint_gutter ) {
int gutter = cache . style_normal - > get_margin ( MARGIN_LEFT ) ;
2019-04-20 13:51:25 +02:00
if ( mb - > get_position ( ) . x > gutter - 6 & & mb - > get_position ( ) . x < = gutter + cache . breakpoint_gutter_width - 3 ) {
2017-05-20 17:38:03 +02:00
set_line_as_breakpoint ( row , ! is_line_set_as_breakpoint ( row ) ) ;
emit_signal ( " breakpoint_toggled " , row ) ;
2016-09-12 15:52:29 +02:00
return ;
}
2017-05-20 17:38:03 +02:00
}
2016-09-12 15:52:29 +02:00
2019-04-20 13:51:25 +02:00
// emit info clicked
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 ;
}
}
2017-11-13 00:12:17 +01:00
// toggle fold on gutter click if can
if ( draw_fold_gutter ) {
int left_margin = cache . style_normal - > get_margin ( MARGIN_LEFT ) ;
2019-04-20 13:51:25 +02:00
int gutter_left = left_margin + cache . breakpoint_gutter_width + cache . line_number_w + cache . info_gutter_width ;
2017-11-16 05:00:27 +01:00
if ( mb - > get_position ( ) . x > gutter_left - 6 & & mb - > get_position ( ) . x < = gutter_left + cache . fold_gutter_width - 3 ) {
2017-11-13 00:12:17 +01:00
if ( is_folded ( row ) ) {
unfold_line ( row ) ;
} else if ( can_fold ( row ) ) {
fold_line ( row ) ;
}
2017-11-16 05:00:27 +01:00
return ;
}
}
// unfold on folded icon click
if ( is_folded ( row ) ) {
int line_width = text . get_line_width ( row ) ;
2019-04-20 13:51:25 +02: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 05:00:27 +01: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-13 00:12:17 +01:00
return ;
}
}
2017-05-20 17:38:03 +02:00
int prev_col = cursor . column ;
int prev_line = cursor . line ;
2016-05-26 15:17:14 +02:00
2017-11-13 00:12:17 +01:00
cursor_set_line ( row , true , false ) ;
2017-05-20 17:38:03 +02:00
cursor_set_column ( col ) ;
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
if ( mb - > get_shift ( ) & & ( cursor . column ! = prev_col | | cursor . line ! = prev_line ) ) {
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02: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 19:08:40 +01:00
2017-05-20 17:38:03 +02:00
if ( cursor . line < selection . selecting_line | | ( cursor . line = = selection . selecting_line & & cursor . column < selection . selecting_column ) ) {
2017-03-05 16:44:50 +01:00
2017-05-20 17:38:03 +02:00
if ( selection . shiftclick_left ) {
2017-03-05 16:44:50 +01:00
SWAP ( selection . from_column , selection . to_column ) ;
SWAP ( selection . from_line , selection . to_line ) ;
2017-05-20 17:38:03 +02:00
selection . shiftclick_left = ! selection . shiftclick_left ;
2015-01-02 19:08:40 +01:00
}
2017-05-20 17:38:03 +02:00
selection . from_column = cursor . column ;
selection . from_line = cursor . line ;
2015-08-13 00:34:07 +02:00
2017-05-20 17:38:03 +02:00
} else if ( cursor . line > selection . selecting_line | | ( cursor . line = = selection . selecting_line & & cursor . column > selection . selecting_column ) ) {
2014-02-10 02:10:30 +01:00
2017-05-20 17:38:03 +02: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 19:08:40 +01:00
}
2017-05-20 17:38:03 +02:00
selection . to_column = cursor . column ;
selection . to_line = cursor . line ;
2015-08-13 00:34:07 +02:00
2017-05-20 17:38:03 +02:00
} else {
selection . active = false ;
2015-01-02 19:08:40 +01:00
}
2014-02-10 02:10:30 +01:00
2017-05-20 17:38:03 +02:00
update ( ) ;
2015-01-02 19:08:40 +01:00
}
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
} else {
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
//if sel active and dblick last time < something
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
//else
selection . active = false ;
selection . selecting_mode = Selection : : MODE_POINTER ;
selection . selecting_line = row ;
selection . selecting_column = col ;
}
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
if ( ! mb - > is_doubleclick ( ) & & ( OS : : get_singleton ( ) - > get_ticks_msec ( ) - last_dblclk ) < 600 & & cursor . line = = prev_line ) {
//tripleclick select line
2017-11-05 16:54:00 +01:00
selection . selecting_mode = Selection : : MODE_LINE ;
_update_selection_mode_line ( ) ;
2017-05-20 17:38:03 +02:00
last_dblclk = 0 ;
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
} else if ( mb - > is_doubleclick ( ) & & text [ cursor . line ] . length ( ) ) {
2016-03-09 00:00:52 +01:00
2017-12-23 09:59:54 +01:00
//doubleclick select word
2017-11-05 16:54:00 +01:00
selection . selecting_mode = Selection : : MODE_WORD ;
_update_selection_mode_word ( ) ;
2017-05-20 17:38:03 +02:00
last_dblclk = OS : : get_singleton ( ) - > get_ticks_msec ( ) ;
2016-05-17 01:25:17 +02:00
}
2015-12-09 19:56:41 +01:00
2017-05-20 17:38:03 +02:00
update ( ) ;
}
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
if ( mb - > get_button_index ( ) = = BUTTON_RIGHT & & context_menu_enabled ) {
2017-12-20 02:36:47 +01: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 ) ) {
2018-09-13 03:38:39 +02:00
// Right click is outside the selected text
2017-12-20 02:36:47 +01:00
deselect ( ) ;
}
}
if ( ! is_selection_active ( ) ) {
cursor_set_line ( row , true , false ) ;
cursor_set_column ( col ) ;
}
}
2017-09-10 15:37:49 +02:00
menu - > set_position ( get_global_transform ( ) . xform ( get_local_mouse_position ( ) ) ) ;
2017-05-20 17:38:03 +02:00
menu - > set_size ( Vector2 ( 1 , 1 ) ) ;
2019-04-23 03:28:38 +02:00
menu - > set_scale ( get_global_transform ( ) . get_scale ( ) ) ;
2017-05-20 17:38:03 +02:00
menu - > popup ( ) ;
grab_focus ( ) ;
2015-01-02 19:08:40 +01:00
}
2017-05-20 17:38:03 +02:00
} else {
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
if ( mb - > get_button_index ( ) = = BUTTON_LEFT )
click_select_held - > stop ( ) ;
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
// notify to show soft keyboard
notification ( NOTIFICATION_FOCUS_ENTER ) ;
}
}
2016-03-09 00:00:52 +01:00
2017-11-01 21:49:39 +01: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 ) ;
return ;
}
2017-05-20 17:38:03 +02:00
Ref < InputEventMouseMotion > mm = p_gui_input ;
2016-09-12 15:52:29 +02:00
2017-05-20 17:38:03 +02:00
if ( mm . is_valid ( ) ) {
if ( select_identifiers_enabled ) {
if ( mm - > get_command ( ) & & mm - > get_button_mask ( ) = = 0 ) {
2017-06-03 10:54:24 +02:00
String new_word = get_word_at_pos ( mm - > get_position ( ) ) ;
2017-05-20 17:38:03 +02:00
if ( new_word ! = highlighted_word ) {
highlighted_word = new_word ;
update ( ) ;
}
} else {
if ( highlighted_word ! = String ( ) ) {
highlighted_word = String ( ) ;
update ( ) ;
2016-09-12 15:52:29 +02:00
}
}
2017-05-20 17:38:03 +02:00
}
2016-09-12 15:52:29 +02:00
2017-05-20 17:38:03 +02:00
if ( mm - > get_button_mask ( ) & BUTTON_MASK_LEFT & & get_viewport ( ) - > gui_get_drag_data ( ) = = Variant ( ) ) { //ignore if dragging
2017-11-05 16:54:00 +01:00
_reset_caret_blink_timer ( ) ;
2016-03-09 00:00:52 +01:00
2017-11-05 16:54:00 +01: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 ;
}
2015-01-02 19:08:40 +01:00
}
2017-05-20 17:38:03 +02:00
}
}
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
Ref < InputEventKey > k = p_gui_input ;
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
if ( k . is_valid ( ) ) {
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
k = k - > duplicate ( ) ; //it will be modified later on
2016-09-12 15:52:29 +02:00
# ifdef OSX_ENABLED
2017-05-20 17:38:03 +02:00
if ( k - > get_scancode ( ) = = KEY_META ) {
2016-09-12 15:52:29 +02:00
# else
2017-05-20 17:38:03 +02:00
if ( k - > get_scancode ( ) = = KEY_CONTROL ) {
2016-09-12 15:52:29 +02:00
# endif
2017-05-20 17:38:03 +02:00
if ( select_identifiers_enabled ) {
2016-09-12 15:52:29 +02:00
2017-05-20 17:38:03 +02:00
if ( k - > is_pressed ( ) ) {
2016-09-12 15:52:29 +02:00
2017-09-10 15:37:49 +02:00
highlighted_word = get_word_at_pos ( get_local_mouse_position ( ) ) ;
2017-05-20 17:38:03 +02:00
update ( ) ;
2016-09-12 15:52:29 +02:00
2017-05-20 17:38:03 +02:00
} else {
highlighted_word = String ( ) ;
update ( ) ;
2016-09-12 15:52:29 +02:00
}
}
2017-05-20 17:38:03 +02:00
}
2016-09-12 15:52:29 +02:00
2017-05-20 17:38:03 +02:00
if ( ! k - > is_pressed ( ) )
return ;
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
if ( completion_active ) {
if ( readonly )
return ;
2015-01-05 02:39:21 +01:00
2017-05-20 17:38:03 +02:00
bool valid = true ;
if ( k - > get_command ( ) | | k - > get_metakey ( ) )
valid = false ;
2015-01-05 02:39:21 +01:00
2017-05-20 17:38:03 +02:00
if ( valid ) {
2015-01-05 02:39:21 +01:00
2017-05-20 17:38:03 +02:00
if ( ! k - > get_alt ( ) ) {
if ( k - > get_scancode ( ) = = KEY_UP ) {
2015-01-05 02:39:21 +01:00
2017-05-20 17:38:03 +02:00
if ( completion_index > 0 ) {
completion_index - - ;
2018-03-08 03:47:42 +01:00
} else {
completion_index = completion_options . size ( ) - 1 ;
2015-01-02 19:08:40 +01:00
}
2018-03-08 03:47:42 +01:00
completion_current = completion_options [ completion_index ] ;
update ( ) ;
2017-05-20 17:38:03 +02:00
accept_event ( ) ;
return ;
}
2015-01-05 02:39:21 +01:00
2017-05-20 17:38:03 +02:00
if ( k - > get_scancode ( ) = = KEY_DOWN ) {
2015-01-05 02:39:21 +01:00
2017-05-20 17:38:03 +02:00
if ( completion_index < completion_options . size ( ) - 1 ) {
completion_index + + ;
2018-03-08 03:47:42 +01:00
} else {
completion_index = 0 ;
2015-01-02 19:08:40 +01:00
}
2018-03-08 03:47:42 +01:00
completion_current = completion_options [ completion_index ] ;
update ( ) ;
2017-05-20 17:38:03 +02:00
accept_event ( ) ;
return ;
}
2015-01-05 02:39:21 +01:00
2017-05-20 17:38:03 +02:00
if ( k - > get_scancode ( ) = = KEY_PAGEUP ) {
2015-01-05 02:39:21 +01:00
2017-05-20 17:38:03 +02:00
completion_index - = get_constant ( " completion_lines " ) ;
if ( completion_index < 0 )
2017-03-05 16:44:50 +01:00
completion_index = 0 ;
2017-05-20 17:38:03 +02:00
completion_current = completion_options [ completion_index ] ;
update ( ) ;
accept_event ( ) ;
return ;
}
2015-01-05 02:39:21 +01:00
2017-05-20 17:38:03 +02:00
if ( k - > get_scancode ( ) = = KEY_PAGEDOWN ) {
2015-01-05 02:39:21 +01:00
2017-05-20 17:38:03 +02:00
completion_index + = get_constant ( " completion_lines " ) ;
if ( completion_index > = completion_options . size ( ) )
2017-03-05 16:44:50 +01:00
completion_index = completion_options . size ( ) - 1 ;
2017-05-20 17:38:03 +02:00
completion_current = completion_options [ completion_index ] ;
update ( ) ;
accept_event ( ) ;
return ;
}
2015-01-05 02:39:21 +01:00
2017-05-20 17:38:03 +02:00
if ( k - > get_scancode ( ) = = KEY_HOME & & completion_index > 0 ) {
2015-01-05 02:39:21 +01:00
2017-05-20 17:38:03 +02:00
completion_index = 0 ;
completion_current = completion_options [ completion_index ] ;
update ( ) ;
accept_event ( ) ;
return ;
}
2015-01-05 02:39:21 +01:00
2017-05-20 17:38:03 +02:00
if ( k - > get_scancode ( ) = = KEY_END & & completion_index < completion_options . size ( ) - 1 ) {
2015-01-05 02:39:21 +01:00
2017-05-20 17:38:03 +02:00
completion_index = completion_options . size ( ) - 1 ;
completion_current = completion_options [ completion_index ] ;
update ( ) ;
accept_event ( ) ;
return ;
}
2015-01-05 02:39:21 +01:00
2017-08-06 15:26:07 +02:00
if ( k - > get_scancode ( ) = = KEY_KP_ENTER | | k - > get_scancode ( ) = = KEY_ENTER | | k - > get_scancode ( ) = = KEY_TAB ) {
2017-05-20 17:38:03 +02:00
_confirm_completion ( ) ;
accept_event ( ) ;
return ;
2015-01-02 19:08:40 +01:00
}
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
if ( k - > get_scancode ( ) = = KEY_BACKSPACE ) {
2016-03-09 00:00:52 +01:00
2016-05-09 20:21:55 +02:00
_reset_caret_blink_timer ( ) ;
2017-05-20 17:38:03 +02:00
backspace_at_cursor ( ) ;
_update_completion_candidates ( ) ;
accept_event ( ) ;
return ;
}
2016-04-03 16:21:16 +02:00
2017-05-20 17:38:03 +02:00
if ( k - > get_scancode ( ) = = KEY_SHIFT ) {
accept_event ( ) ;
return ;
}
}
2016-04-03 16:21:16 +02:00
2017-05-20 17:38:03 +02:00
if ( k - > get_unicode ( ) > 32 ) {
2016-04-03 16:21:16 +02:00
2017-05-20 17:38:03 +02:00
_reset_caret_blink_timer ( ) ;
2016-04-03 16:21:16 +02:00
2017-05-20 17:38:03 +02: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 {
// remove the old character if in insert mode
if ( insert_mode ) {
begin_complex_operation ( ) ;
// make sure we don't try and remove empty space
if ( cursor . column < get_line ( cursor . line ) . length ( ) ) {
_remove_text ( cursor . line , cursor . column , cursor . line , cursor . column + 1 ) ;
2015-01-02 19:08:40 +01:00
}
}
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
_insert_text_at_cursor ( chr ) ;
if ( insert_mode ) {
end_complex_operation ( ) ;
}
2015-01-02 19:08:40 +01:00
}
2017-05-20 17:38:03 +02:00
_update_completion_candidates ( ) ;
accept_event ( ) ;
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
return ;
}
2015-01-02 19:08:40 +01:00
}
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
_cancel_completion ( ) ;
}
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
/* TEST CONTROL FIRST!! */
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
// some remaps for duplicate functions..
if ( k - > get_command ( ) & & ! k - > get_shift ( ) & & ! k - > get_alt ( ) & & ! k - > get_metakey ( ) & & k - > get_scancode ( ) = = KEY_INSERT ) {
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02: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-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
k - > set_scancode ( KEY_V ) ;
k - > set_command ( true ) ;
k - > set_shift ( false ) ;
}
2019-05-02 19:57:37 +02:00
# ifdef APPLE_STYLE_KEYS
if ( k - > get_control ( ) & & ! k - > get_shift ( ) & & ! k - > get_alt ( ) & & ! k - > get_command ( ) ) {
uint32_t move_cursor_key = KEY_UNKNOWN ;
switch ( k - > get_scancode ( ) ) {
case KEY_F : {
move_cursor_key = KEY_RIGHT ;
} break ;
case KEY_B : {
move_cursor_key = KEY_LEFT ;
} break ;
case KEY_P : {
move_cursor_key = KEY_UP ;
} break ;
case KEY_N : {
move_cursor_key = KEY_DOWN ;
} break ;
}
if ( move_cursor_key ! = KEY_UNKNOWN ) {
k - > set_scancode ( move_cursor_key ) ;
k - > set_control ( false ) ;
}
}
# endif
2016-09-12 15:52:29 +02:00
2018-08-10 11:23:13 +02:00
_reset_caret_blink_timer ( ) ;
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
// save here for insert mode, just in case it is cleared in the following section
bool had_selection = selection . active ;
2015-01-02 19:08:40 +01:00
2017-05-20 17:38:03 +02:00
// stuff to do when selection is active..
2019-02-16 07:56:24 +01:00
if ( ! readonly & & selection . active ) {
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
bool clear = false ;
bool unselect = false ;
bool dobreak = false ;
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
switch ( k - > get_scancode ( ) ) {
case KEY_TAB : {
if ( k - > get_shift ( ) ) {
2017-12-14 10:10:53 +01:00
indent_left ( ) ;
2017-05-20 17:38:03 +02:00
} else {
2017-12-14 10:10:53 +01:00
indent_right ( ) ;
2017-05-20 17:38:03 +02:00
}
dobreak = true ;
accept_event ( ) ;
} break ;
case KEY_X :
case KEY_C :
//special keys often used with control, wait...
clear = ( ! k - > get_command ( ) | | k - > get_shift ( ) | | k - > get_alt ( ) ) ;
break ;
case KEY_DELETE :
if ( ! k - > get_shift ( ) ) {
2015-01-02 19:08:40 +01:00
accept_event ( ) ;
2017-03-05 16:44:50 +01:00
clear = true ;
dobreak = true ;
2017-05-20 17:38:03 +02: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 :
// ignore arrows if any modifiers are held (shift = selecting, others may be used for editor hotkeys)
if ( k - > get_command ( ) | | k - > get_shift ( ) | | k - > get_alt ( ) )
2015-01-02 19:08:40 +01:00
break ;
2017-05-20 17:38:03 +02:00
unselect = true ;
break ;
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02: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-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
if ( unselect ) {
selection . active = false ;
selection . selecting_mode = Selection : : MODE_NONE ;
update ( ) ;
}
if ( clear ) {
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
if ( ! dobreak ) {
begin_complex_operation ( ) ;
2015-01-02 19:08:40 +01:00
}
2017-05-20 17:38:03 +02:00
selection . active = false ;
update ( ) ;
_remove_text ( selection . from_line , selection . from_column , selection . to_line , selection . to_column ) ;
2017-11-13 00:12:17 +01:00
cursor_set_line ( selection . from_line , true , false ) ;
2017-05-20 17:38:03 +02:00
cursor_set_column ( selection . from_column ) ;
update ( ) ;
2015-01-02 19:08:40 +01:00
}
2017-05-20 17:38:03 +02:00
if ( dobreak )
return ;
}
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
selection . selecting_text = false ;
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
bool scancode_handled = true ;
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
// special scancode test...
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
switch ( k - > get_scancode ( ) ) {
2016-03-09 00:00:52 +01:00
2017-08-06 15:26:07 +02:00
case KEY_KP_ENTER :
case KEY_ENTER : {
2015-01-02 19:08:40 +01:00
2017-05-20 17:38:03 +02:00
if ( readonly )
break ;
2015-01-02 19:08:40 +01:00
2017-05-20 17:38:03 +02:00
String ins = " \n " ;
//keep indentation
int space_count = 0 ;
2017-08-30 20:35:38 +02:00
for ( int i = 0 ; i < cursor . column ; i + + ) {
if ( text [ cursor . line ] [ i ] = = ' \t ' ) {
2017-05-20 17:38:03 +02:00
if ( indent_using_spaces ) {
ins + = space_indent ;
} else {
ins + = " \t " ;
}
space_count = 0 ;
2017-08-30 20:35:38 +02:00
} else if ( text [ cursor . line ] [ i ] = = ' ' ) {
2017-05-20 17:38:03 +02:00
space_count + + ;
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
if ( space_count = = indent_size ) {
2017-04-17 15:24:30 +02:00
if ( indent_using_spaces ) {
ins + = space_indent ;
} else {
ins + = " \t " ;
}
space_count = 0 ;
}
2017-05-20 17:38:03 +02:00
} else {
break ;
2015-01-02 19:08:40 +01:00
}
2017-05-20 17:38:03 +02:00
}
2017-09-02 16:59:30 +02:00
2017-11-13 00:12:17 +01:00
if ( is_folded ( cursor . line ) )
unfold_line ( cursor . line ) ;
2017-09-02 16:59:30 +02:00
bool brace_indent = false ;
// no need to indent if we are going upwards.
if ( auto_indent & & ! ( k - > get_command ( ) & & k - > get_shift ( ) ) ) {
2018-10-09 23:36:12 +02:00
// indent once again if previous line will end with ':' or '{' and the line is not a comment
2017-09-02 16:59:30 +02:00
// (i.e. colon/brace precedes current cursor position)
2018-10-09 23:36:12 +02: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 17:38:03 +02:00
if ( indent_using_spaces ) {
ins + = space_indent ;
} else {
ins + = " \t " ;
2015-11-29 17:02:35 +01:00
}
2017-09-02 16:59:30 +02:00
// no need to move the brace below if we are not taking the text with us.
if ( text [ cursor . line ] [ cursor . column ] = = ' } ' & & ! k - > get_command ( ) ) {
brace_indent = true ;
ins + = " \n " + ins . substr ( 1 , ins . length ( ) - 2 ) ;
}
2015-11-29 17:02:35 +01:00
}
2017-05-20 17:38:03 +02:00
}
2017-10-31 21:42:53 +01:00
begin_complex_operation ( ) ;
2017-05-20 17:38:03 +02: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 20:10:31 +01:00
cursor_set_column ( text [ cursor . line ] . length ( ) ) ;
2017-05-20 17:38:03 +02:00
} else {
cursor_set_column ( 0 ) ;
first_line = true ;
2016-03-04 20:10:31 +01:00
}
2017-05-20 17:38:03 +02:00
} else {
cursor_set_column ( text [ cursor . line ] . length ( ) ) ;
2016-03-04 20:10:31 +01:00
}
2017-05-20 17:38:03 +02:00
}
2016-03-09 00:00:52 +01:00
2017-10-31 21:42:53 +01:00
insert_text_at_cursor ( ins ) ;
2016-03-04 20:10:31 +01:00
2017-05-20 17:38:03 +02:00
if ( first_line ) {
cursor_set_line ( 0 ) ;
2017-09-02 16:59:30 +02:00
} else if ( brace_indent ) {
cursor_set_line ( cursor . line - 1 ) ;
cursor_set_column ( text [ cursor . line ] . length ( ) ) ;
2017-05-20 17:38:03 +02:00
}
2017-10-31 21:42:53 +01:00
end_complex_operation ( ) ;
2017-05-20 17:38:03 +02:00
} break ;
case KEY_ESCAPE : {
if ( completion_hint ! = " " ) {
completion_hint = " " ;
update ( ) ;
} else {
scancode_handled = false ;
}
} break ;
case KEY_TAB : {
if ( k - > get_command ( ) ) break ; // avoid tab when command
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
if ( readonly )
break ;
2016-03-09 00:00:52 +01:00
2017-12-14 10:10:53 +01:00
if ( is_selection_active ( ) ) {
if ( k - > get_shift ( ) ) {
indent_left ( ) ;
} else {
indent_right ( ) ;
}
2017-05-20 17:38:03 +02:00
} else {
if ( k - > get_shift ( ) ) {
//simple unindent
int cc = cursor . column ;
2017-12-23 09:28:02 +01:00
const int len = text [ cursor . line ] . length ( ) ;
const String & line = text [ cursor . line ] ;
int left = 0 ; // number of whitespace chars at beginning of line
while ( left < len & & ( line [ left ] = = ' \t ' | | line [ left ] = = ' ' ) )
left + + ;
cc = MIN ( cc , left ) ;
while ( cc < indent_size & & cc < left & & line [ cc ] = = ' ' )
cc + + ;
2017-05-20 17:38:03 +02:00
if ( cc > 0 & & cc < = text [ cursor . line ] . length ( ) ) {
2017-12-23 09:28:02 +01:00
if ( text [ cursor . line ] [ cc - 1 ] = = ' \t ' ) {
_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 17:38:03 +02:00
} else {
2017-12-23 09:28:02 +01:00
int n = 0 ;
2016-03-09 00:00:52 +01:00
2017-12-23 09:28:02 +01:00
for ( int i = 1 ; i < = MIN ( cc , indent_size ) ; i + + ) {
if ( line [ cc - i ] ! = ' ' ) {
break ;
2017-04-17 15:24:30 +02:00
}
2017-12-23 09:28:02 +01:00
n + + ;
}
2017-05-20 17:38:03 +02:00
2017-12-23 09:28:02 +01:00
if ( n > 0 ) {
_remove_text ( cursor . line , cc - n , cursor . line , cc ) ;
if ( cursor . column > left - n ) // inside text?
cursor_set_column ( MAX ( 0 , cursor . column - n ) ) ;
update ( ) ;
2017-04-17 15:24:30 +02:00
}
2015-01-02 19:08:40 +01:00
}
2017-12-23 09:28:02 +01:00
} else if ( cc = = 0 & & line . length ( ) > 0 & & line [ 0 ] = = ' \t ' ) {
_remove_text ( cursor . line , 0 , cursor . line , 1 ) ;
update ( ) ;
2017-05-20 17:38:03 +02:00
}
} else {
//simple indent
if ( indent_using_spaces ) {
_insert_text_at_cursor ( space_indent ) ;
2015-01-02 19:08:40 +01:00
} else {
2017-05-20 17:38:03 +02:00
_insert_text_at_cursor ( " \t " ) ;
2015-01-02 19:08:40 +01:00
}
}
2017-05-20 17:38:03 +02:00
}
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
} break ;
case KEY_BACKSPACE : {
if ( readonly )
break ;
2015-11-12 17:35:48 +01:00
# ifdef APPLE_STYLE_KEYS
2017-08-01 19:40:43 +02:00
if ( k - > get_alt ( ) & & cursor . column > 1 ) {
2015-11-12 17:35:48 +01:00
# else
2017-05-20 17:38:03 +02:00
if ( k - > get_alt ( ) ) {
scancode_handled = false ;
break ;
2017-08-01 19:40:43 +02:00
} else if ( k - > get_command ( ) & & cursor . column > 1 ) {
2015-11-12 17:35:48 +01:00
# endif
2017-05-20 17:38:03 +02:00
int line = cursor . line ;
int column = cursor . column ;
2015-11-12 17:35:48 +01:00
2017-08-01 19:40:43 +02:00
// check if we are removing a single whitespace, if so remove it and the next char type
// else we just remove the whitespace
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 ] ) ) {
// remove the single whitespace
column - - ;
}
2015-11-12 17:35:48 +01:00
2017-08-01 19:40:43 +02:00
// check if its a text char
bool only_char = ( _is_text_char ( text [ line ] [ column - 1 ] ) & & ! only_whitespace ) ;
2015-11-12 17:35:48 +01:00
2017-08-01 19:40:43 +02:00
// if its not whitespace or char then symbol.
bool only_symbols = ! ( only_whitespace | | only_char ) ;
2015-11-12 17:35:48 +01:00
2017-05-20 17:38:03 +02:00
while ( column > 0 ) {
2017-08-01 19:40:43 +02:00
bool is_whitespace = _is_whitespace ( text [ line ] [ column - 1 ] ) ;
bool is_text_char = _is_text_char ( text [ line ] [ column - 1 ] ) ;
2015-11-12 17:35:48 +01:00
2017-08-01 19:40:43 +02:00
if ( only_whitespace & & ! is_whitespace ) {
2017-05-20 17:38:03 +02:00
break ;
2017-08-01 19:40:43 +02:00
} else if ( only_char & & ! is_text_char ) {
break ;
} else if ( only_symbols & & ( is_whitespace | | is_text_char ) ) {
break ;
}
2017-05-20 17:38:03 +02:00
column - - ;
2016-01-07 21:38:38 +01:00
}
2017-05-20 17:38:03 +02:00
_remove_text ( line , column , cursor . line , cursor . column ) ;
cursor_set_line ( line ) ;
cursor_set_column ( column ) ;
2018-04-24 15:23:04 +02: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 17:38:03 +02:00
} else {
2017-11-13 00:12:17 +01:00
if ( cursor . line > 0 & & is_line_hidden ( cursor . line - 1 ) )
unfold_line ( cursor . line - 1 ) ;
2017-05-20 17:38:03 +02:00
backspace_at_cursor ( ) ;
2016-01-07 21:38:38 +01:00
}
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
} break ;
case KEY_KP_4 : {
if ( k - > get_unicode ( ) ! = 0 ) {
scancode_handled = false ;
break ;
}
2019-04-05 14:06:16 +02:00
FALLTHROUGH ;
2017-05-20 17:38:03 +02:00
}
case KEY_LEFT : {
if ( k - > get_shift ( ) )
_pre_shift_selection ( ) ;
2016-06-23 23:03:32 +02:00
# ifdef APPLE_STYLE_KEYS
2017-05-20 17:38:03 +02:00
else
2016-06-23 23:03:32 +02:00
# else
2017-05-20 17:38:03 +02:00
else if ( ! k - > get_alt ( ) )
2016-06-23 23:03:32 +02:00
# endif
2017-05-20 17:38:03 +02:00
deselect ( ) ;
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
# ifdef APPLE_STYLE_KEYS
2017-05-20 17:38:03 +02:00
if ( k - > get_command ( ) ) {
2019-04-28 17:51:47 +02: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 17:38:03 +02:00
} else if ( k - > get_alt ( ) ) {
2014-02-10 02:10:30 +01:00
# else
2017-05-20 17:38:03 +02:00
if ( k - > get_alt ( ) ) {
scancode_handled = false ;
break ;
} else if ( k - > get_command ( ) ) {
2014-02-10 02:10:30 +01:00
# endif
2017-05-20 17:38:03 +02:00
int cc = cursor . column ;
2017-08-18 20:53:03 +02:00
if ( cc = = 0 & & cursor . line > 0 ) {
cursor_set_line ( cursor . line - 1 ) ;
cursor_set_column ( text [ cursor . line ] . length ( ) ) ;
2017-12-23 15:47:04 +01:00
} else {
2018-05-14 04:26:45 +02:00
bool prev_char = false ;
2017-12-23 15:47:04 +01:00
while ( cc > 0 ) {
bool ischar = _is_text_char ( text [ cursor . line ] [ cc - 1 ] ) ;
2016-03-09 00:00:52 +01:00
2017-12-23 15:47:04 +01:00
if ( prev_char & & ! ischar )
break ;
2016-03-09 00:00:52 +01:00
2017-12-23 15:47:04 +01:00
prev_char = ischar ;
cc - - ;
}
cursor_set_column ( cc ) ;
2015-01-02 19:08:40 +01:00
}
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
} else if ( cursor . column = = 0 ) {
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
if ( cursor . line > 0 ) {
2017-11-16 05:00:27 +01:00
cursor_set_line ( cursor . line - num_lines_from ( CLAMP ( cursor . line - 1 , 0 , text . size ( ) - 1 ) , - 1 ) ) ;
2017-05-20 17:38:03 +02:00
cursor_set_column ( text [ cursor . line ] . length ( ) ) ;
2016-01-07 21:38:38 +01:00
}
2017-05-20 17:38:03 +02: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 21:38:38 +01:00
}
2019-04-05 14:06:16 +02:00
FALLTHROUGH ;
2017-05-20 17:38:03 +02:00
}
case KEY_RIGHT : {
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
if ( k - > get_shift ( ) )
_pre_shift_selection ( ) ;
2016-06-23 23:03:32 +02:00
# ifdef APPLE_STYLE_KEYS
2017-05-20 17:38:03 +02:00
else
2016-06-23 23:03:32 +02:00
# else
2017-05-20 17:38:03 +02:00
else if ( ! k - > get_alt ( ) )
2016-06-23 23:03:32 +02:00
# endif
2017-05-20 17:38:03 +02:00
deselect ( ) ;
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
# ifdef APPLE_STYLE_KEYS
2017-05-20 17:38:03 +02:00
if ( k - > get_command ( ) ) {
cursor_set_column ( text [ cursor . line ] . length ( ) ) ;
} else if ( k - > get_alt ( ) ) {
2014-02-10 02:10:30 +01:00
# else
2017-05-20 17:38:03 +02:00
if ( k - > get_alt ( ) ) {
scancode_handled = false ;
break ;
} else if ( k - > get_command ( ) ) {
2014-02-10 02:10:30 +01:00
# endif
2017-05-20 17:38:03 +02:00
int cc = cursor . column ;
2017-08-18 20:53:03 +02: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 15:47:04 +01:00
} else {
2018-05-14 04:26:45 +02:00
bool prev_char = false ;
2017-12-23 15:47:04 +01:00
while ( cc < text [ cursor . line ] . length ( ) ) {
bool ischar = _is_text_char ( text [ cursor . line ] [ cc ] ) ;
2016-03-09 00:00:52 +01:00
2017-12-23 15:47:04 +01:00
if ( prev_char & & ! ischar )
break ;
prev_char = ischar ;
cc + + ;
}
cursor_set_column ( cc ) ;
2017-05-20 17:38:03 +02:00
}
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
} else if ( cursor . column = = text [ cursor . line ] . length ( ) ) {
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
if ( cursor . line < text . size ( ) - 1 ) {
2017-11-16 05:00:27 +01:00
cursor_set_line ( cursor_get_line ( ) + num_lines_from ( CLAMP ( cursor . line + 1 , 0 , text . size ( ) - 1 ) , 1 ) , true , false ) ;
2017-05-20 17:38:03 +02:00
cursor_set_column ( 0 ) ;
2015-01-02 19:08:40 +01:00
}
2017-05-20 17:38:03 +02:00
} else {
cursor_set_column ( cursor_get_column ( ) + 1 ) ;
}
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
if ( k - > get_shift ( ) )
_post_shift_selection ( ) ;
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
} break ;
case KEY_KP_8 : {
if ( k - > get_unicode ( ) ! = 0 ) {
scancode_handled = false ;
break ;
2016-01-07 21:38:38 +01:00
}
2019-04-05 14:06:16 +02:00
FALLTHROUGH ;
2017-05-20 17:38:03 +02:00
}
case KEY_UP : {
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
if ( k - > get_alt ( ) ) {
scancode_handled = false ;
break ;
}
2016-04-27 19:32:14 +02:00
# ifndef APPLE_STYLE_KEYS
2017-05-20 17:38:03 +02:00
if ( k - > get_command ( ) ) {
2016-04-27 19:32:14 +02:00
# else
2017-05-20 17:38:03 +02:00
if ( k - > get_command ( ) & & k - > get_alt ( ) ) {
2019-02-04 11:55:45 +01:00
# endif
2017-05-20 17:38:03 +02:00
_scroll_lines_up ( ) ;
break ;
}
2016-04-27 19:32:14 +02:00
2019-02-04 11:55:45 +01:00
if ( k - > get_shift ( ) ) {
_pre_shift_selection ( ) ;
}
# ifdef APPLE_STYLE_KEYS
2018-01-26 02:41:17 +01:00
if ( k - > get_command ( ) ) {
2019-02-04 11:55:45 +01:00
2017-05-20 17:38:03 +02:00
cursor_set_line ( 0 ) ;
2018-01-26 02:41:17 +01:00
} else
2014-02-10 02:10:30 +01:00
# endif
2018-01-26 02:41:17 +01: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-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
if ( k - > get_shift ( ) )
_post_shift_selection ( ) ;
_cancel_code_hint ( ) ;
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
} break ;
case KEY_KP_2 : {
if ( k - > get_unicode ( ) ! = 0 ) {
scancode_handled = false ;
break ;
2016-01-07 21:38:38 +01:00
}
2019-04-05 14:06:16 +02:00
FALLTHROUGH ;
2017-05-20 17:38:03 +02:00
}
case KEY_DOWN : {
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
if ( k - > get_alt ( ) ) {
scancode_handled = false ;
break ;
}
2016-04-27 19:32:14 +02:00
# ifndef APPLE_STYLE_KEYS
2017-05-20 17:38:03 +02:00
if ( k - > get_command ( ) ) {
2016-04-27 19:32:14 +02:00
# else
2017-05-20 17:38:03 +02:00
if ( k - > get_command ( ) & & k - > get_alt ( ) ) {
2019-02-04 11:55:45 +01:00
# endif
2017-05-20 17:38:03 +02:00
_scroll_lines_down ( ) ;
break ;
}
2016-04-27 19:32:14 +02:00
2019-02-04 11:55:45 +01:00
if ( k - > get_shift ( ) ) {
_pre_shift_selection ( ) ;
}
# ifdef APPLE_STYLE_KEYS
2018-01-26 02:41:17 +01:00
if ( k - > get_command ( ) ) {
cursor_set_line ( get_last_unhidden_line ( ) , true , false , 9999 ) ;
} else
2014-02-10 02:10:30 +01:00
# endif
2018-01-26 02:41:17 +01: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 07:53:16 +01:00
} else {
2018-01-26 02:41:17 +01: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 07:53:16 +01:00
}
}
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
if ( k - > get_shift ( ) )
_post_shift_selection ( ) ;
_cancel_code_hint ( ) ;
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
} break ;
case KEY_DELETE : {
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
if ( readonly )
break ;
2016-03-15 13:03:38 +01:00
2017-05-20 17:38:03 +02:00
if ( k - > get_shift ( ) & & ! k - > get_command ( ) & & ! k - > get_alt ( ) ) {
cut ( ) ;
break ;
}
2016-03-15 13:03:38 +01:00
2017-05-20 17:38:03 +02:00
int curline_len = text [ cursor . line ] . length ( ) ;
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
if ( cursor . line = = text . size ( ) - 1 & & cursor . column = = curline_len )
break ; //nothing to do
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
int next_line = cursor . column < curline_len ? cursor . line : cursor . line + 1 ;
int next_column ;
2015-11-12 17:35:48 +01:00
# ifdef APPLE_STYLE_KEYS
2017-08-01 19:40:43 +02:00
if ( k - > get_alt ( ) & & cursor . column < curline_len - 1 ) {
2015-11-12 17:35:48 +01:00
# else
2017-05-20 17:38:03 +02:00
if ( k - > get_alt ( ) ) {
scancode_handled = false ;
break ;
2017-08-01 19:40:43 +02:00
} else if ( k - > get_command ( ) & & cursor . column < curline_len - 1 ) {
2015-11-12 17:35:48 +01:00
# endif
2017-05-20 17:38:03 +02:00
int line = cursor . line ;
int column = cursor . column ;
2015-11-12 17:35:48 +01:00
2017-08-01 19:40:43 +02:00
// check if we are removing a single whitespace, if so remove it and the next char type
// else we just remove the whitespace
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 ] ) ) {
// remove the single whitespace
column + + ;
2017-05-20 17:38:03 +02:00
}
2015-11-12 17:35:48 +01:00
2017-08-01 19:40:43 +02:00
// check if its a text char
bool only_char = ( _is_text_char ( text [ line ] [ column ] ) & & ! only_whitespace ) ;
2015-11-12 17:35:48 +01:00
2017-08-01 19:40:43 +02:00
// if its not whitespace or char then symbol.
bool only_symbols = ! ( only_whitespace | | only_char ) ;
2015-11-12 17:35:48 +01:00
2017-08-01 19:40:43 +02: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 17:38:03 +02:00
break ;
2017-08-01 19:40:43 +02:00
} else if ( only_char & & ! is_text_char ) {
break ;
} else if ( only_symbols & & ( is_whitespace | | is_text_char ) ) {
break ;
}
2017-05-20 17:38:03 +02:00
column + + ;
2015-11-12 17:35:48 +01:00
}
2017-05-20 17:38:03 +02:00
next_line = line ;
next_column = column ;
2018-04-24 15:23:04 +02:00
# ifdef APPLE_STYLE_KEYS
} else if ( k - > get_command ( ) ) {
next_column = curline_len ;
next_line = cursor . line ;
# endif
2017-05-20 17:38:03 +02:00
} else {
next_column = cursor . column < curline_len ? ( cursor . column + 1 ) : 0 ;
}
2015-11-12 17:35:48 +01:00
2017-05-20 17:38:03 +02: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 22:45:28 +01:00
}
2019-04-05 14:06:16 +02:00
FALLTHROUGH ;
2017-05-20 17:38:03 +02:00
}
case KEY_HOME : {
2019-04-05 14:06:16 +02:00
# ifdef APPLE_STYLE_KEYS
2017-05-20 17:38:03 +02:00
if ( k - > get_shift ( ) )
_pre_shift_selection ( ) ;
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
cursor_set_line ( 0 ) ;
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
if ( k - > get_shift ( ) )
_post_shift_selection ( ) ;
else if ( k - > get_command ( ) | | k - > get_control ( ) )
deselect ( ) ;
2014-02-10 02:10:30 +01:00
# else
2017-05-20 17:38:03 +02:00
if ( k - > get_shift ( ) )
_pre_shift_selection ( ) ;
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
if ( k - > get_command ( ) ) {
cursor_set_line ( 0 ) ;
cursor_set_column ( 0 ) ;
} else {
2018-01-26 02:41:17 +01:00
// move cursor column to start of wrapped row and then to start of text
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 08:01:41 +01:00
}
2018-01-26 02:41:17 +01:00
if ( cursor . column = = row_start_col | | wi = = 0 ) {
// compute whitespace symbols seq length
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-09 00:00:52 +01:00
2018-01-26 02:41:17 +01: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 17:38:03 +02:00
}
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
if ( k - > get_shift ( ) )
_post_shift_selection ( ) ;
else if ( k - > get_command ( ) | | k - > get_control ( ) )
deselect ( ) ;
_cancel_completion ( ) ;
completion_hint = " " ;
2016-01-07 22:45:28 +01:00
# endif
2019-04-05 14:06:16 +02:00
} break ;
2017-05-20 17:38:03 +02:00
case KEY_KP_1 : {
if ( k - > get_unicode ( ) ! = 0 ) {
scancode_handled = false ;
break ;
2016-01-07 22:45:28 +01:00
}
2019-04-05 14:06:16 +02:00
FALLTHROUGH ;
2017-05-20 17:38:03 +02:00
}
case KEY_END : {
2019-04-05 14:06:16 +02:00
# ifdef APPLE_STYLE_KEYS
2017-05-20 17:38:03 +02:00
if ( k - > get_shift ( ) )
_pre_shift_selection ( ) ;
2016-01-07 22:45:28 +01:00
2018-01-26 02:41:17 +01:00
cursor_set_line ( get_last_unhidden_line ( ) , true , false , 9999 ) ;
2016-01-07 22:45:28 +01:00
2017-05-20 17:38:03 +02:00
if ( k - > get_shift ( ) )
_post_shift_selection ( ) ;
else if ( k - > get_command ( ) | | k - > get_control ( ) )
deselect ( ) ;
2016-01-07 22:45:28 +01:00
# else
2017-05-20 17:38:03 +02:00
if ( k - > get_shift ( ) )
_pre_shift_selection ( ) ;
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
if ( k - > get_command ( ) )
2018-01-26 02:41:17 +01:00
cursor_set_line ( get_last_unhidden_line ( ) , true , false , 9999 ) ;
// move cursor column to end of wrapped row and then to end of text
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-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
if ( k - > get_shift ( ) )
_post_shift_selection ( ) ;
else if ( k - > get_command ( ) | | k - > get_control ( ) )
deselect ( ) ;
2015-06-27 20:52:39 +02:00
2017-05-20 17:38:03 +02:00
_cancel_completion ( ) ;
completion_hint = " " ;
2014-02-10 02:10:30 +01:00
# endif
2019-04-05 14:06:16 +02:00
} break ;
2017-05-20 17:38:03 +02:00
case KEY_KP_9 : {
if ( k - > get_unicode ( ) ! = 0 ) {
scancode_handled = false ;
break ;
2016-01-07 22:45:28 +01:00
}
2019-04-05 14:06:16 +02:00
FALLTHROUGH ;
2017-05-20 17:38:03 +02:00
}
case KEY_PAGEUP : {
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
if ( k - > get_shift ( ) )
_pre_shift_selection ( ) ;
2016-03-09 00:00:52 +01:00
2018-01-26 02:41:17 +01: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-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
if ( k - > get_shift ( ) )
_post_shift_selection ( ) ;
2015-06-27 20:52:39 +02:00
2017-05-20 17:38:03 +02:00
_cancel_completion ( ) ;
completion_hint = " " ;
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
} break ;
case KEY_KP_3 : {
if ( k - > get_unicode ( ) ! = 0 ) {
scancode_handled = false ;
break ;
2016-01-07 22:45:28 +01:00
}
2019-04-05 14:06:16 +02:00
FALLTHROUGH ;
2017-05-20 17:38:03 +02:00
}
case KEY_PAGEDOWN : {
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
if ( k - > get_shift ( ) )
_pre_shift_selection ( ) ;
2016-03-09 00:00:52 +01:00
2018-01-26 02:41:17 +01: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-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
if ( k - > get_shift ( ) )
_post_shift_selection ( ) ;
2015-06-27 20:52:39 +02:00
2017-05-20 17:38:03 +02:00
_cancel_completion ( ) ;
completion_hint = " " ;
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
} break ;
case KEY_A : {
2016-03-09 00:00:52 +01:00
2018-04-05 18:58:15 +02:00
# ifndef APPLE_STYLE_KEYS
2018-05-18 19:46:40 +02:00
if ( ! k - > get_control ( ) | | k - > get_shift ( ) | | k - > get_alt ( ) ) {
2017-05-20 17:38:03 +02:00
scancode_handled = false ;
break ;
}
select_all ( ) ;
2018-04-05 18:58:15 +02:00
# else
2018-05-18 19:46:40 +02:00
if ( ( ! k - > get_command ( ) & & ! k - > get_control ( ) ) ) {
2018-04-05 18:58:15 +02: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-09 00:00:52 +01:00
2018-04-05 18:58:15 +02: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 17:38:03 +02:00
} break ;
case KEY_X : {
if ( readonly ) {
break ;
}
if ( ! k - > get_command ( ) | | k - > get_shift ( ) | | k - > get_alt ( ) ) {
scancode_handled = false ;
break ;
}
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
cut ( ) ;
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
} break ;
case KEY_C : {
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
if ( ! k - > get_command ( ) | | k - > get_shift ( ) | | k - > get_alt ( ) ) {
scancode_handled = false ;
break ;
}
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
copy ( ) ;
2016-03-15 13:02:38 +01:00
2017-05-20 17:38:03 +02:00
} break ;
case KEY_Z : {
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
if ( ! k - > get_command ( ) ) {
scancode_handled = false ;
break ;
}
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
if ( k - > get_shift ( ) )
redo ( ) ;
else
undo ( ) ;
} break ;
2017-08-24 07:22:00 +02:00
case KEY_Y : {
if ( ! k - > get_command ( ) ) {
scancode_handled = false ;
break ;
}
redo ( ) ;
} break ;
2017-05-20 17:38:03 +02:00
case KEY_V : {
if ( readonly ) {
break ;
}
if ( ! k - > get_command ( ) | | k - > get_shift ( ) | | k - > get_alt ( ) ) {
scancode_handled = false ;
break ;
}
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
paste ( ) ;
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
} break ;
case KEY_SPACE : {
2015-01-04 15:03:31 +01:00
# ifdef OSX_ENABLED
2017-05-20 17:38:03 +02:00
if ( completion_enabled & & k - > get_metakey ( ) ) { //cmd-space is spotlight shortcut in OSX
2015-01-04 15:03:31 +01:00
# else
2017-05-20 17:38:03 +02:00
if ( completion_enabled & & k - > get_command ( ) ) {
2015-01-04 15:03:31 +01:00
# endif
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
query_code_comple ( ) ;
scancode_handled = true ;
} else {
scancode_handled = false ;
}
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
} break ;
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
case KEY_U : {
if ( ! k - > get_command ( ) | | k - > get_shift ( ) ) {
scancode_handled = false ;
break ;
} else {
if ( selection . active ) {
int ini = selection . from_line ;
int end = selection . to_line ;
2018-09-08 14:20:45 +02:00
2017-05-20 17:38:03 +02:00
for ( int i = ini ; i < = end ; i + + ) {
2018-09-08 14:20:45 +02:00
_uncomment_line ( i ) ;
2015-01-02 19:08:40 +01:00
}
2017-05-20 17:38:03 +02:00
} else {
2018-09-08 14:20:45 +02:00
_uncomment_line ( cursor . line ) ;
if ( cursor . column > = get_line ( cursor . line ) . length ( ) ) {
cursor . column = MAX ( 0 , get_line ( cursor . line ) . length ( ) - 1 ) ;
2017-09-17 22:07:42 +02:00
}
2015-01-02 19:08:40 +01:00
}
2017-05-20 17:38:03 +02:00
update ( ) ;
}
} break ;
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
default : {
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
scancode_handled = false ;
} break ;
}
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
if ( scancode_handled )
accept_event ( ) ;
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
if ( k - > get_scancode ( ) = = KEY_INSERT ) {
set_insert_mode ( ! insert_mode ) ;
accept_event ( ) ;
return ;
}
2016-03-09 00:00:52 +01:00
2018-01-18 21:37:17 +01:00
if ( ! scancode_handled & & ! k - > get_command ( ) ) { //for German kbds
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
if ( k - > get_unicode ( ) > = 32 ) {
2016-03-31 21:49:30 +02:00
2017-05-20 17:38:03 +02:00
if ( readonly )
return ;
2016-03-31 21:49:30 +02:00
2017-05-20 17:38:03 +02:00
// remove the old character if in insert mode and no selection
if ( insert_mode & & ! had_selection ) {
begin_complex_operation ( ) ;
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
// make sure we don't try and remove empty space
if ( cursor . column < get_line ( cursor . line ) . length ( ) ) {
_remove_text ( cursor . line , cursor . column , cursor . line , cursor . column + 1 ) ;
2015-01-02 19:08:40 +01:00
}
2017-05-20 17:38:03 +02:00
}
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
const CharType chr [ 2 ] = { ( CharType ) k - > get_unicode ( ) , 0 } ;
2016-04-05 14:52:18 +02:00
2017-05-20 17:38:03 +02: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 19:08:40 +01:00
} else {
2017-05-20 17:38:03 +02:00
_insert_text_at_cursor ( chr ) ;
}
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
if ( insert_mode & & ! had_selection ) {
end_complex_operation ( ) ;
2015-01-02 19:08:40 +01:00
}
2017-05-20 17:38:03 +02:00
if ( selection . active ! = had_selection ) {
end_complex_operation ( ) ;
}
accept_event ( ) ;
2015-01-02 19:08:40 +01:00
}
2017-05-20 17:38:03 +02:00
}
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
return ;
2015-01-02 19:08:40 +01:00
}
2014-02-10 02:10:30 +01:00
}
2018-09-08 14:20:45 +02:00
void TextEdit : : _uncomment_line ( int p_line ) {
String line_text = get_line ( p_line ) ;
for ( int i = 0 ; i < line_text . length ( ) ; i + + ) {
if ( line_text [ i ] = = ' # ' ) {
_remove_text ( p_line , i , p_line , i + 1 ) ;
if ( p_line = = selection . to_line & & selection . to_column > line_text . length ( ) - 1 ) {
selection . to_column - = 1 ;
if ( selection . to_column > = selection . from_column ) {
selection . active = false ;
}
}
return ;
} else if ( line_text [ i ] ! = ' \t ' & & line_text [ i ] ! = ' ' ) {
return ;
}
}
}
2017-11-01 21:49:39 +01:00
void TextEdit : : _scroll_up ( real_t p_delta ) {
2017-12-28 15:49:37 +01:00
if ( scrolling & & smooth_scroll_enabled & & SGN ( target_v_scroll - v_scroll - > get_value ( ) ) ! = SGN ( - p_delta ) )
scrolling = false ;
2017-11-01 21:49:39 +01:00
if ( scrolling ) {
target_v_scroll = ( target_v_scroll - p_delta ) ;
} else {
2018-01-26 02:41:17 +01:00
target_v_scroll = ( get_v_scroll ( ) - p_delta ) ;
2017-11-01 21:49:39 +01:00
}
if ( smooth_scroll_enabled ) {
if ( target_v_scroll < = 0 ) {
target_v_scroll = 0 ;
}
2017-12-28 15:49:37 +01: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 09:28:14 +02:00
set_physics_process_internal ( true ) ;
2017-12-28 15:49:37 +01:00
}
2017-11-01 21:49:39 +01:00
} else {
2018-01-26 02:41:17 +01:00
set_v_scroll ( target_v_scroll ) ;
2017-11-01 21:49:39 +01:00
}
}
void TextEdit : : _scroll_down ( real_t p_delta ) {
2017-12-28 15:49:37 +01:00
if ( scrolling & & smooth_scroll_enabled & & SGN ( target_v_scroll - v_scroll - > get_value ( ) ) ! = SGN ( p_delta ) )
scrolling = false ;
2017-11-01 21:49:39 +01:00
if ( scrolling ) {
target_v_scroll = ( target_v_scroll + p_delta ) ;
} else {
2018-01-26 02:41:17 +01:00
target_v_scroll = ( get_v_scroll ( ) + p_delta ) ;
2017-11-01 21:49:39 +01:00
}
if ( smooth_scroll_enabled ) {
2018-10-05 22:50:49 +02:00
int max_v_scroll = round ( v_scroll - > get_max ( ) - v_scroll - > get_page ( ) ) ;
2017-11-01 21:49:39 +01:00
if ( target_v_scroll > max_v_scroll ) {
target_v_scroll = max_v_scroll ;
}
2017-12-28 15:49:37 +01: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 09:28:14 +02:00
set_physics_process_internal ( true ) ;
2017-12-28 15:49:37 +01:00
}
2017-11-01 21:49:39 +01:00
} else {
2018-01-26 02:41:17 +01:00
set_v_scroll ( target_v_scroll ) ;
2017-11-01 21:49:39 +01:00
}
}
2014-02-10 02:10:30 +01:00
void TextEdit : : _pre_shift_selection ( ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( ! selection . active | | selection . selecting_mode = = Selection : : MODE_NONE ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
selection . selecting_line = cursor . line ;
selection . selecting_column = cursor . column ;
selection . active = true ;
2015-01-02 19:08:40 +01:00
}
2015-08-13 00:34:07 +02:00
2017-03-05 16:44:50 +01:00
selection . selecting_mode = Selection : : MODE_SHIFT ;
2014-02-10 02:10:30 +01:00
}
void TextEdit : : _post_shift_selection ( ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( selection . active & & selection . selecting_mode = = Selection : : MODE_SHIFT ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
select ( selection . selecting_line , selection . selecting_column , cursor . line , cursor . column ) ;
2015-01-02 19:08:40 +01:00
update ( ) ;
}
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
selection . selecting_text = true ;
2014-02-10 02:10:30 +01:00
}
2016-04-27 19:32:14 +02:00
void TextEdit : : _scroll_lines_up ( ) {
2017-08-22 21:02:08 +02:00
scrolling = false ;
2016-04-27 19:32:14 +02:00
// adjust the vertical scroll
2018-01-26 02:41:17 +01:00
set_v_scroll ( get_v_scroll ( ) - 1 ) ;
// adjust the cursor to viewport
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 19:32:14 +02:00
2018-01-26 02:41:17 +01: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 19:32:14 +02:00
}
}
void TextEdit : : _scroll_lines_down ( ) {
2017-08-22 21:02:08 +02:00
scrolling = false ;
2016-04-27 19:32:14 +02:00
// adjust the vertical scroll
2018-01-26 02:41:17 +01:00
set_v_scroll ( get_v_scroll ( ) + 1 ) ;
2016-04-27 19:32:14 +02:00
2018-01-26 02:41:17 +01:00
// adjust the cursor to viewport
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 19:32:14 +02:00
}
}
2014-02-10 02:10:30 +01:00
/**** TEXT EDIT CORE API ****/
2017-03-05 16:44:50 +01: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-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
//save for undo...
2017-03-05 16:44:50 +01:00
ERR_FAIL_INDEX ( p_line , text . size ( ) ) ;
ERR_FAIL_COND ( p_char < 0 ) ;
2016-03-09 00:00:52 +01:00
2018-05-30 14:02:51 +02:00
/* STEP 1 remove \r from source text and separate in substrings */
Vector < String > substrings = p_text . replace ( " \r " , " " ) . split ( " \n " ) ;
/* STEP 2 fire breakpoint_toggled signals */
// 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 ) ;
}
}
/* STEP 3 add spaces if the char is greater than the end of the line */
2017-03-05 16:44:50 +01:00
while ( p_char > text [ p_line ] . length ( ) ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
text . set ( p_line , text [ p_line ] + String : : chr ( ' ' ) ) ;
2015-01-02 19:08:40 +01:00
}
2016-03-09 00:00:52 +01:00
2018-05-30 14:02:51 +02:00
/* STEP 4 separate dest string in pre and post text */
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01: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-09 00:00:52 +01:00
2019-02-12 21:10:08 +01:00
for ( int j = 0 ; j < substrings . size ( ) ; j + + ) {
2015-01-02 19:08:40 +01:00
//insert the substrings
2016-03-09 00:00:52 +01:00
2019-02-12 21:10:08 +01:00
if ( j = = 0 ) {
2016-03-09 00:00:52 +01:00
2019-02-12 21:10:08 +01:00
text . set ( p_line , preinsert_text + substrings [ j ] ) ;
2015-01-02 19:08:40 +01:00
} else {
2016-03-09 00:00:52 +01:00
2019-02-12 21:10:08 +01:00
text . insert ( p_line + j , substrings [ j ] ) ;
2015-01-02 19:08:40 +01:00
}
2016-03-09 00:00:52 +01:00
2019-02-12 21:10:08 +01:00
if ( j = = substrings . size ( ) - 1 ) {
2016-03-09 00:00:52 +01:00
2019-02-12 21:10:08 +01:00
text . set ( p_line + j , text [ p_line + j ] + postinsert_text ) ;
2015-01-02 19:08:40 +01:00
}
}
2016-03-09 00:00:52 +01:00
2018-05-30 14:02:51 +02:00
if ( shift_first_line ) {
2017-11-13 00:12:17 +01: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 13:51:25 +02:00
text . set_info_icon ( p_line + 1 , text . get_info_icon ( p_line ) , text . get_info ( p_line ) ) ;
2017-11-13 00:12:17 +01:00
text . set_breakpoint ( p_line , false ) ;
text . set_hidden ( p_line , false ) ;
2019-04-20 13:51:25 +02:00
text . set_info_icon ( p_line , NULL , " " ) ;
2017-11-13 00:12:17 +01:00
}
2018-01-26 02:41:17 +01:00
text . set_line_wrap_amount ( p_line , - 1 ) ;
2017-03-05 16:44:50 +01:00
r_end_line = p_line + substrings . size ( ) - 1 ;
r_end_column = text [ r_end_line ] . length ( ) - postinsert_text . length ( ) ;
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
if ( ! text_changed_dirty & & ! setting_text ) {
if ( is_inside_tree ( ) )
2017-03-05 16:44:50 +01:00
MessageQueue : : get_singleton ( ) - > push_call ( this , " _text_changed_emit " ) ;
text_changed_dirty = true ;
2015-01-02 19:08:40 +01:00
}
2018-04-07 15:14:19 +02:00
_line_edited_from ( p_line ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01: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-09 00:00:52 +01:00
2017-03-05 16:44:50 +01: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 ( ) ) ;
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-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
String ret ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
for ( int i = p_from_line ; i < = p_to_line ; i + + ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01: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-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( i > p_from_line )
ret + = " \n " ;
ret + = text [ i ] . substr ( begin , end - begin ) ;
2015-01-02 19:08:40 +01:00
}
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
return ret ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
void TextEdit : : _base_remove_text ( int p_from_line , int p_from_column , int p_to_line , int p_to_column ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01: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 ) ;
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-09 00:00:52 +01:00
2017-03-05 16:44:50 +01: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-09 00:00:52 +01:00
2018-05-30 14:02:51 +02:00
int lines = p_to_line - p_from_line ;
2016-03-09 00:00:52 +01:00
2018-05-30 14:02:51 +02: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 19:08:40 +01:00
}
2016-03-09 00:00:52 +01:00
2018-05-30 14:02:51 +02:00
for ( int i = p_from_line ; i < p_to_line ; i + + ) {
text . remove ( p_from_line + 1 ) ;
}
2017-03-05 16:44:50 +01:00
text . set ( p_from_line , pre_text + post_text ) ;
2016-03-09 00:00:52 +01:00
2018-01-26 02:41:17 +01:00
text . set_line_wrap_amount ( p_from_line , - 1 ) ;
2015-01-02 19:08:40 +01:00
if ( ! text_changed_dirty & & ! setting_text ) {
if ( is_inside_tree ( ) )
2017-03-05 16:44:50 +01:00
MessageQueue : : get_singleton ( ) - > push_call ( this , " _text_changed_emit " ) ;
text_changed_dirty = true ;
2015-01-02 19:08:40 +01:00
}
2018-04-07 15:14:19 +02:00
_line_edited_from ( p_from_line ) ;
2014-02-10 02:10:30 +01:00
}
2017-08-11 21:10:05 +02: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-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
if ( ! setting_text )
idle_detect - > start ( ) ;
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
if ( undo_enabled ) {
_clear_redo ( ) ;
}
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
int retline , retchar ;
_base_insert_text ( p_line , p_char , p_text , retline , retchar ) ;
2015-01-02 19:08:40 +01:00
if ( r_end_line )
2017-03-05 16:44:50 +01:00
* r_end_line = retline ;
2017-08-11 21:10:05 +02:00
if ( r_end_char )
* r_end_char = retchar ;
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
if ( ! undo_enabled )
return ;
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
/* UNDO!! */
TextOperation op ;
2017-03-05 16:44:50 +01: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-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
//see if it shold just be set as current op
2017-03-05 16:44:50 +01:00
if ( current_op . type ! = op . type ) {
2016-03-13 21:08:12 +01:00
op . prev_version = get_version ( ) ;
2015-01-02 19:08:40 +01:00
_push_current_op ( ) ;
2017-03-05 16:44:50 +01:00
current_op = op ;
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
return ; //set as current op, return
}
//see if it can be merged
2017-03-05 16:44:50 +01:00
if ( current_op . to_line ! = p_line | | current_op . to_column ! = p_char ) {
2016-03-13 21:08:12 +01:00
op . prev_version = get_version ( ) ;
2015-01-02 19:08:40 +01:00
_push_current_op ( ) ;
2017-03-05 16:44:50 +01:00
current_op = op ;
2015-01-02 19:08:40 +01:00
return ; //set as current op, return
}
//merge current op
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
current_op . text + = p_text ;
current_op . to_column = retchar ;
current_op . to_line = retline ;
current_op . version = op . version ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
void TextEdit : : _remove_text ( int p_from_line , int p_from_column , int p_to_line , int p_to_column ) {
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
if ( ! setting_text )
idle_detect - > start ( ) ;
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
String text ;
if ( undo_enabled ) {
_clear_redo ( ) ;
2017-03-05 16:44:50 +01:00
text = _base_get_text ( p_from_line , p_from_column , p_to_line , p_to_column ) ;
2015-01-02 19:08:40 +01:00
}
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
_base_remove_text ( p_from_line , p_from_column , p_to_line , p_to_column ) ;
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
if ( ! undo_enabled )
return ;
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
/* UNDO!! */
TextOperation op ;
2017-03-05 16:44:50 +01: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-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
//see if it shold just be set as current op
2017-03-05 16:44:50 +01:00
if ( current_op . type ! = op . type ) {
2016-03-13 21:08:12 +01:00
op . prev_version = get_version ( ) ;
2015-01-02 19:08:40 +01:00
_push_current_op ( ) ;
2017-03-05 16:44:50 +01:00
current_op = op ;
2015-01-02 19:08:40 +01:00
return ; //set as current op, return
}
//see if it can be merged
2017-03-05 16:44:50 +01:00
if ( current_op . from_line = = p_to_line & & current_op . from_column = = p_to_column ) {
2015-01-02 19:08:40 +01:00
//basckace or similar
2017-03-05 16:44:50 +01:00
current_op . text = text + current_op . text ;
current_op . from_line = p_from_line ;
current_op . from_column = p_from_column ;
2015-01-02 19:08:40 +01:00
return ; //update current op
}
2017-03-05 16:44:50 +01:00
if ( current_op . from_line = = p_from_line & & current_op . from_column = = p_from_column ) {
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
//current_op.text=text+current_op.text;
//current_op.from_line=p_from_line;
//current_op.from_column=p_from_column;
//return; //update current op
}
2016-03-09 00:00:52 +01:00
2016-03-13 21:08:12 +01:00
op . prev_version = get_version ( ) ;
2015-01-02 19:08:40 +01:00
_push_current_op ( ) ;
2017-03-05 16:44:50 +01:00
current_op = op ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
void TextEdit : : _insert_text_at_cursor ( const String & p_text ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
int new_column , new_line ;
_insert_text ( cursor . line , cursor . column , p_text , & new_line , & new_column ) ;
2015-01-02 19:08:40 +01:00
cursor_set_line ( new_line ) ;
cursor_set_column ( new_column ) ;
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
update ( ) ;
2014-02-10 02:10:30 +01:00
}
2018-04-07 15:14:19 +02: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 ) ;
}
}
2014-02-10 02:10:30 +01:00
int TextEdit : : get_char_count ( ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
int totalsize = 0 ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < text . size ( ) ; i + + ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( i > 0 )
2015-01-02 19:08:40 +01:00
totalsize + + ; // incliude \n
2017-03-05 16:44:50 +01:00
totalsize + = text [ i ] . length ( ) ;
2015-01-02 19:08:40 +01:00
}
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
return totalsize ; // omit last \n
2014-02-10 02:10:30 +01:00
}
2016-06-12 18:31:22 +02:00
Size2 TextEdit : : get_minimum_size ( ) const {
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
return cache . style_normal - > get_minimum_size ( ) ;
2014-02-10 02:10:30 +01:00
}
2017-11-13 00:12:17 +01:00
2014-02-10 02:10:30 +01:00
int TextEdit : : get_visible_rows ( ) const {
2016-03-09 00:00:52 +01:00
2018-08-20 13:36:34 +02:00
int total = get_size ( ) . height ;
2017-03-05 16:44:50 +01:00
total - = cache . style_normal - > get_minimum_size ( ) . height ;
2018-01-26 02:41:17 +01:00
if ( h_scroll - > is_visible_in_tree ( ) )
total - = h_scroll - > get_size ( ) . height ;
2017-03-05 16:44:50 +01:00
total / = get_row_height ( ) ;
2017-08-20 15:07:54 +02:00
return total ;
2014-02-10 02:10:30 +01:00
}
2017-11-13 00:12:17 +01:00
2018-01-26 02:41:17 +01:00
int TextEdit : : get_total_visible_rows ( ) const {
// returns the total amount of rows we need in the editor.
// This skips hidden lines and counts each wrapping of a line.
if ( ! is_hiding_enabled ( ) & & ! is_wrap_enabled ( ) )
2017-11-16 05:00:27 +01:00
return text . size ( ) ;
2018-01-26 02:41:17 +01:00
int total_rows = 0 ;
2017-11-16 05:00:27 +01:00
for ( int i = 0 ; i < text . size ( ) ; i + + ) {
2018-01-26 02:41:17 +01:00
if ( ! text . is_hidden ( i ) ) {
total_rows + + ;
total_rows + = times_line_wraps ( i ) ;
}
2017-11-16 05:00:27 +01:00
}
2018-01-26 02:41:17 +01:00
return total_rows ;
2017-11-16 05:00:27 +01:00
}
2019-01-05 17:58:54 +01:00
void TextEdit : : _update_wrap_at ( ) {
2017-11-16 05:00:27 +01:00
2019-04-20 13:51:25 +02: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 - wrap_right_offset ;
2018-01-26 02:41:17 +01:00
update_cursor_wrap_offset ( ) ;
text . clear_wrap_cache ( ) ;
2017-11-16 05:00:27 +01:00
2018-01-26 02:41:17 +01:00
for ( int i = 0 ; i < text . size ( ) ; i + + ) {
// update all values that wrap
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 05:00:27 +01:00
}
}
2018-01-26 02:41:17 +01:00
void TextEdit : : adjust_viewport_to_cursor ( ) {
2017-11-16 05:00:27 +01:00
2018-01-26 02:41:17 +01:00
// make sure cursor is visible on the screen
scrolling = false ;
2017-11-16 05:00:27 +01:00
2018-01-26 02:41:17 +01:00
int cur_line = cursor . line ;
int cur_wrap = get_cursor_wrap_index ( ) ;
2017-11-16 05:00:27 +01:00
2018-01-26 02:41:17 +01: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-09 00:00:52 +01:00
2018-01-26 02:41:17 +01:00
if ( cur_line < first_vis_line | | ( cur_line = = first_vis_line & & cur_wrap < first_vis_wrap ) ) {
// cursor is above screen
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 ) ) {
// cursor is below screen
set_line_as_last_visible ( cur_line , cur_wrap ) ;
2017-11-16 05:00:27 +01:00
}
2016-03-09 00:00:52 +01:00
2019-04-20 13:51:25 +02: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 ;
2017-01-13 14:45:50 +01:00
if ( v_scroll - > is_visible_in_tree ( ) )
2017-03-05 16:44:50 +01:00
visible_width - = v_scroll - > get_combined_minimum_size ( ) . width ;
visible_width - = 20 ; // give it a little more space
2016-03-09 00:00:52 +01:00
2018-05-15 21:32:09 +02:00
if ( ! is_wrap_enabled ( ) ) {
2018-01-26 02:41:17 +01:00
// adjust x offset
int cursor_x = get_column_x_offset ( cursor . column , text [ cursor . line ] ) ;
2016-03-09 00:00:52 +01:00
2018-01-26 02:41:17 +01:00
if ( cursor_x > ( cursor . x_ofs + visible_width ) )
cursor . x_ofs = cursor_x - visible_width + 1 ;
2016-03-09 00:00:52 +01:00
2018-01-26 02:41:17 +01:00
if ( cursor_x < cursor . x_ofs )
cursor . x_ofs = cursor_x ;
} else {
cursor . x_ofs = 0 ;
2017-11-26 20:10:05 +01:00
}
2018-01-26 02:41:17 +01:00
h_scroll - > set_value ( cursor . x_ofs ) ;
2015-01-02 19:08:40 +01:00
update ( ) ;
2014-02-10 02:10:30 +01:00
}
2016-07-21 03:40:08 +02:00
void TextEdit : : center_viewport_to_cursor ( ) {
2018-01-26 02:41:17 +01:00
// move viewport so the cursor is in the center of the screen
scrolling = false ;
2016-07-21 03:40:08 +02:00
2017-11-13 00:12:17 +01:00
if ( is_line_hidden ( cursor . line ) )
unfold_line ( cursor . line ) ;
2018-01-26 02:41:17 +01:00
set_line_as_center_visible ( cursor . line , get_cursor_wrap_index ( ) ) ;
2019-04-20 13:51:25 +02: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 ;
2017-01-13 14:45:50 +01:00
if ( v_scroll - > is_visible_in_tree ( ) )
2017-03-05 16:44:50 +01:00
visible_width - = v_scroll - > get_combined_minimum_size ( ) . width ;
visible_width - = 20 ; // give it a little more space
2016-07-21 03:40:08 +02:00
2018-01-26 02:41:17 +01:00
if ( is_wrap_enabled ( ) ) {
// center x offset
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 22:46:31 +01:00
}
2018-01-26 02:41:17 +01:00
h_scroll - > set_value ( cursor . x_ofs ) ;
2016-07-21 03:40:08 +02:00
2018-01-26 02:41:17 +01:00
update ( ) ;
}
2016-07-21 03:40:08 +02:00
2018-01-26 02:41:17 +01: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 03:40:08 +02:00
2018-01-26 02:41:17 +01: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 ) {
// update the value
Vector < String > rows = get_wrap_rows_text ( line ) ;
wrap_amount = rows . size ( ) - 1 ;
text . set_line_wrap_amount ( line , wrap_amount ) ;
2017-11-26 20:10:05 +01:00
}
2018-01-26 02:41:17 +01: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 14:52:33 +01:00
if ( tab_offset_px > = wrap_at ) {
tab_offset_px = 0 ;
}
2018-01-26 02:41:17 +01:00
while ( col < line_text . length ( ) ) {
2018-09-28 06:31:15 +02:00
CharType c = line_text [ col ] ;
2018-01-26 02:41:17 +01: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 14:52:33 +01:00
if ( indent_ofs + word_px + w > wrap_at ) {
// not enough space to add this char; start next line
2018-01-26 02:41:17 +01:00
wrap_substring + = word_str ;
2019-02-20 14:52:33 +01:00
lines . push_back ( wrap_substring ) ;
cur_wrap_index + + ;
wrap_substring = " " ;
px = 0 ;
2018-01-26 02:41:17 +01:00
2019-02-20 14:52:33 +01:00
word_str = " " ;
word_str + = c ;
word_px = w ;
} else {
word_str + = c ;
word_px + = w ;
if ( c = = ' ' ) {
// end of a word; add this word to the substring
2018-01-26 02:41:17 +01:00
wrap_substring + = word_str ;
2019-02-20 14:52:33 +01:00
px + = word_px ;
2018-01-26 02:41:17 +01:00
word_str = " " ;
word_px = 0 ;
}
2019-02-20 14:52:33 +01:00
if ( indent_ofs + px + word_px > wrap_at ) {
// this word will be moved to the next line
lines . push_back ( wrap_substring ) ;
// reset for next wrap
cur_wrap_index + + ;
wrap_substring = " " ;
px = 0 ;
}
2018-01-26 02:41:17 +01:00
}
col + + ;
}
// line ends before hit wrap_at; add this word to the substring
wrap_substring + = word_str ;
lines . push_back ( wrap_substring ) ;
2018-12-18 14:01:15 +01:00
// update cache
text . set_line_wrap_amount ( p_line , lines . size ( ) - 1 ) ;
2018-01-26 02:41:17 +01: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 ;
// loop through wraps in the line text until we get to the column
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 03:40:08 +02:00
}
2015-08-13 00:34:07 +02:00
void TextEdit : : cursor_set_column ( int p_col , bool p_adjust_viewport ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( p_col < 0 )
p_col = 0 ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
cursor . column = p_col ;
if ( cursor . column > get_line ( cursor . line ) . length ( ) )
cursor . column = get_line ( cursor . line ) . length ( ) ;
2016-03-09 00:00:52 +01:00
2018-01-26 02:41:17 +01:00
cursor . last_fit_x = get_column_x_offset_for_line ( cursor . column , cursor . line ) ;
2016-03-09 00:00:52 +01:00
2015-08-13 00:34:07 +02:00
if ( p_adjust_viewport )
adjust_viewport_to_cursor ( ) ;
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
if ( ! cursor_changed_dirty ) {
if ( is_inside_tree ( ) )
2017-03-05 16:44:50 +01:00
MessageQueue : : get_singleton ( ) - > push_call ( this , " _cursor_changed_emit " ) ;
cursor_changed_dirty = true ;
2015-01-02 19:08:40 +01:00
}
2014-02-10 02:10:30 +01:00
}
2018-01-26 02:41:17 +01:00
void TextEdit : : cursor_set_line ( int p_row , bool p_adjust_viewport , bool p_can_be_hidden , int p_wrap_index ) {
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
if ( setting_row )
return ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
setting_row = true ;
if ( p_row < 0 )
p_row = 0 ;
2016-03-09 00:00:52 +01:00
2018-01-26 02:41:17 +01:00
if ( p_row > = text . size ( ) )
p_row = text . size ( ) - 1 ;
2016-03-09 00:00:52 +01:00
2017-11-13 00:12:17 +01:00
if ( ! p_can_be_hidden ) {
2017-11-16 05:00:27 +01: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-13 00:12:17 +01:00
}
}
}
}
2017-03-05 16:44:50 +01:00
cursor . line = p_row ;
2018-01-26 02:41:17 +01:00
int n_col = get_char_pos_for_line ( cursor . last_fit_x , p_row , p_wrap_index ) ;
if ( is_wrap_enabled ( ) & & p_wrap_index < times_line_wraps ( p_row ) ) {
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-09 00:00:52 +01:00
2015-08-13 00:34:07 +02:00
if ( p_adjust_viewport )
adjust_viewport_to_cursor ( ) ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
setting_row = false ;
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
if ( ! cursor_changed_dirty ) {
if ( is_inside_tree ( ) )
2017-03-05 16:44:50 +01:00
MessageQueue : : get_singleton ( ) - > push_call ( this , " _cursor_changed_emit " ) ;
cursor_changed_dirty = true ;
2015-01-02 19:08:40 +01:00
}
2014-02-10 02:10:30 +01:00
}
int TextEdit : : cursor_get_column ( ) const {
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
return cursor . column ;
2014-02-10 02:10:30 +01:00
}
int TextEdit : : cursor_get_line ( ) const {
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
return cursor . line ;
2014-02-10 02:10:30 +01:00
}
2016-05-09 20:21:55 +02: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 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
void TextEdit : : cursor_set_block_mode ( const bool p_enable ) {
2016-07-12 17:07:17 +02:00
block_caret = p_enable ;
update ( ) ;
}
bool TextEdit : : cursor_is_block_mode ( ) const {
return block_caret ;
}
2017-12-20 02:36:47 +01: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 21:02:08 +02:00
void TextEdit : : _v_scroll_input ( ) {
scrolling = false ;
}
2014-02-10 02:10:30 +01:00
void TextEdit : : _scroll_moved ( double p_to_val ) {
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
if ( updating_scrolls )
return ;
2016-03-09 00:00:52 +01:00
2017-01-13 14:45:50 +01:00
if ( h_scroll - > is_visible_in_tree ( ) )
2017-03-05 16:44:50 +01:00
cursor . x_ofs = h_scroll - > get_value ( ) ;
2017-11-16 05:00:27 +01:00
if ( v_scroll - > is_visible_in_tree ( ) ) {
2018-01-26 02:41:17 +01:00
// set line ofs and wrap ofs
int v_scroll_i = floor ( get_v_scroll ( ) ) ;
int sc = 0 ;
int n_line ;
for ( n_line = 0 ; n_line < text . size ( ) ; n_line + + ) {
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 05:00:27 +01:00
}
2015-01-02 19:08:40 +01:00
update ( ) ;
2014-02-10 02:10:30 +01:00
}
int TextEdit : : get_row_height ( ) const {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
return cache . font - > get_height ( ) + cache . line_spacing ;
2014-02-10 02:10:30 +01:00
}
2018-01-26 02:41:17 +01:00
int TextEdit : : get_char_pos_for_line ( int p_px , int p_line , int p_wrap_index ) const {
2016-03-09 00:00:52 +01:00
2018-01-26 02:41:17 +01:00
ERR_FAIL_INDEX_V ( p_line , text . size ( ) , 0 ) ;
2016-03-09 00:00:52 +01:00
2018-01-26 02:41:17 +01:00
if ( line_wraps ( p_line ) ) {
2016-03-09 00:00:52 +01:00
2018-01-26 02:41:17 +01: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 14:52:33 +01:00
if ( wrap_offset_px > = wrap_at ) {
wrap_offset_px = 0 ;
}
2018-01-26 02:41:17 +01: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-09 00:00:52 +01:00
2018-01-26 02:41:17 +01:00
return c_pos ;
} else {
2016-03-09 00:00:52 +01:00
2018-01-26 02:41:17 +01:00
return get_char_pos_for ( p_px , text [ p_line ] ) ;
}
}
2016-03-09 00:00:52 +01:00
2018-01-26 02:41:17 +01:00
int TextEdit : : get_column_x_offset_for_line ( int p_char , int p_line ) const {
2016-03-09 00:00:52 +01:00
2018-01-26 02:41:17 +01:00
ERR_FAIL_INDEX_V ( p_line , text . size ( ) , 0 ) ;
2016-03-09 00:00:52 +01:00
2018-01-26 02:41:17 +01: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 19:08:40 +01:00
}
2018-01-26 02:41:17 +01: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 14:52:33 +01:00
if ( wrap_offset_px > = wrap_at ) {
wrap_offset_px = 0 ;
}
2018-01-26 02:41:17 +01: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-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( p_px < ( px + w / 2 ) )
2015-01-02 19:08:40 +01:00
break ;
2017-03-05 16:44:50 +01:00
px + = w ;
2015-01-02 19:08:40 +01:00
c + + ;
}
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
return c ;
2014-02-10 02:10:30 +01:00
}
2018-01-26 02:41:17 +01:00
int TextEdit : : get_column_x_offset ( int p_char , String p_str ) const {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
int px = 0 ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < p_char ; i + + ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( i > = p_str . length ( ) )
2015-01-02 19:08:40 +01:00
break ;
2016-03-09 00:00:52 +01:00
2018-01-26 02:41:17 +01:00
px + = text . get_char_width ( p_str [ i ] , p_str [ i + 1 ] , px ) ;
2015-01-02 19:08:40 +01:00
}
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
return px ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
void TextEdit : : insert_text_at_cursor ( const String & p_text ) {
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
if ( selection . active ) {
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
cursor_set_line ( selection . from_line ) ;
cursor_set_column ( selection . from_column ) ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01: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 19:08:40 +01:00
}
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
_insert_text_at_cursor ( p_text ) ;
update ( ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
Control : : CursorShape TextEdit : : get_cursor_shape ( const Point2 & p_pos ) const {
2017-04-06 17:35:08 +02:00
if ( highlighted_word ! = String ( ) )
2016-09-12 15:52:29 +02:00
return CURSOR_POINTING_HAND ;
2019-04-20 13:51:25 +02: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 05:00:27 +01: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 ) ;
// breakpoint icon
2019-04-20 13:51:25 +02:00
if ( draw_breakpoint_gutter & & p_pos . x > left_margin - 6 & & p_pos . x < = left_margin + cache . breakpoint_gutter_width - 3 ) {
2017-11-16 05:00:27 +01:00
return CURSOR_POINTING_HAND ;
}
2019-04-20 13:51:25 +02:00
// info icons
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 ;
}
2017-11-16 05:00:27 +01:00
// fold icon
2019-04-20 13:51:25 +02: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 05:00:27 +01:00
if ( is_folded ( row ) | | can_fold ( row ) )
return CURSOR_POINTING_HAND ;
else
return CURSOR_ARROW ;
}
2019-04-20 13:51:25 +02:00
2015-01-02 19:08:40 +01:00
return CURSOR_ARROW ;
2017-11-16 05:00:27 +01:00
} else {
int row , col ;
_get_mouse_pos ( p_pos , row , col ) ;
// eol fold icon
if ( is_folded ( row ) ) {
int line_width = text . get_line_width ( row ) ;
2019-04-20 13:51:25 +02: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 05:00:27 +01: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 19:08:40 +01:00
}
2017-11-16 05:00:27 +01:00
2018-09-11 18:25:40 +02:00
return get_default_cursor_shape ( ) ;
2014-04-17 13:30:40 +02:00
}
2017-03-05 16:44:50 +01:00
void TextEdit : : set_text ( String p_text ) {
2014-04-17 13:30:40 +02:00
2017-03-05 16:44:50 +01:00
setting_text = true ;
2018-05-19 20:31:18 +02:00
_clear ( ) ;
2015-01-02 19:08:40 +01:00
_insert_text_at_cursor ( p_text ) ;
2015-04-18 21:57:01 +02:00
clear_undo_history ( ) ;
2017-03-05 16:44:50 +01:00
cursor . column = 0 ;
cursor . line = 0 ;
cursor . x_ofs = 0 ;
cursor . line_ofs = 0 ;
2018-01-26 02:41:17 +01:00
cursor . wrap_ofs = 0 ;
2017-03-05 16:44:50 +01:00
cursor . last_fit_x = 0 ;
2015-01-02 19:08:40 +01:00
cursor_set_line ( 0 ) ;
cursor_set_column ( 0 ) ;
update ( ) ;
2017-03-05 16:44:50 +01:00
setting_text = false ;
2018-05-19 20:31:18 +02:00
2015-01-02 19:08:40 +01:00
//get_range()->set(0);
2014-02-10 02:10:30 +01:00
} ;
String TextEdit : : get_text ( ) {
2015-01-02 19:08:40 +01:00
String longthing ;
int len = text . size ( ) ;
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < len ; i + + ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
longthing + = text [ i ] ;
if ( i ! = len - 1 )
longthing + = " \n " ;
2014-12-17 02:31:57 +01:00
}
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
return longthing ;
} ;
2014-12-17 02:31:57 +01:00
2016-09-12 15:52:29 +02:00
String TextEdit : : get_text_for_lookup_completion ( ) {
2017-03-05 16:44:50 +01:00
int row , col ;
2017-09-10 15:37:49 +02:00
_get_mouse_pos ( get_local_mouse_position ( ) , row , col ) ;
2016-09-12 15:52:29 +02:00
String longthing ;
int len = text . size ( ) ;
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < len ; i + + ) {
2016-09-12 15:52:29 +02:00
2017-03-05 16:44:50 +01:00
if ( i = = row ) {
longthing + = text [ i ] . substr ( 0 , col ) ;
longthing + = String : : chr ( 0xFFFF ) ; //not unicode, represents the cursor
longthing + = text [ i ] . substr ( col , text [ i ] . size ( ) ) ;
2016-09-12 15:52:29 +02:00
} else {
2017-03-05 16:44:50 +01:00
longthing + = text [ i ] ;
2016-09-12 15:52:29 +02:00
}
2017-03-05 16:44:50 +01:00
if ( i ! = len - 1 )
longthing + = " \n " ;
2016-09-12 15:52:29 +02:00
}
return longthing ;
}
2015-01-02 19:08:40 +01:00
String TextEdit : : get_text_for_completion ( ) {
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
String longthing ;
int len = text . size ( ) ;
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < len ; i + + ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( i = = cursor . line ) {
longthing + = text [ i ] . substr ( 0 , cursor . column ) ;
longthing + = String : : chr ( 0xFFFF ) ; //not unicode, represents the cursor
longthing + = text [ i ] . substr ( cursor . column , text [ i ] . size ( ) ) ;
2015-01-02 19:08:40 +01:00
} else {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
longthing + = text [ i ] ;
2015-01-02 19:08:40 +01:00
}
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( i ! = len - 1 )
longthing + = " \n " ;
2015-01-02 19:08:40 +01:00
}
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
return longthing ;
2014-12-17 02:31:57 +01:00
} ;
2014-02-10 02:10:30 +01:00
String TextEdit : : get_line ( int line ) const {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( line < 0 | | line > = text . size ( ) )
2015-01-02 19:08:40 +01:00
return " " ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
return text [ line ] ;
2014-02-10 02:10:30 +01:00
} ;
void TextEdit : : _clear ( ) {
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
clear_undo_history ( ) ;
text . clear ( ) ;
2017-03-05 16:44:50 +01:00
cursor . column = 0 ;
cursor . line = 0 ;
cursor . x_ofs = 0 ;
cursor . line_ofs = 0 ;
2018-01-26 02:41:17 +01:00
cursor . wrap_ofs = 0 ;
2017-03-05 16:44:50 +01:00
cursor . last_fit_x = 0 ;
2018-11-25 18:24:49 +01:00
selection . active = false ;
2014-02-10 02:10:30 +01:00
}
void TextEdit : : clear ( ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
setting_text = true ;
2015-01-02 19:08:40 +01:00
_clear ( ) ;
2017-03-05 16:44:50 +01:00
setting_text = false ;
2014-02-10 02:10:30 +01:00
} ;
void TextEdit : : set_readonly ( bool p_readonly ) {
2016-03-09 00:00:52 +01:00
2019-04-22 01:09:52 +02:00
if ( readonly = = p_readonly )
return ;
2017-03-05 16:44:50 +01:00
readonly = p_readonly ;
2019-04-22 01:09:52 +02:00
// Reorganize context menu.
menu - > clear ( ) ;
if ( ! readonly )
menu - > add_item ( RTR ( " Cut " ) , MENU_CUT , KEY_MASK_CMD | KEY_X ) ;
menu - > add_item ( RTR ( " Copy " ) , MENU_COPY , KEY_MASK_CMD | KEY_C ) ;
if ( ! readonly )
menu - > add_item ( RTR ( " Paste " ) , MENU_PASTE , KEY_MASK_CMD | KEY_V ) ;
menu - > add_separator ( ) ;
menu - > add_item ( RTR ( " Select All " ) , MENU_SELECT_ALL , KEY_MASK_CMD | KEY_A ) ;
if ( ! readonly ) {
menu - > add_item ( RTR ( " Clear " ) , MENU_CLEAR ) ;
menu - > add_separator ( ) ;
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 ) ;
}
2017-11-30 04:53:15 +01:00
update ( ) ;
2014-02-10 02:10:30 +01:00
}
2017-11-11 19:07:17 +01:00
bool TextEdit : : is_readonly ( ) const {
return readonly ;
}
2018-01-26 02:41:17 +01:00
void TextEdit : : set_wrap_enabled ( bool p_wrap_enabled ) {
2016-03-09 00:00:52 +01:00
2018-01-26 02:41:17 +01:00
wrap_enabled = p_wrap_enabled ;
2014-02-10 02:10:30 +01:00
}
2018-01-26 02:41:17 +01:00
bool TextEdit : : is_wrap_enabled ( ) const {
2018-01-11 23:35:12 +01:00
2018-01-26 02:41:17 +01:00
return wrap_enabled ;
2018-01-11 23:35:12 +01:00
}
2014-02-10 02:10:30 +01:00
void TextEdit : : set_max_chars ( int p_max_chars ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
max_chars = p_max_chars ;
2014-02-10 02:10:30 +01:00
}
2018-01-11 23:35:12 +01:00
int TextEdit : : get_max_chars ( ) const {
return max_chars ;
}
2016-05-09 20:21:55 +02: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 14:45:50 +01:00
if ( is_visible_in_tree ( ) & & has_focus ( ) & & window_has_focus ) {
2016-06-08 15:02:39 +02:00
update ( ) ;
}
2016-05-09 20:21:55 +02:00
}
2014-02-10 02:10:30 +01:00
void TextEdit : : _update_caches ( ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
cache . style_normal = get_stylebox ( " normal " ) ;
cache . style_focus = get_stylebox ( " focus " ) ;
2017-11-30 04:53:15 +01:00
cache . style_readonly = get_stylebox ( " read_only " ) ;
2017-03-05 16:44:50 +01: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 18:50:21 +02:00
cache . safe_line_number_color = get_color ( " safe_line_number_color " ) ;
2017-03-05 16:44:50 +01:00
cache . font_color = get_color ( " font_color " ) ;
cache . font_selected_color = get_color ( " font_selected_color " ) ;
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 " ) ;
cache . breakpoint_color = get_color ( " breakpoint_color " ) ;
2019-04-22 18:20:27 +02:00
cache . executing_line_color = get_color ( " executing_line_color " ) ;
2017-12-04 22:48:20 +01:00
cache . code_folding_color = get_color ( " code_folding_color " ) ;
2017-03-05 16:44:50 +01: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 23:50:16 +02:00
# ifdef TOOLS_ENABLED
cache . line_spacing = get_constant ( " line_spacing " ) * EDSCALE ;
# else
2017-03-05 16:44:50 +01:00
cache . line_spacing = get_constant ( " line_spacing " ) ;
2018-07-26 23:50:16 +02:00
# endif
2015-01-02 19:08:40 +01:00
cache . row_height = cache . font - > get_height ( ) + cache . line_spacing ;
2017-03-05 16:44:50 +01:00
cache . tab_icon = get_icon ( " tab " ) ;
2019-04-24 09:59:17 +02:00
cache . folded_icon = get_icon ( " folded " ) ;
cache . can_fold_icon = get_icon ( " fold " ) ;
2017-11-20 20:03:22 +01:00
cache . folded_eol_icon = get_icon ( " GuiEllipsis " , " EditorIcons " ) ;
2019-04-22 18:20:27 +02:00
cache . executing_icon = get_icon ( " MainPlay " , " EditorIcons " ) ;
2015-01-02 19:08:40 +01:00
text . set_font ( cache . font ) ;
2018-04-02 13:41:44 +02: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 ( ) ;
}
update ( ) ;
}
2018-04-07 15:14:19 +02:00
int TextEdit : : _is_line_in_region ( int p_line ) {
// do we have in cache?
if ( color_region_cache . has ( p_line ) ) {
return color_region_cache [ p_line ] ;
}
// if not find the closest line we have
int previous_line = p_line - 1 ;
2018-09-27 12:07:59 +02:00
for ( ; previous_line > - 1 ; previous_line - - ) {
2018-04-07 15:14:19 +02:00
if ( color_region_cache . has ( p_line ) ) {
break ;
}
2018-04-02 13:41:44 +02:00
}
2018-04-07 15:14:19 +02:00
// calculate up to line we need and update the cache along the way.
int in_region = color_region_cache [ previous_line ] ;
2018-04-10 21:42:28 +02:00
if ( previous_line = = - 1 ) {
in_region = - 1 ;
}
2018-04-07 15:14:19 +02: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 13:41:44 +02:00
}
TextEdit : : ColorRegion TextEdit : : _get_color_region ( int p_region ) const {
2018-04-07 15:14:19 +02:00
if ( p_region < 0 | | p_region > = color_regions . size ( ) ) {
2018-04-02 13:41:44 +02: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 02:10:30 +01:00
}
void TextEdit : : clear_colors ( ) {
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
keywords . clear ( ) ;
2016-11-06 14:50:23 +01:00
color_regions . clear ( ) ;
2018-04-07 15:14:19 +02:00
color_region_cache . clear ( ) ;
2018-01-26 02:41:17 +01:00
text . clear_width_cache ( ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
void TextEdit : : add_keyword_color ( const String & p_keyword , const Color & p_color ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
keywords [ p_keyword ] = p_color ;
2015-01-02 19:08:40 +01:00
update ( ) ;
2014-02-10 02:10:30 +01:00
}
2018-04-02 13:41:44 +02:00
bool TextEdit : : has_keyword_color ( String p_keyword ) const {
return keywords . has ( p_keyword ) ;
}
Color TextEdit : : get_keyword_color ( String p_keyword ) const {
return keywords [ p_keyword ] ;
}
2017-03-05 16:44:50 +01: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-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
color_regions . push_back ( ColorRegion ( p_begin_key , p_end_key , p_color , p_line_only ) ) ;
2018-01-26 02:41:17 +01:00
text . clear_width_cache ( ) ;
2015-01-02 19:08:40 +01:00
update ( ) ;
2014-02-10 02:10:30 +01:00
}
2018-01-12 15:00:41 +01:00
void TextEdit : : add_member_keyword ( const String & p_keyword , const Color & p_color ) {
member_keywords [ p_keyword ] = p_color ;
update ( ) ;
}
2018-04-02 13:41:44 +02: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 15:00:41 +01:00
void TextEdit : : clear_member_keywords ( ) {
member_keywords . clear ( ) ;
update ( ) ;
}
2014-02-10 02:10:30 +01:00
void TextEdit : : set_syntax_coloring ( bool p_enabled ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
syntax_coloring = p_enabled ;
2015-01-02 19:08:40 +01:00
update ( ) ;
2014-02-10 02:10:30 +01:00
}
bool TextEdit : : is_syntax_coloring_enabled ( ) const {
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
return syntax_coloring ;
2014-02-10 02:10:30 +01:00
}
2015-11-29 17:02:35 +01:00
void TextEdit : : set_auto_indent ( bool p_auto_indent ) {
auto_indent = p_auto_indent ;
}
2014-02-10 02:10:30 +01:00
void TextEdit : : cut ( ) {
2016-03-09 00:00:52 +01:00
2016-03-15 13:02:38 +01:00
if ( ! selection . active ) {
2016-03-09 00:00:52 +01:00
2016-03-15 13:02:38 +01:00
String clipboard = text [ cursor . line ] ;
OS : : get_singleton ( ) - > set_clipboard ( clipboard ) ;
cursor_set_line ( cursor . line ) ;
cursor_set_column ( 0 ) ;
2017-03-05 16:44:50 +01:00
_remove_text ( cursor . line , 0 , cursor . line , text [ cursor . line ] . length ( ) ) ;
2016-03-09 00:00:52 +01:00
2016-03-15 13:02:38 +01:00
backspace_at_cursor ( ) ;
update ( ) ;
2017-03-05 16:44:50 +01:00
cursor_set_line ( cursor . line + 1 ) ;
2017-12-17 16:24:23 +01:00
cut_copy_line = clipboard ;
2016-03-09 00:00:52 +01:00
2016-03-15 13:02:38 +01:00
} else {
2017-03-05 16:44:50 +01:00
String clipboard = _base_get_text ( selection . from_line , selection . from_column , selection . to_line , selection . to_column ) ;
2016-03-15 13:02:38 +01:00
OS : : get_singleton ( ) - > set_clipboard ( clipboard ) ;
2017-08-27 19:07:28 +02:00
_remove_text ( selection . from_line , selection . from_column , selection . to_line , selection . to_column ) ;
cursor_set_line ( selection . from_line ) ; // set afterwards else it causes the view to be offset
2016-03-15 13:02:38 +01:00
cursor_set_column ( selection . from_column ) ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
selection . active = false ;
selection . selecting_mode = Selection : : MODE_NONE ;
2016-03-15 13:02:38 +01:00
update ( ) ;
2017-12-17 16:24:23 +01:00
cut_copy_line = " " ;
2016-03-15 13:02:38 +01:00
}
2014-02-10 02:10:30 +01:00
}
void TextEdit : : copy ( ) {
2016-03-09 00:00:52 +01:00
2016-03-15 13:02:38 +01:00
if ( ! selection . active ) {
2018-09-30 17:17:29 +02: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 13:02:38 +01:00
} else {
2017-03-05 16:44:50 +01:00
String clipboard = _base_get_text ( selection . from_line , selection . from_column , selection . to_line , selection . to_column ) ;
2016-03-15 13:02:38 +01:00
OS : : get_singleton ( ) - > set_clipboard ( clipboard ) ;
2017-12-17 16:24:23 +01:00
cut_copy_line = " " ;
2016-03-15 13:02:38 +01:00
}
2014-02-10 02:10:30 +01:00
}
2016-03-15 13:02:38 +01:00
2014-02-10 02:10:30 +01:00
void TextEdit : : paste ( ) {
2016-03-09 00:00:52 +01:00
2016-03-15 13:02:38 +01:00
String clipboard = OS : : get_singleton ( ) - > get_clipboard ( ) ;
2018-05-09 14:07:06 +02:00
begin_complex_operation ( ) ;
2015-01-02 19:08:40 +01:00
if ( selection . active ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01: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 19:08:40 +01:00
cursor_set_line ( selection . from_line ) ;
cursor_set_column ( selection . from_column ) ;
2016-03-09 00:00:52 +01:00
2017-12-17 16:24:23 +01:00
} else if ( ! cut_copy_line . empty ( ) & & cut_copy_line = = clipboard ) {
2016-03-09 00:00:52 +01:00
2016-03-15 13:02:38 +01:00
cursor_set_column ( 0 ) ;
2017-03-05 16:44:50 +01:00
String ins = " \n " ;
2016-03-15 13:02:38 +01:00
clipboard + = ins ;
2015-01-02 19:08:40 +01:00
}
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
_insert_text_at_cursor ( clipboard ) ;
2018-05-09 14:07:06 +02:00
end_complex_operation ( ) ;
2015-01-02 19:08:40 +01:00
update ( ) ;
2014-02-10 02:10:30 +01:00
}
void TextEdit : : select_all ( ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( text . size ( ) = = 1 & & text [ 0 ] . length ( ) = = 0 )
2015-01-02 19:08:40 +01:00
return ;
2017-03-05 16:44:50 +01: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 19:08:40 +01:00
update ( ) ;
2014-02-10 02:10:30 +01:00
}
void TextEdit : : deselect ( ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
selection . active = false ;
2015-01-02 19:08:40 +01:00
update ( ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
void TextEdit : : select ( int p_from_line , int p_from_column , int p_to_line , int p_to_column ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( p_from_line > = text . size ( ) )
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 21:13:17 +01:00
if ( p_from_column < 0 )
p_from_column = 0 ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( p_to_line > = text . size ( ) )
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 21:13:17 +01:00
if ( p_to_column < 0 )
p_to_column = 0 ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01: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-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
selection . active = true ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( selection . from_line = = selection . to_line ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( selection . from_column = = selection . to_column ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
selection . active = false ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
} else if ( selection . from_column > selection . to_column ) {
2016-03-09 00:00:52 +01:00
2015-08-13 00:34:07 +02:00
selection . shiftclick_left = false ;
2017-03-05 16:44:50 +01:00
SWAP ( selection . from_column , selection . to_column ) ;
2015-08-13 00:34:07 +02:00
} else {
selection . shiftclick_left = true ;
2015-01-02 19:08:40 +01:00
}
2017-03-05 16:44:50 +01:00
} else if ( selection . from_line > selection . to_line ) {
2016-03-09 00:00:52 +01:00
2015-08-13 00:34:07 +02:00
selection . shiftclick_left = false ;
2017-03-05 16:44:50 +01:00
SWAP ( selection . from_line , selection . to_line ) ;
SWAP ( selection . from_column , selection . to_column ) ;
2015-08-13 00:34:07 +02:00
} else {
selection . shiftclick_left = true ;
2015-01-02 19:08:40 +01:00
}
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
update ( ) ;
2014-02-10 02:10:30 +01:00
}
2017-11-11 05:07:41 +01: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 02:10:30 +01:00
bool TextEdit : : is_selection_active ( ) const {
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
return selection . active ;
2014-02-10 02:10:30 +01:00
}
int TextEdit : : get_selection_from_line ( ) const {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
ERR_FAIL_COND_V ( ! selection . active , - 1 ) ;
2015-01-02 19:08:40 +01:00
return selection . from_line ;
2014-02-10 02:10:30 +01:00
}
int TextEdit : : get_selection_from_column ( ) const {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
ERR_FAIL_COND_V ( ! selection . active , - 1 ) ;
2015-01-02 19:08:40 +01:00
return selection . from_column ;
2014-02-10 02:10:30 +01:00
}
int TextEdit : : get_selection_to_line ( ) const {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
ERR_FAIL_COND_V ( ! selection . active , - 1 ) ;
2015-01-02 19:08:40 +01:00
return selection . to_line ;
2014-02-10 02:10:30 +01:00
}
int TextEdit : : get_selection_to_column ( ) const {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
ERR_FAIL_COND_V ( ! selection . active , - 1 ) ;
2015-01-02 19:08:40 +01:00
return selection . to_column ;
2014-02-10 02:10:30 +01:00
}
String TextEdit : : get_selection_text ( ) const {
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
if ( ! selection . active )
return " " ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
return _base_get_text ( selection . from_line , selection . from_column , selection . to_line , selection . to_column ) ;
2014-02-10 02:10:30 +01:00
}
2014-05-06 11:36:39 +02:00
String TextEdit : : get_word_under_cursor ( ) const {
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
int prev_cc = cursor . column ;
2017-03-05 16:44:50 +01:00
while ( prev_cc > 0 ) {
bool is_char = _is_text_char ( text [ cursor . line ] [ prev_cc - 1 ] ) ;
2015-01-02 19:08:40 +01:00
if ( ! is_char )
break ;
- - prev_cc ;
}
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
int next_cc = cursor . column ;
2017-03-05 16:44:50 +01:00
while ( next_cc < text [ cursor . line ] . length ( ) ) {
2015-01-02 19:08:40 +01:00
bool is_char = _is_text_char ( text [ cursor . line ] [ next_cc ] ) ;
2017-03-05 16:44:50 +01:00
if ( ! is_char )
2015-01-02 19:08:40 +01:00
break ;
2017-03-05 16:44:50 +01:00
+ + next_cc ;
2015-01-02 19:08:40 +01:00
}
if ( prev_cc = = cursor . column | | next_cc = = cursor . column )
return " " ;
2017-03-05 16:44:50 +01:00
return text [ cursor . line ] . substr ( prev_cc , next_cc - prev_cc ) ;
2014-05-06 11:36:39 +02:00
}
2014-02-10 02:10:30 +01:00
2016-05-28 18:25:45 +02: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 18:15:41 +02:00
update ( ) ;
2016-05-28 18:25:45 +02:00
}
2016-03-16 22:20:42 +01:00
void TextEdit : : set_highlight_all_occurrences ( const bool p_enabled ) {
highlight_all_occurrences = p_enabled ;
update ( ) ;
}
2016-07-11 16:20:01 +02:00
bool TextEdit : : is_highlight_all_occurrences_enabled ( ) const {
return highlight_all_occurrences ;
}
2016-05-28 18:25:45 +02: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 22:20:42 +01: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 18:25:45 +02:00
while ( col = = - 1 & & p_from_column < = p_search . length ( ) ) {
2017-03-05 16:44:50 +01:00
if ( p_search_flags & SEARCH_MATCH_CASE ) {
col = p_search . find ( p_key , p_from_column ) ;
2016-05-28 18:25:45 +02:00
} else {
2017-03-05 16:44:50 +01:00
col = p_search . findn ( p_key , p_from_column ) ;
2016-05-28 18:25:45 +02:00
}
2016-03-17 21:35:04 +01:00
// whole words only
2017-03-05 16:44:50 +01:00
if ( col ! = - 1 & & p_search_flags & SEARCH_WHOLE_WORDS ) {
p_from_column = col ;
2016-03-17 21:35:04 +01:00
2017-03-05 16:44:50 +01:00
if ( col > 0 & & _is_text_char ( p_search [ col - 1 ] ) ) {
2016-03-17 21:35:04 +01:00
col = - 1 ;
2017-03-05 16:44:50 +01:00
} else if ( ( col + p_key . length ( ) ) < p_search . length ( ) & & _is_text_char ( p_search [ col + p_key . length ( ) ] ) ) {
2016-03-17 21:35:04 +01:00
col = - 1 ;
}
2016-03-16 22:20:42 +01:00
}
2016-03-17 21:35:04 +01:00
2017-03-05 16:44:50 +01:00
p_from_column + = 1 ;
2016-03-16 22:20:42 +01:00
}
}
return col ;
}
2017-03-05 16:44:50 +01: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-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
int col , line ;
if ( search ( p_key , p_search_flags , p_from_line , p_from_column , col , line ) ) {
2017-01-07 22:25:37 +01:00
PoolVector < int > result ;
2015-01-02 19:08:40 +01:00
result . resize ( 2 ) ;
2017-03-05 16:44:50 +01:00
result . set ( 0 , line ) ;
result . set ( 1 , col ) ;
2015-01-02 19:08:40 +01:00
return result ;
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
} else {
2016-03-09 00:00:52 +01:00
2017-01-07 22:25:37 +01:00
return PoolVector < int > ( ) ;
2015-01-02 19:08:40 +01:00
}
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01: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-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( p_key . length ( ) = = 0 )
2015-01-02 19:08:40 +01:00
return false ;
2017-03-05 16:44:50 +01: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-09 00:00:52 +01:00
2018-02-21 17:30:55 +01:00
//search through the whole document, but start by current line
2016-03-09 00:00:52 +01:00
2017-08-21 21:15:36 +02:00
int line = p_from_line ;
2017-03-05 16:44:50 +01:00
int pos = - 1 ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < text . size ( ) + 1 ; i + + ) {
2015-01-02 19:08:40 +01:00
//backwards is broken...
//int idx=(p_search_flags&SEARCH_BACKWARDS)?(text.size()-i):i; //do backwards seearch
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( line < 0 ) {
line = text . size ( ) - 1 ;
2015-01-02 19:08:40 +01:00
}
2017-03-05 16:44:50 +01:00
if ( line = = text . size ( ) ) {
line = 0 ;
2015-01-02 19:08:40 +01:00
}
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
String text_line = text [ line ] ;
2017-03-05 16:44:50 +01:00
int from_column = 0 ;
if ( line = = p_from_line ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( i = = text . size ( ) ) {
2015-01-02 19:08:40 +01:00
//wrapped
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( p_search_flags & SEARCH_BACKWARDS ) {
from_column = text_line . length ( ) ;
2015-01-02 19:08:40 +01:00
} else {
2017-03-05 16:44:50 +01:00
from_column = 0 ;
2015-01-02 19:08:40 +01:00
}
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
} else {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
from_column = p_from_column ;
2015-01-02 19:08:40 +01:00
}
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
} else {
2017-03-05 16:44:50 +01:00
if ( p_search_flags & SEARCH_BACKWARDS )
from_column = text_line . length ( ) - 1 ;
2015-01-02 19:08:40 +01:00
else
2017-03-05 16:44:50 +01:00
from_column = 0 ;
2015-01-02 19:08:40 +01:00
}
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
pos = - 1 ;
2016-03-09 00:00:52 +01:00
2018-08-27 19:01:12 +02:00
int pos_from = ( p_search_flags & SEARCH_BACKWARDS ) ? text_line . length ( ) : 0 ;
2017-03-05 16:44:50 +01:00
int last_pos = - 1 ;
2016-03-09 00:00:52 +01:00
2018-01-08 22:50:33 +01:00
while ( true ) {
2016-05-28 18:23:49 +02:00
2018-08-27 19:01:12 +02: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 22:50:33 +01:00
break ;
2018-08-27 19:01:12 +02: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 22:50:33 +01:00
if ( last_pos > = from_column ) {
pos = last_pos ;
break ;
}
2018-08-27 19:01:12 +02:00
pos_from = last_pos + p_key . length ( ) ;
2016-05-28 18:23:49 +02:00
}
}
2016-03-09 00:00:52 +01:00
2018-01-08 22:50:33 +01:00
bool is_match = true ;
if ( pos ! = - 1 & & ( p_search_flags & SEARCH_WHOLE_WORDS ) ) {
//validate for whole words
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 19:01:12 +02:00
if ( pos_from = = - 1 ) {
pos = - 1 ;
}
2018-01-08 22:50:33 +01:00
if ( is_match | | last_pos = = - 1 | | pos = = - 1 ) {
break ;
}
2016-03-09 00:00:52 +01:00
2018-08-27 19:01:12 +02:00
pos_from = ( p_search_flags & SEARCH_BACKWARDS ) ? pos - 1 : pos + 1 ;
2018-01-08 22:50:33 +01:00
pos = - 1 ;
2015-01-02 19:08:40 +01:00
}
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( pos ! = - 1 )
2015-01-02 19:08:40 +01:00
break ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( p_search_flags & SEARCH_BACKWARDS )
2015-01-02 19:08:40 +01:00
line - - ;
else
line + + ;
}
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( pos = = - 1 ) {
r_line = - 1 ;
r_column = - 1 ;
2015-01-02 19:08:40 +01:00
return false ;
}
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
r_line = line ;
r_column = pos ;
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
return true ;
2014-02-10 02:10:30 +01:00
}
void TextEdit : : _cursor_changed_emit ( ) {
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
emit_signal ( " cursor_changed " ) ;
2017-03-05 16:44:50 +01:00
cursor_changed_dirty = false ;
2014-02-10 02:10:30 +01:00
}
void TextEdit : : _text_changed_emit ( ) {
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
emit_signal ( " text_changed " ) ;
2017-03-05 16:44:50 +01:00
text_changed_dirty = false ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
void TextEdit : : set_line_as_marked ( int p_line , bool p_marked ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
ERR_FAIL_INDEX ( p_line , text . size ( ) ) ;
text . set_marked ( p_line , p_marked ) ;
2015-01-02 19:08:40 +01:00
update ( ) ;
2014-02-10 02:10:30 +01:00
}
2018-06-05 18:50:21 +02: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 18:20:27 +02: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 ( ) ;
}
2014-02-10 02:10:30 +01:00
bool TextEdit : : is_line_set_as_breakpoint ( int p_line ) const {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
ERR_FAIL_INDEX_V ( p_line , text . size ( ) , false ) ;
2015-01-02 19:08:40 +01:00
return text . is_breakpoint ( p_line ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
void TextEdit : : set_line_as_breakpoint ( int p_line , bool p_breakpoint ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
ERR_FAIL_INDEX ( p_line , text . size ( ) ) ;
text . set_breakpoint ( p_line , p_breakpoint ) ;
2015-01-02 19:08:40 +01:00
update ( ) ;
2014-02-10 02:10:30 +01:00
}
void TextEdit : : get_breakpoints ( List < int > * p_breakpoints ) const {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < text . size ( ) ; i + + ) {
2015-01-02 19:08:40 +01:00
if ( text . is_breakpoint ( i ) )
p_breakpoints - > push_back ( i ) ;
}
2014-02-10 02:10:30 +01:00
}
2018-05-30 14:02:51 +02: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 13:51:25 +02: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-13 00:12:17 +01: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 05:00:27 +01:00
void TextEdit : : fold_all_lines ( ) {
for ( int i = 0 ; i < text . size ( ) ; i + + ) {
fold_line ( i ) ;
}
_update_scrollbars ( ) ;
update ( ) ;
}
2017-11-13 00:12:17 +01:00
void TextEdit : : unhide_all_lines ( ) {
for ( int i = 0 ; i < text . size ( ) ; i + + ) {
text . set_hidden ( i , false ) ;
}
2017-11-16 05:00:27 +01:00
_update_scrollbars ( ) ;
2017-11-13 00:12:17 +01:00
update ( ) ;
}
2018-01-26 02:41:17 +01:00
int TextEdit : : num_lines_from ( int p_line_from , int visible_amount ) const {
2017-11-13 00:12:17 +01:00
2018-01-26 02:41:17 +01:00
// returns the number of lines (hidden and unhidden) from p_line_from to (p_line_from + visible_amount of unhidden lines)
ERR_FAIL_INDEX_V ( p_line_from , text . size ( ) , ABS ( visible_amount ) ) ;
2017-11-13 00:12:17 +01:00
if ( ! is_hiding_enabled ( ) )
2018-01-26 02:41:17 +01:00
return ABS ( visible_amount ) ;
2017-11-13 00:12:17 +01:00
int num_visible = 0 ;
int num_total = 0 ;
2018-01-26 02:41:17 +01:00
if ( visible_amount > = 0 ) {
2017-11-13 00:12:17 +01:00
for ( int i = p_line_from ; i < text . size ( ) ; i + + ) {
num_total + + ;
2018-01-26 02:41:17 +01:00
if ( ! is_line_hidden ( i ) ) {
2017-11-13 00:12:17 +01:00
num_visible + + ;
2018-01-26 02:41:17 +01:00
}
if ( num_visible > = visible_amount )
2017-11-13 00:12:17 +01:00
break ;
}
} else {
2018-01-26 02:41:17 +01:00
visible_amount = ABS ( visible_amount ) ;
2017-11-13 00:12:17 +01:00
for ( int i = p_line_from ; i > = 0 ; i - - ) {
num_total + + ;
2018-01-26 02:41:17 +01:00
if ( ! is_line_hidden ( i ) ) {
2017-11-13 00:12:17 +01:00
num_visible + + ;
2018-01-26 02:41:17 +01:00
}
if ( num_visible > = visible_amount )
2017-11-13 00:12:17 +01:00
break ;
}
}
return num_total ;
}
2018-01-26 02:41:17 +01: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 07:53:16 +01:00
2018-01-26 02:41:17 +01: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
wrap_index = 0 ;
ERR_FAIL_INDEX_V ( p_line_from , text . size ( ) , ABS ( visible_amount ) ) ;
2017-12-26 07:53:16 +01:00
2018-01-26 02:41:17 +01: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 07:53:16 +01:00
2018-01-26 02:41:17 +01:00
// returns the last line in the text that is not hidden
2017-12-26 07:53:16 +01:00
if ( ! is_hiding_enabled ( ) )
2018-01-26 02:41:17 +01:00
return text . size ( ) - 1 ;
2017-12-26 07:53:16 +01:00
2018-01-26 02:41:17 +01: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 07:53:16 +01:00
}
2018-01-26 02:41:17 +01:00
return last_line ;
2017-12-26 07:53:16 +01:00
}
2017-11-28 12:03:46 +01:00
int TextEdit : : get_indent_level ( int p_line ) const {
2017-11-13 00:12:17 +01:00
ERR_FAIL_INDEX_V ( p_line , text . size ( ) , 0 ) ;
// counts number of tabs and spaces before line starts
2017-11-28 12:03:46 +01:00
int tab_count = 0 ;
2017-11-13 00:12:17 +01: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 12:03:46 +01:00
tab_count + + ;
2017-11-13 00:12:17 +01:00
} else if ( text [ p_line ] [ i ] = = ' ' ) {
whitespace_count + + ;
} else {
break ;
}
}
2018-01-26 02:41:17 +01:00
return tab_count * indent_size + whitespace_count ;
2017-11-13 00:12:17 +01:00
}
2018-01-12 06:25:04 +01:00
bool TextEdit : : is_line_comment ( int p_line ) const {
// checks to see if this line is the start of a comment
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 ] ;
if ( color_regions [ cri . region ] . begin_key = = " # " | | color_regions [ cri . region ] . begin_key = = " // " ) {
return true ;
} else {
return false ;
}
} else if ( _is_whitespace ( text [ p_line ] [ i ] ) ) {
continue ;
} else {
break ;
}
}
return false ;
}
2017-11-13 00:12:17 +01: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 05:00:27 +01:00
if ( p_line + 1 > = text . size ( ) )
2017-11-13 00:12:17 +01:00
return false ;
2018-09-29 19:08:56 +02:00
if ( text [ p_line ] . strip_edges ( ) . size ( ) = = 0 )
2017-11-13 00:12:17 +01:00
return false ;
if ( is_folded ( p_line ) )
return false ;
2017-11-16 05:00:27 +01:00
if ( is_line_hidden ( p_line ) )
return false ;
2018-01-12 06:25:04 +01:00
if ( is_line_comment ( p_line ) )
return false ;
2017-11-13 00:12:17 +01:00
2017-11-28 12:03:46 +01:00
int start_indent = get_indent_level ( p_line ) ;
2017-11-13 00:12:17 +01:00
for ( int i = p_line + 1 ; i < text . size ( ) ; i + + ) {
2018-09-29 19:08:56 +02:00
if ( text [ i ] . strip_edges ( ) . size ( ) = = 0 )
2017-11-13 00:12:17 +01:00
continue ;
2017-11-28 12:03:46 +01:00
int next_indent = get_indent_level ( i ) ;
2018-01-12 06:25:04 +01:00
if ( is_line_comment ( i ) ) {
continue ;
} else if ( next_indent > start_indent ) {
2017-11-13 00:12:17 +01:00
return true ;
2018-01-12 06:25:04 +01:00
} else {
2017-11-13 00:12:17 +01:00
return false ;
2018-01-12 06:25:04 +01:00
}
2017-11-13 00:12:17 +01:00
}
return false ;
}
bool TextEdit : : is_folded ( int p_line ) const {
ERR_FAIL_INDEX_V ( p_line , text . size ( ) , false ) ;
2017-11-23 21:37:08 +01:00
if ( p_line + 1 > = text . size ( ) )
2017-11-13 00:12:17 +01:00
return false ;
if ( ! is_line_hidden ( p_line ) & & is_line_hidden ( p_line + 1 ) )
return true ;
return false ;
}
2019-04-13 13:43:35 +02: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-13 00:12:17 +01: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 ;
2017-11-16 05:00:27 +01:00
// hide lines below this one
2017-11-28 12:03:46 +01:00
int start_indent = get_indent_level ( p_line ) ;
int last_line = start_indent ;
2017-11-13 00:12:17 +01:00
for ( int i = p_line + 1 ; i < text . size ( ) ; i + + ) {
2017-11-28 12:03:46 +01:00
if ( text [ i ] . strip_edges ( ) . size ( ) ! = 0 ) {
2018-01-12 06:25:04 +01:00
if ( is_line_comment ( i ) ) {
continue ;
} else if ( get_indent_level ( i ) > start_indent ) {
2017-11-28 12:03:46 +01:00
last_line = i ;
} else {
break ;
2017-11-13 00:12:17 +01:00
}
}
}
2017-11-28 12:03:46 +01:00
for ( int i = p_line + 1 ; i < = last_line ; i + + ) {
set_line_as_hidden ( i , true ) ;
}
2017-11-16 05:00:27 +01:00
// fix selection
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 ) ;
}
}
// reset cursor
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-13 00:12:17 +01:00
update ( ) ;
}
void TextEdit : : unfold_line ( int p_line ) {
ERR_FAIL_INDEX ( p_line , text . size ( ) ) ;
2017-11-16 05:00:27 +01:00
if ( ! is_folded ( p_line ) & & ! is_line_hidden ( p_line ) )
return ;
2017-11-13 00:12:17 +01: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 05:00:27 +01:00
_update_scrollbars ( ) ;
2017-11-13 00:12:17 +01:00
update ( ) ;
}
2017-12-08 19:17:10 +01: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 02:10:30 +01:00
int TextEdit : : get_line_count ( ) const {
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
return text . size ( ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
void TextEdit : : _do_text_op ( const TextOperation & p_op , bool p_reverse ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
ERR_FAIL_COND ( p_op . type = = TextOperation : : TYPE_NONE ) ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
bool insert = p_op . type = = TextOperation : : TYPE_INSERT ;
2015-01-02 19:08:40 +01:00
if ( p_reverse )
2017-03-05 16:44:50 +01:00
insert = ! insert ;
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
if ( insert ) {
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
int check_line ;
int check_column ;
2017-03-05 16:44:50 +01:00
_base_insert_text ( p_op . from_line , p_op . from_column , p_op . text , check_line , check_column ) ;
ERR_FAIL_COND ( check_line ! = p_op . to_line ) ; // BUG
ERR_FAIL_COND ( check_column ! = p_op . to_column ) ; // BUG
2015-01-02 19:08:40 +01:00
} else {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
_base_remove_text ( p_op . from_line , p_op . from_column , p_op . to_line , p_op . to_column ) ;
2015-01-02 19:08:40 +01:00
}
2014-02-10 02:10:30 +01:00
}
void TextEdit : : _clear_redo ( ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( undo_stack_pos = = NULL )
2015-01-02 19:08:40 +01:00
return ; //nothing to clear
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
_push_current_op ( ) ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
while ( undo_stack_pos ) {
2015-01-02 19:08:40 +01:00
List < TextOperation > : : Element * elem = undo_stack_pos ;
2017-03-05 16:44:50 +01:00
undo_stack_pos = undo_stack_pos - > next ( ) ;
2015-01-02 19:08:40 +01:00
undo_stack . erase ( elem ) ;
}
2014-02-10 02:10:30 +01:00
}
void TextEdit : : undo ( ) {
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
_push_current_op ( ) ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( undo_stack_pos = = NULL ) {
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
if ( ! undo_stack . size ( ) )
return ; //nothing to undo
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
undo_stack_pos = undo_stack . back ( ) ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
} else if ( undo_stack_pos = = undo_stack . front ( ) )
2015-01-02 19:08:40 +01:00
return ; // at the bottom of the undo stack
else
2017-03-05 16:44:50 +01:00
undo_stack_pos = undo_stack_pos - > prev ( ) ;
2016-03-09 00:00:52 +01:00
2018-01-13 11:38:01 +01:00
deselect ( ) ;
2016-03-13 21:08:12 +01:00
TextOperation op = undo_stack_pos - > get ( ) ;
_do_text_op ( op , true ) ;
2019-04-21 20:19:31 +02:00
if ( op . from_line ! = op . to_line | | op . to_column ! = op . from_column + 1 )
select ( op . from_line , op . from_column , op . to_line , op . to_column ) ;
2017-03-05 16:44:50 +01:00
current_op . version = op . prev_version ;
if ( undo_stack_pos - > get ( ) . chain_backward ) {
while ( true ) {
2016-06-14 13:48:34 +02:00
ERR_BREAK ( ! undo_stack_pos - > prev ( ) ) ;
2015-01-02 19:08:40 +01:00
undo_stack_pos = undo_stack_pos - > prev ( ) ;
2016-03-13 21:08:12 +01:00
op = undo_stack_pos - > get ( ) ;
_do_text_op ( op , true ) ;
current_op . version = op . prev_version ;
2016-06-14 13:48:34 +02:00
if ( undo_stack_pos - > get ( ) . chain_forward ) {
break ;
}
}
2015-01-02 19:08:40 +01:00
}
2016-03-09 00:00:52 +01:00
2016-06-13 21:40:28 +02: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 21:55:26 +02:00
_cancel_code_hint ( ) ;
2016-06-13 21:40:28 +02:00
} else {
cursor_set_line ( undo_stack_pos - > get ( ) . from_line ) ;
cursor_set_column ( undo_stack_pos - > get ( ) . from_column ) ;
}
2015-01-02 19:08:40 +01:00
update ( ) ;
2014-02-10 02:10:30 +01:00
}
void TextEdit : : redo ( ) {
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
_push_current_op ( ) ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( undo_stack_pos = = NULL )
2015-01-02 19:08:40 +01:00
return ; //nothing to do.
2016-03-09 00:00:52 +01:00
2018-01-13 11:38:01 +01:00
deselect ( ) ;
2016-03-13 21:08:12 +01:00
TextOperation op = undo_stack_pos - > get ( ) ;
_do_text_op ( op , false ) ;
current_op . version = op . version ;
2017-03-05 16:44:50 +01:00
if ( undo_stack_pos - > get ( ) . chain_forward ) {
2016-06-14 13:48:34 +02:00
2017-03-05 16:44:50 +01:00
while ( true ) {
2016-06-14 13:48:34 +02:00
ERR_BREAK ( ! undo_stack_pos - > next ( ) ) ;
2017-03-05 16:44:50 +01:00
undo_stack_pos = undo_stack_pos - > next ( ) ;
2016-03-13 21:08:12 +01:00
op = undo_stack_pos - > get ( ) ;
_do_text_op ( op , false ) ;
current_op . version = op . version ;
2016-06-14 13:48:34 +02:00
if ( undo_stack_pos - > get ( ) . chain_backward )
break ;
}
2015-01-02 19:08:40 +01:00
}
2016-03-13 21:08:12 +01:00
cursor_set_line ( undo_stack_pos - > get ( ) . to_line ) ;
cursor_set_column ( undo_stack_pos - > get ( ) . to_column ) ;
2017-03-05 16:44:50 +01:00
undo_stack_pos = undo_stack_pos - > next ( ) ;
2015-01-02 19:08:40 +01:00
update ( ) ;
2014-02-10 02:10:30 +01:00
}
void TextEdit : : clear_undo_history ( ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
saved_version = 0 ;
current_op . type = TextOperation : : TYPE_NONE ;
undo_stack_pos = NULL ;
2015-01-02 19:08:40 +01:00
undo_stack . clear ( ) ;
2014-02-10 02:10:30 +01:00
}
2016-04-06 08:36:29 +02:00
void TextEdit : : begin_complex_operation ( ) {
2015-01-02 19:08:40 +01:00
_push_current_op ( ) ;
2017-03-05 16:44:50 +01:00
next_operation_is_complex = true ;
2014-04-26 14:42:19 +02:00
}
2014-02-10 02:10:30 +01:00
2016-04-06 08:36:29 +02:00
void TextEdit : : end_complex_operation ( ) {
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
_push_current_op ( ) ;
ERR_FAIL_COND ( undo_stack . size ( ) = = 0 ) ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( undo_stack . back ( ) - > get ( ) . chain_forward ) {
undo_stack . back ( ) - > get ( ) . chain_forward = false ;
2015-01-02 19:08:40 +01:00
return ;
}
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
undo_stack . back ( ) - > get ( ) . chain_backward = true ;
2014-04-26 14:42:19 +02:00
}
2014-02-10 02:10:30 +01:00
2014-04-26 14:42:19 +02:00
void TextEdit : : _push_current_op ( ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( current_op . type = = TextOperation : : TYPE_NONE )
2015-01-02 19:08:40 +01:00
return ; // do nothing
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( next_operation_is_complex ) {
current_op . chain_forward = true ;
next_operation_is_complex = false ;
2015-01-02 19:08:40 +01:00
}
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
undo_stack . push_back ( current_op ) ;
2017-03-05 16:44:50 +01:00
current_op . type = TextOperation : : TYPE_NONE ;
current_op . text = " " ;
current_op . chain_forward = false ;
2014-02-10 02:10:30 +01:00
}
2017-04-17 15:24:30 +02: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 19:10:01 +01:00
ERR_FAIL_COND ( p_size < = 0 ) ;
2017-04-17 15:24:30 +02: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 19:10:01 +01:00
update ( ) ;
}
2018-05-03 19:03:20 +02:00
int TextEdit : : get_indent_size ( ) {
return indent_size ;
}
2014-02-10 02:10:30 +01:00
void TextEdit : : set_draw_tabs ( bool p_draw ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
draw_tabs = p_draw ;
2019-04-24 09:59:17 +02:00
update ( ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
bool TextEdit : : is_drawing_tabs ( ) const {
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
return draw_tabs ;
2014-02-10 02:10:30 +01:00
}
2017-09-27 19:24:05 +02: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 02:41:17 +01:00
2017-09-27 19:24:05 +02:00
bool TextEdit : : is_overriding_selected_font_color ( ) const {
return override_selected_font_color ;
}
2016-03-31 21:49:30 +02: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 03:02:51 +02:00
bool TextEdit : : is_insert_text_operation ( ) {
return ( current_op . type = = TextOperation : : TYPE_INSERT ) ;
}
2014-02-10 02:10:30 +01:00
uint32_t TextEdit : : get_version ( ) const {
2015-01-02 19:08:40 +01:00
return current_op . version ;
2014-02-10 02:10:30 +01:00
}
2018-01-26 02:41:17 +01:00
2014-02-10 02:10:30 +01:00
uint32_t TextEdit : : get_saved_version ( ) const {
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
return saved_version ;
2014-02-10 02:10:30 +01:00
}
2018-01-26 02:41:17 +01:00
2014-02-10 02:10:30 +01:00
void TextEdit : : tag_saved_version ( ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
saved_version = get_version ( ) ;
2014-02-10 02:10:30 +01:00
}
2018-01-26 02:41:17 +01:00
double TextEdit : : get_scroll_pos_for_line ( int p_line , int p_wrap_index ) const {
2016-03-09 00:00:52 +01:00
2018-01-26 02:41:17 +01:00
if ( ! is_wrap_enabled ( ) & & ! is_hiding_enabled ( ) )
return p_line ;
2016-03-09 00:00:52 +01:00
2018-01-26 02:41:17 +01:00
// count the number of visible lines up to this line
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 15:29:27 +02:00
}
}
2018-01-26 02:41:17 +01: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-26 01:36:34 +02:00
num_lines_from_rows ( first_vis_line , cursor . wrap_ofs , get_visible_rows ( ) + 1 , wi ) ;
2018-01-26 02:41:17 +01:00
return wi ;
}
double TextEdit : : get_visible_rows_offset ( ) const {
2018-08-20 13:36:34 +02:00
double total = get_size ( ) . height ;
2018-01-26 02:41:17 +01:00
total - = cache . style_normal - > get_minimum_size ( ) . height ;
if ( h_scroll - > is_visible_in_tree ( ) )
total - = h_scroll - > get_size ( ) . height ;
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 05:16:14 +01:00
v_scroll - > set_value ( p_scroll ) ;
2018-01-26 02:41:17 +01: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 02:10:30 +01:00
}
int TextEdit : : get_h_scroll ( ) const {
2016-03-09 00:00:52 +01:00
2017-01-04 05:16:14 +01:00
return h_scroll - > get_value ( ) ;
2014-02-10 02:10:30 +01:00
}
2018-01-26 02:41:17 +01:00
2014-02-10 02:10:30 +01:00
void TextEdit : : set_h_scroll ( int p_scroll ) {
2016-03-09 00:00:52 +01:00
2018-01-26 02:41:17 +01:00
if ( p_scroll < 0 ) {
p_scroll = 0 ;
}
2017-01-04 05:16:14 +01:00
h_scroll - > set_value ( p_scroll ) ;
2014-02-10 02:10:30 +01:00
}
2017-08-19 16:23:45 +02:00
void TextEdit : : set_smooth_scroll_enabled ( bool p_enable ) {
2018-01-26 02:41:17 +01:00
2017-08-19 16:23:45 +02:00
v_scroll - > set_smooth_scroll_enabled ( p_enable ) ;
smooth_scroll_enabled = p_enable ;
}
bool TextEdit : : is_smooth_scroll_enabled ( ) const {
2018-01-26 02:41:17 +01:00
2017-08-19 16:23:45 +02:00
return smooth_scroll_enabled ;
}
2017-08-21 20:13:24 +02:00
void TextEdit : : set_v_scroll_speed ( float p_speed ) {
2018-01-26 02:41:17 +01:00
2017-08-21 20:13:24 +02:00
v_scroll_speed = p_speed ;
}
float TextEdit : : get_v_scroll_speed ( ) const {
2018-01-26 02:41:17 +01:00
2017-08-21 20:13:24 +02:00
return v_scroll_speed ;
}
2017-03-05 16:44:50 +01:00
void TextEdit : : set_completion ( bool p_enabled , const Vector < String > & p_prefixes ) {
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
completion_prefixes . clear ( ) ;
2017-03-05 16:44:50 +01:00
completion_enabled = p_enabled ;
for ( int i = 0 ; i < p_prefixes . size ( ) ; i + + )
2015-01-02 19:08:40 +01:00
completion_prefixes . insert ( p_prefixes [ i ] ) ;
2014-02-10 02:10:30 +01:00
}
void TextEdit : : _confirm_completion ( ) {
2016-03-09 00:00:52 +01:00
2016-06-13 21:55:26 +02:00
begin_complex_operation ( ) ;
2016-03-09 00:00:52 +01:00
2016-06-13 21:55:26 +02:00
_remove_text ( cursor . line , cursor . column - completion_base . length ( ) , cursor . line , cursor . column ) ;
cursor_set_column ( cursor . column - completion_base . length ( ) , false ) ;
insert_text_at_cursor ( completion_current ) ;
2019-04-05 04:06:44 +02:00
// When inserted into the middle of an existing string/method, don't add an unnecessary quote/bracket.
2018-05-26 23:13:42 +02:00
String line = text [ cursor . line ] ;
CharType next_char = line [ cursor . column ] ;
CharType last_completion_char = completion_current [ completion_current . length ( ) - 1 ] ;
2019-04-05 04:06:44 +02:00
if ( ( last_completion_char = = ' " ' | | last_completion_char = = ' \' ' ) & & last_completion_char = = next_char ) {
2018-05-26 23:13:42 +02:00
_base_remove_text ( cursor . line , cursor . column , cursor . line , cursor . column + 1 ) ;
}
2019-04-05 04:06:44 +02: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 19:08:40 +01:00
}
2016-03-09 00:00:52 +01:00
2016-06-13 21:55:26 +02:00
end_complex_operation ( ) ;
2015-01-02 19:08:40 +01:00
_cancel_completion ( ) ;
2018-08-14 18:02:33 +02:00
if ( last_completion_char = = ' ( ' ) {
query_code_comple ( ) ;
}
2014-02-10 02:10:30 +01:00
}
2014-12-17 02:31:57 +01:00
void TextEdit : : _cancel_code_hint ( ) {
2016-10-05 10:19:08 +02:00
2017-03-05 16:44:50 +01:00
completion_hint = " " ;
2014-12-17 02:31:57 +01:00
update ( ) ;
}
2014-02-10 02:10:30 +01:00
void TextEdit : : _cancel_completion ( ) {
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
if ( ! completion_active )
return ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
completion_active = false ;
2017-08-24 05:06:56 +02:00
completion_forced = false ;
2015-01-02 19:08:40 +01:00
update ( ) ;
2014-02-10 02:10:30 +01:00
}
2014-12-17 02:31:57 +01:00
static bool _is_completable ( CharType c ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
return ! _is_symbol ( c ) | | c = = ' " ' | | c = = ' \' ' ;
2014-12-17 02:31:57 +01:00
}
2014-02-10 02:10:30 +01:00
void TextEdit : : _update_completion_candidates ( ) {
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
String l = text [ cursor . line ] ;
2017-03-05 16:44:50 +01:00
int cofs = CLAMP ( cursor . column , 0 , l . length ( ) ) ;
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
String s ;
2015-01-03 17:03:13 +01:00
//look for keywords first
2017-03-05 16:44:50 +01:00
bool inquote = false ;
int first_quote = - 1 ;
2019-02-24 18:18:09 +01:00
int restore_quotes = - 1 ;
2015-06-26 06:14:31 +02:00
2017-03-05 16:44:50 +01: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 18:18:09 +01: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 06:14:31 +02:00
}
c - - ;
}
2015-01-03 17:03:13 +01:00
2017-03-05 16:44:50 +01:00
bool pre_keyword = false ;
bool cancel = false ;
2015-06-26 06:14:31 +02:00
2017-03-05 16:44:50 +01:00
if ( ! inquote & & first_quote = = cofs - 1 ) {
2015-06-26 06:14:31 +02:00
//no completion here
2017-03-05 16:44:50 +01:00
cancel = true ;
} else if ( inquote & & first_quote ! = - 1 ) {
2015-06-26 06:14:31 +02:00
2017-03-05 16:44:50 +01:00
s = l . substr ( first_quote , cofs - first_quote ) ;
} else if ( cofs > 0 & & l [ cofs - 1 ] = = ' ' ) {
int kofs = cofs - 1 ;
2015-01-03 17:03:13 +01:00
String kw ;
2017-03-05 16:44:50 +01:00
while ( kofs > = 0 & & l [ kofs ] = = ' ' )
2015-01-03 17:03:13 +01:00
kofs - - ;
2017-03-05 16:44:50 +01:00
while ( kofs > = 0 & & l [ kofs ] > 32 & & _is_completable ( l [ kofs ] ) ) {
kw = String : : chr ( l [ kofs ] ) + kw ;
2015-01-03 17:03:13 +01:00
kofs - - ;
}
2017-03-05 16:44:50 +01:00
pre_keyword = keywords . has ( kw ) ;
2015-01-03 17:03:13 +01:00
} else {
2018-03-06 19:25:14 +01:00
while ( cofs > 0 & & l [ cofs - 1 ] > 32 & & ( l [ cofs - 1 ] = = ' / ' | | _is_completable ( l [ cofs - 1 ] ) ) ) {
2017-03-05 16:44:50 +01:00
s = String : : chr ( l [ cofs - 1 ] ) + s ;
if ( l [ cofs - 1 ] = = ' \' ' | | l [ cofs - 1 ] = = ' " ' | | l [ cofs - 1 ] = = ' $ ' )
2015-01-03 17:03:13 +01:00
break ;
cofs - - ;
}
2015-01-02 19:08:40 +01:00
}
2015-01-03 17:03:13 +01:00
2017-08-24 05:06:56 +02:00
if ( cursor . column > 0 & & l [ cursor . column - 1 ] = = ' ( ' & & ! pre_keyword & & ! completion_forced ) {
2016-05-07 13:01:56 +02:00
cancel = true ;
}
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
update ( ) ;
2016-03-09 00:00:52 +01:00
2017-08-24 05:06:56 +02:00
bool prev_is_prefix = false ;
if ( cofs > 0 & & completion_prefixes . has ( String : : chr ( l [ cofs - 1 ] ) ) )
prev_is_prefix = true ;
if ( cofs > 1 & & l [ cofs - 1 ] = = ' ' & & completion_prefixes . has ( String : : chr ( l [ cofs - 2 ] ) ) ) //check with one space before prefix, to allow indent
prev_is_prefix = true ;
if ( cancel | | ( ! pre_keyword & & s = = " " & & ( cofs = = 0 | | ! prev_is_prefix ) ) ) {
2015-01-02 19:08:40 +01:00
//none to complete, cancel
_cancel_completion ( ) ;
return ;
}
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
completion_options . clear ( ) ;
2017-03-05 16:44:50 +01:00
completion_index = 0 ;
completion_base = s ;
2016-06-13 21:55:26 +02:00
Vector < float > sim_cache ;
2017-10-28 23:46:20 +02:00
bool single_quote = s . begins_with ( " ' " ) ;
2018-11-26 16:36:46 +01:00
Vector < String > completion_options_casei ;
2017-10-28 23:46:20 +02:00
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < completion_strings . size ( ) ; i + + ) {
2017-10-28 23:46:20 +02:00
if ( single_quote & & completion_strings [ i ] . is_quoted ( ) ) {
2018-07-25 03:11:03 +02:00
completion_strings . write [ i ] = completion_strings [ i ] . unquote ( ) . quote ( " ' " ) ;
2017-10-28 23:46:20 +02:00
}
2019-02-24 18:18:09 +01:00
if ( inquote & & restore_quotes = = 1 & & ! completion_strings [ i ] . is_quoted ( ) ) {
String quote = single_quote ? " ' " : " \" " ;
completion_strings . write [ i ] = completion_strings [ i ] . quote ( quote ) ;
}
2018-08-14 18:02:33 +02:00
if ( completion_strings [ i ] . begins_with ( s ) ) {
completion_options . push_back ( completion_strings [ i ] ) ;
2018-11-26 16:36:46 +01:00
} else if ( completion_strings [ i ] . to_lower ( ) . begins_with ( s . to_lower ( ) ) ) {
completion_options_casei . push_back ( completion_strings [ i ] ) ;
2016-07-03 16:34:23 +02:00
}
2018-08-14 18:02:33 +02:00
}
2017-01-08 07:01:52 +01:00
2018-11-26 16:36:46 +01:00
completion_options . append_array ( completion_options_casei ) ;
2018-08-14 18:02:33 +02:00
if ( completion_options . size ( ) = = 0 ) {
for ( int i = 0 ; i < completion_strings . size ( ) ; i + + ) {
if ( s . is_subsequence_of ( completion_strings [ i ] ) ) {
completion_options . push_back ( completion_strings [ i ] ) ;
2016-07-03 16:32:30 +02:00
}
2018-08-14 18:02:33 +02:00
}
}
2016-07-03 16:32:30 +02:00
2018-08-14 18:02:33 +02:00
if ( completion_options . size ( ) = = 0 ) {
for ( int i = 0 ; i < completion_strings . size ( ) ; i + + ) {
if ( s . is_subsequence_ofi ( completion_strings [ i ] ) ) {
2016-06-13 21:55:26 +02:00
completion_options . push_back ( completion_strings [ i ] ) ;
2015-01-02 19:08:40 +01:00
}
}
}
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( completion_options . size ( ) = = 0 ) {
2015-01-02 19:08:40 +01:00
//no options to complete, cancel
_cancel_completion ( ) ;
2018-08-14 18:02:33 +02:00
return ;
}
2017-01-08 07:01:52 +01:00
2018-08-14 18:02:33 +02:00
if ( completion_options . size ( ) = = 1 & & s = = completion_options [ 0 ] ) {
// A perfect match, stop completion
_cancel_completion ( ) ;
2015-01-02 19:08:40 +01:00
return ;
}
2016-03-09 00:00:52 +01:00
2016-06-13 21:55:26 +02:00
// The top of the list is the best match
2017-03-05 16:44:50 +01:00
completion_current = completion_options [ 0 ] ;
completion_enabled = true ;
2014-02-10 02:10:30 +01:00
}
void TextEdit : : query_code_comple ( ) {
2016-03-09 00:00:52 +01:00
2014-12-17 02:31:57 +01:00
String l = text [ cursor . line ] ;
2017-03-05 16:44:50 +01:00
int ofs = CLAMP ( cursor . column , 0 , l . length ( ) ) ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
bool inquote = false ;
2015-06-26 06:14:31 +02:00
2017-03-05 16:44:50 +01:00
int c = ofs - 1 ;
while ( c > = 0 ) {
if ( l [ c ] = = ' " ' | | l [ c ] = = ' \' ' )
inquote = ! inquote ;
2015-06-26 06:14:31 +02:00
c - - ;
}
2017-03-05 16:44:50 +01:00
if ( ofs > 0 & & ( inquote | | _is_completable ( l [ ofs - 1 ] ) | | completion_prefixes . has ( String : : chr ( l [ ofs - 1 ] ) ) ) )
2014-12-17 02:31:57 +01:00
emit_signal ( " request_completion " ) ;
2017-08-24 05:06:56 +02:00
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 02:31:57 +01:00
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
void TextEdit : : set_code_hint ( const String & p_hint ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
completion_hint = p_hint ;
completion_hint_offset = - 0xFFFF ;
2014-12-17 02:31:57 +01:00
update ( ) ;
2014-02-10 02:10:30 +01:00
}
2017-08-24 05:06:56 +02:00
void TextEdit : : code_complete ( const Vector < String > & p_strings , bool p_forced ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
completion_strings = p_strings ;
completion_active = true ;
2017-08-24 05:06:56 +02:00
completion_forced = p_forced ;
2017-03-05 16:44:50 +01:00
completion_current = " " ;
completion_index = 0 ;
2015-01-02 19:08:40 +01:00
_update_completion_candidates ( ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
String TextEdit : : get_word_at_pos ( const Vector2 & p_pos ) const {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
int row , col ;
2016-09-12 15:52:29 +02:00
_get_mouse_pos ( p_pos , row , col ) ;
String s = text [ row ] ;
2017-03-05 16:44:50 +01:00
if ( s . length ( ) = = 0 )
2016-09-12 15:52:29 +02:00
return " " ;
2017-12-23 09:59:54 +01:00
int beg , end ;
if ( select_word ( s , col , beg , end ) ) {
2016-09-12 15:52:29 +02:00
2017-08-02 19:20:27 +02:00
bool inside_quotes = false ;
2018-09-28 06:31:15 +02:00
CharType selected_quote = ' \0 ' ;
2017-09-01 22:33:39 +02:00
int qbegin = 0 , qend = 0 ;
2017-08-02 19:20:27 +02:00
for ( int i = 0 ; i < s . length ( ) ; i + + ) {
2018-03-11 13:59:48 +01: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 19:20:27 +02:00
}
}
}
}
2017-03-05 16:44:50 +01:00
return s . substr ( beg , end - beg ) ;
2016-09-12 15:52:29 +02:00
}
return String ( ) ;
}
2017-03-05 16:44:50 +01:00
String TextEdit : : get_tooltip ( const Point2 & p_pos ) const {
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
if ( ! tooltip_obj )
return Control : : get_tooltip ( p_pos ) ;
2017-03-05 16:44:50 +01:00
int row , col ;
2015-11-07 13:39:03 +01:00
_get_mouse_pos ( p_pos , row , col ) ;
2015-01-02 19:08:40 +01:00
String s = text [ row ] ;
2017-03-05 16:44:50 +01:00
if ( s . length ( ) = = 0 )
2015-01-02 19:08:40 +01:00
return Control : : get_tooltip ( p_pos ) ;
2017-12-23 09:59:54 +01:00
int beg , end ;
if ( select_word ( s , col , beg , end ) ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
String tt = tooltip_obj - > call ( tooltip_func , s . substr ( beg , end - beg ) , tooltip_ud ) ;
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
return tt ;
}
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
return Control : : get_tooltip ( p_pos ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
void TextEdit : : set_tooltip_request_func ( Object * p_obj , const StringName & p_function , const Variant & p_udata ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
tooltip_obj = p_obj ;
tooltip_func = p_function ;
tooltip_ud = p_udata ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
void TextEdit : : set_line ( int line , String new_text ) {
2015-01-02 19:08:40 +01: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 16:44:50 +01:00
if ( cursor . line = = line ) {
cursor . column = MIN ( cursor . column , new_text . length ( ) ) ;
2015-05-05 05:17:22 +02:00
}
2018-09-08 14:20:45 +02:00
if ( is_selection_active ( ) & & line = = selection . to_line & & selection . to_column > text [ line ] . length ( ) ) {
selection . to_column = text [ line ] . length ( ) ;
}
2014-11-12 15:39:21 +01:00
}
2017-03-05 16:44:50 +01:00
void TextEdit : : insert_at ( const String & p_text , int at ) {
2014-12-07 08:21:49 +01:00
cursor_set_column ( 0 ) ;
2017-11-13 00:12:17 +01:00
cursor_set_line ( at , false , true ) ;
2017-03-05 16:44:50 +01:00
_insert_text ( at , 0 , p_text + " \n " ) ;
2014-11-12 15:39:21 +01:00
}
2014-02-10 02:10:30 +01:00
void TextEdit : : set_show_line_numbers ( bool p_show ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
line_numbers = p_show ;
2015-01-02 19:08:40 +01:00
update ( ) ;
2014-02-10 02:10:30 +01:00
}
2016-10-10 13:43:09 +02:00
void TextEdit : : set_line_numbers_zero_padded ( bool p_zero_padded ) {
2017-03-05 16:44:50 +01:00
line_numbers_zero_padded = p_zero_padded ;
2016-10-10 13:43:09 +02:00
update ( ) ;
}
2016-07-11 16:11:35 +02:00
bool TextEdit : : is_show_line_numbers_enabled ( ) const {
return line_numbers ;
}
2016-10-10 10:34:51 +02:00
void TextEdit : : set_show_line_length_guideline ( bool p_show ) {
2017-03-05 16:44:50 +01:00
line_length_guideline = p_show ;
2016-10-10 10:34:51 +02:00
update ( ) ;
}
void TextEdit : : set_line_length_guideline_column ( int p_column ) {
2017-03-05 16:44:50 +01:00
line_length_guideline_col = p_column ;
2016-10-10 10:34:51 +02:00
update ( ) ;
}
2018-05-30 14:02:51 +02:00
void TextEdit : : set_breakpoint_gutter_enabled ( bool p_draw ) {
2016-05-26 15:17:14 +02:00
draw_breakpoint_gutter = p_draw ;
update ( ) ;
}
2018-05-30 14:02:51 +02:00
bool TextEdit : : is_breakpoint_gutter_enabled ( ) const {
2016-05-26 15:17:14 +02: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-13 00:12:17 +01: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 13:51:25 +02: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 ;
}
2017-11-13 00:12:17 +01:00
void TextEdit : : set_hiding_enabled ( int p_enabled ) {
2017-11-16 05:00:27 +01:00
if ( ! p_enabled )
unhide_all_lines ( ) ;
2017-11-13 00:12:17 +01:00
hiding_enabled = p_enabled ;
update ( ) ;
}
int TextEdit : : is_hiding_enabled ( ) const {
return hiding_enabled ;
}
2017-10-22 14:38:00 +02: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 15:29:54 +02:00
bool TextEdit : : is_text_field ( ) const {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
return true ;
2015-10-17 15:29:54 +02:00
}
2016-05-17 01:25:17 +02:00
void TextEdit : : menu_option ( int p_option ) {
2017-03-05 16:44:50 +01:00
switch ( p_option ) {
2016-05-17 01:25:17 +02:00
case MENU_CUT : {
2016-09-19 14:17:48 +02:00
if ( ! readonly ) {
cut ( ) ;
}
2016-05-17 01:25:17 +02:00
} break ;
case MENU_COPY : {
copy ( ) ;
} break ;
case MENU_PASTE : {
2016-09-19 14:17:48 +02:00
if ( ! readonly ) {
paste ( ) ;
}
2016-05-17 01:25:17 +02:00
} break ;
case MENU_CLEAR : {
2016-09-19 14:17:48 +02:00
if ( ! readonly ) {
clear ( ) ;
}
2016-05-17 01:25:17 +02:00
} break ;
case MENU_SELECT_ALL : {
select_all ( ) ;
} break ;
case MENU_UNDO : {
undo ( ) ;
} break ;
2018-11-29 18:21:43 +01:00
case MENU_REDO : {
redo ( ) ;
}
}
2016-05-17 01:25:17 +02:00
}
2016-09-12 15:52:29 +02:00
void TextEdit : : set_select_identifiers_on_hover ( bool p_enable ) {
2017-03-05 16:44:50 +01:00
select_identifiers_enabled = p_enable ;
2016-09-12 15:52:29 +02:00
}
bool TextEdit : : is_selecting_identifiers_on_hover_enabled ( ) const {
return select_identifiers_enabled ;
}
2016-09-29 09:12:45 +02:00
void TextEdit : : set_context_menu_enabled ( bool p_enable ) {
context_menu_enabled = p_enable ;
}
2016-09-12 15:52:29 +02:00
2017-11-09 21:46:29 +01:00
bool TextEdit : : is_context_menu_enabled ( ) {
return context_menu_enabled ;
}
2016-05-17 01:25:17 +02:00
PopupMenu * TextEdit : : get_menu ( ) const {
return menu ;
}
2014-02-10 02:10:30 +01:00
void TextEdit : : _bind_methods ( ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01: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 21:02:08 +02:00
ClassDB : : bind_method ( D_METHOD ( " _v_scroll_input " ) , & TextEdit : : _v_scroll_input ) ;
2019-01-05 17:58:54 +01:00
ClassDB : : bind_method ( D_METHOD ( " _update_wrap_at " ) , & TextEdit : : _update_wrap_at ) ;
2016-03-09 00:00:52 +01:00
2017-08-20 17:45:01 +02:00
BIND_ENUM_CONSTANT ( SEARCH_MATCH_CASE ) ;
BIND_ENUM_CONSTANT ( SEARCH_WHOLE_WORDS ) ;
BIND_ENUM_CONSTANT ( SEARCH_BACKWARDS ) ;
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
/*
2017-11-05 16:54:00 +01:00
ClassDB : : bind_method ( D_METHOD ( " delete_char " ) , & TextEdit : : delete_char ) ;
ClassDB : : bind_method ( D_METHOD ( " delete_line " ) , & TextEdit : : delete_line ) ;
2014-02-10 02:10:30 +01:00
*/
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01: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 12:47:24 +01:00
2017-03-05 16:44:50 +01: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 12:47:24 +01:00
2017-04-15 19:48:10 +02:00
ClassDB : : bind_method ( D_METHOD ( " cursor_set_column " , " column " , " adjust_viewport " ) , & TextEdit : : cursor_set_column , DEFVAL ( true ) ) ;
2018-05-15 21:53:42 +02: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 12:47:24 +01:00
2017-03-05 16:44:50 +01: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 12:47:24 +01: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 02:36:47 +01: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 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_readonly " , " enable " ) , & TextEdit : : set_readonly ) ;
2017-11-11 19:07:17 +01:00
ClassDB : : bind_method ( D_METHOD ( " is_readonly " ) , & TextEdit : : is_readonly ) ;
2018-01-26 02:41:17 +01: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 ) ;
2018-01-11 23:35:12 +01:00
// ClassDB::bind_method(D_METHOD("set_max_chars", "amount"), &TextEdit::set_max_chars);
// ClassDB::bind_method(D_METHOD("get_max_char"), &TextEdit::get_max_chars);
2017-11-09 21:46:29 +01: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 16:44:50 +01: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 20:34:16 +01:00
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " select " , " from_line " , " from_column " , " to_line " , " to_column " ) , & TextEdit : : select ) ;
2017-12-16 20:34:16 +01:00
ClassDB : : bind_method ( D_METHOD ( " select_all " ) , & TextEdit : : select_all ) ;
ClassDB : : bind_method ( D_METHOD ( " deselect " ) , & TextEdit : : deselect ) ;
2017-03-05 16:44:50 +01: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 13:54:55 +02:00
ClassDB : : bind_method ( D_METHOD ( " search " , " key " , " flags " , " from_line " , " from_column " ) , & TextEdit : : _search_bind ) ;
2017-03-05 16:44:50 +01: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 12:47:24 +01: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 09:59:17 +02: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 ) ;
2018-05-30 14:02:51 +02: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 09:59:17 +02: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 12:47:24 +01:00
2017-11-13 00:12:17 +01: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 15:19:56 +01:00
ClassDB : : bind_method ( D_METHOD ( " is_line_hidden " , " line " ) , & TextEdit : : is_line_hidden ) ;
2017-11-16 05:00:27 +01:00
ClassDB : : bind_method ( D_METHOD ( " fold_all_lines " ) , & TextEdit : : fold_all_lines ) ;
2017-11-13 00:12:17 +01: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 19:17:10 +01:00
ClassDB : : bind_method ( D_METHOD ( " toggle_fold_line " , " line " ) , & TextEdit : : toggle_fold_line ) ;
2017-11-13 00:12:17 +01: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 12:47:24 +01: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 19:24:05 +02: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 16:44:50 +01: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 12:47:24 +01:00
2017-10-22 14:38:00 +02: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 16:23:45 +02: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 20:13:24 +02: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 16:23:45 +02:00
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " add_keyword_color " , " keyword " , " color " ) , & TextEdit : : add_keyword_color ) ;
2018-04-02 13:41:44 +02: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 16:44:50 +01: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 12:11:42 +02:00
ClassDB : : bind_method ( D_METHOD ( " menu_option " , " option " ) , & TextEdit : : menu_option ) ;
2017-08-09 13:19:41 +02:00
ClassDB : : bind_method ( D_METHOD ( " get_menu " ) , & TextEdit : : get_menu ) ;
2016-03-09 00:00:52 +01:00
2018-05-30 14:02:51 +02:00
ClassDB : : bind_method ( D_METHOD ( " get_breakpoints " ) , & TextEdit : : get_breakpoints_array ) ;
ClassDB : : bind_method ( D_METHOD ( " remove_breakpoints " ) , & TextEdit : : remove_breakpoints ) ;
2017-11-12 16:10:26 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : STRING , " text " , PROPERTY_HINT_MULTILINE_TEXT ) , " set_text " , " get_text " ) ;
2017-11-11 19:07:17 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " readonly " ) , " set_readonly " , " is_readonly " ) ;
2017-10-22 14:38:00 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " highlight_current_line " ) , " set_highlight_current_line " , " is_highlight_current_line_enabled " ) ;
2017-02-12 01:11:37 +01: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 09:59:17 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " draw_tabs " ) , " set_draw_tabs " , " is_drawing_tabs " ) ;
2018-05-30 14:02:51 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " breakpoint_gutter " ) , " set_breakpoint_gutter_enabled " , " is_breakpoint_gutter_enabled " ) ;
2019-04-24 09:59:17 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " fold_gutter " ) , " set_draw_fold_gutter " , " is_drawing_fold_gutter " ) ;
2017-02-12 01:11:37 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " highlight_all_occurrences " ) , " set_highlight_all_occurrences " , " is_highlight_all_occurrences_enabled " ) ;
2017-09-27 19:24:05 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " override_selected_font_color " ) , " set_override_selected_font_color " , " is_overriding_selected_font_color " ) ;
2017-11-09 21:46:29 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " context_menu_enabled " ) , " set_context_menu_enabled " , " is_context_menu_enabled " ) ;
2017-08-19 16:23:45 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " smooth_scrolling " ) , " set_smooth_scroll_enable " , " is_smooth_scroll_enabled " ) ;
2017-08-21 20:13:24 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : REAL , " v_scroll_speed " ) , " set_v_scroll_speed " , " get_v_scroll_speed " ) ;
2017-11-13 00:12:17 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " hiding_enabled " ) , " set_hiding_enabled " , " is_hiding_enabled " ) ;
2018-01-26 02:41:17 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " wrap_enabled " ) , " set_wrap_enabled " , " is_wrap_enabled " ) ;
2018-01-11 23:35:12 +01:00
// ADD_PROPERTY(PropertyInfo(Variant::BOOL, "max_chars"), "set_max_chars", "get_max_chars");
2016-07-11 16:11:35 +02:00
2017-03-05 16:44:50 +01:00
ADD_GROUP ( " Caret " , " caret_ " ) ;
2017-02-12 01:11:37 +01: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 15:30:02 +01: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 02:36:47 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " caret_moving_by_right_click " ) , " set_right_click_moves_caret " , " is_right_click_moving_caret " ) ;
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
ADD_SIGNAL ( MethodInfo ( " cursor_changed " ) ) ;
ADD_SIGNAL ( MethodInfo ( " text_changed " ) ) ;
ADD_SIGNAL ( MethodInfo ( " request_completion " ) ) ;
2017-03-05 16:44:50 +01: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 13:51:25 +02:00
ADD_SIGNAL ( MethodInfo ( " info_clicked " , PropertyInfo ( Variant : : INT , " row " ) , PropertyInfo ( Variant : : STRING , " info " ) ) ) ;
2016-05-17 01:25:17 +02:00
2017-08-20 17:45:01 +02: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 18:21:43 +01:00
BIND_ENUM_CONSTANT ( MENU_REDO ) ;
2017-08-20 17:45:01 +02:00
BIND_ENUM_CONSTANT ( MENU_MAX ) ;
2016-05-17 01:25:17 +02:00
2017-03-05 16:44:50 +01:00
GLOBAL_DEF ( " gui/timers/text_edit_idle_detect_sec " , 3 ) ;
2018-10-05 18:43:53 +02: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 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
TextEdit : : TextEdit ( ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
setting_row = false ;
draw_tabs = false ;
2017-09-27 19:24:05 +02:00
override_selected_font_color = false ;
2017-03-05 16:44:50 +01:00
draw_caret = true ;
max_chars = 0 ;
2015-01-02 19:08:40 +01:00
clear ( ) ;
2018-01-26 02:41:17 +01:00
wrap_enabled = false ;
wrap_right_offset = 10 ;
2015-01-02 19:08:40 +01:00
set_focus_mode ( FOCUS_ALL ) ;
2018-04-02 13:41:44 +02:00
syntax_highlighter = NULL ;
2015-01-02 19:08:40 +01:00
_update_caches ( ) ;
2017-03-05 16:44:50 +01:00
cache . row_height = 1 ;
cache . line_spacing = 1 ;
cache . line_number_w = 1 ;
cache . breakpoint_gutter_width = 0 ;
2016-05-26 15:17:14 +02:00
breakpoint_gutter_width = 0 ;
2017-11-13 00:12:17 +01:00
cache . fold_gutter_width = 0 ;
fold_gutter_width = 0 ;
2019-04-20 13:51:25 +02:00
info_gutter_width = 0 ;
cache . info_gutter_width = 0 ;
2018-09-11 18:25:40 +02:00
set_default_cursor_shape ( CURSOR_IBEAM ) ;
2015-06-07 03:06:58 +02:00
2017-04-17 15:24:30 +02:00
indent_size = 4 ;
text . set_indent_size ( indent_size ) ;
2015-01-02 19:08:40 +01:00
text . clear ( ) ;
2018-04-22 19:36:01 +02:00
//text.insert(1,"Mongolia...");
2017-01-14 12:26:56 +01:00
//text.insert(2,"PAIS GENEROSO!!");
2015-01-02 19:08:40 +01:00
text . set_color_regions ( & color_regions ) ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
h_scroll = memnew ( HScrollBar ) ;
v_scroll = memnew ( VScrollBar ) ;
2016-03-09 00:00:52 +01:00
2015-01-02 19:08:40 +01:00
add_child ( h_scroll ) ;
add_child ( v_scroll ) ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
updating_scrolls = false ;
selection . active = false ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
h_scroll - > connect ( " value_changed " , this , " _scroll_moved " ) ;
v_scroll - > connect ( " value_changed " , this , " _scroll_moved " ) ;
2016-03-09 00:00:52 +01:00
2017-08-22 21:02:08 +02:00
v_scroll - > connect ( " scrolling " , this , " _v_scroll_input " ) ;
2017-03-05 16:44:50 +01:00
cursor_changed_dirty = false ;
text_changed_dirty = false ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01: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-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
block_caret = false ;
caret_blink_enabled = false ;
2016-05-09 20:21:55 +02:00
caret_blink_timer = memnew ( Timer ) ;
add_child ( caret_blink_timer ) ;
caret_blink_timer - > set_wait_time ( 0.65 ) ;
2017-03-05 16:44:50 +01:00
caret_blink_timer - > connect ( " timeout " , this , " _toggle_draw_caret " ) ;
2016-05-09 20:21:55 +02:00
cursor_set_blink_enabled ( false ) ;
2017-12-20 02:36:47 +01:00
right_click_moves_caret = true ;
2016-05-09 20:21:55 +02:00
2017-03-05 16:44:50 +01:00
idle_detect = memnew ( Timer ) ;
2015-01-02 19:08:40 +01:00
add_child ( idle_detect ) ;
idle_detect - > set_one_shot ( true ) ;
2017-01-05 13:16:00 +01:00
idle_detect - > set_wait_time ( GLOBAL_GET ( " gui/timers/text_edit_idle_detect_sec " ) ) ;
2017-03-05 16:44:50 +01:00
idle_detect - > connect ( " timeout " , this , " _push_current_op " ) ;
2015-12-09 19:56:41 +01:00
2017-03-05 16:44:50 +01:00
click_select_held = memnew ( Timer ) ;
2015-12-09 19:56:41 +01:00
add_child ( click_select_held ) ;
click_select_held - > set_wait_time ( 0.05 ) ;
2017-03-05 16:44:50 +01:00
click_select_held - > connect ( " timeout " , this , " _click_selection_held " ) ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01: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 ;
draw_breakpoint_gutter = false ;
2017-11-13 00:12:17 +01:00
draw_fold_gutter = false ;
2019-04-20 13:51:25 +02:00
draw_info_gutter = false ;
2017-11-13 00:12:17 +01:00
hiding_enabled = false ;
2017-03-05 16:44:50 +01: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 14:38:00 +02:00
highlight_current_line = false ;
2017-04-17 15:24:30 +02:00
indent_using_spaces = false ;
space_indent = " " ;
2017-03-05 16:44:50 +01:00
auto_indent = false ;
2016-03-31 21:49:30 +02:00
insert_mode = false ;
2017-03-05 16:44:50 +01:00
window_has_focus = true ;
select_identifiers_enabled = false ;
2017-08-19 16:23:45 +02:00
smooth_scroll_enabled = false ;
scrolling = false ;
target_v_scroll = 0 ;
2017-08-21 20:13:24 +02:00
v_scroll_speed = 80 ;
2016-05-17 01:25:17 +02:00
2017-03-05 16:44:50 +01:00
context_menu_enabled = true ;
menu = memnew ( PopupMenu ) ;
2016-05-17 01:25:17 +02:00
add_child ( menu ) ;
2019-04-22 01:09:52 +02:00
set_readonly ( false ) ;
2017-03-05 16:44:50 +01:00
menu - > connect ( " id_pressed " , this , " menu_option " ) ;
2019-04-12 04:21:48 +02:00
first_draw = true ;
2019-04-22 18:20:27 +02:00
executing_line = - 1 ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
TextEdit : : ~ TextEdit ( ) {
2017-12-16 20:34:16 +01:00
}
2018-04-02 13:41:44 +02:00
///////////////////////////////////////////////////////////////////////////////
Map < int , TextEdit : : HighlighterInfo > TextEdit : : _get_line_syntax_highlighting ( int p_line ) {
if ( syntax_highlighter ! = NULL ) {
return syntax_highlighter - > _get_line_syntax_highlighting ( p_line ) ;
}
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 15:14:19 +02:00
int in_region = _is_line_in_region ( p_line ) ;
2018-04-02 13:41:44 +02: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 ] ) ;
// allow ABCDEF in hex notation
if ( is_hex_notation & & ( _is_hex_symbol ( str [ j ] ) | | is_number ) ) {
is_number = true ;
} else {
is_hex_notation = false ;
}
2018-09-08 14:41:46 +02:00
// check for dot or underscore or 'x' for hex notation in floating point number or 'e' for scientific notation
if ( ( str [ j ] = = ' . ' | | str [ j ] = = ' x ' | | str [ j ] = = ' _ ' | | str [ j ] = = ' f ' | | str [ j ] = = ' e ' ) & & ! in_word & & prev_is_number & & ! is_number ) {
2018-04-02 13:41:44 +02: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 ;
}
} else if ( in_region = = cri . region & & ! color_regions [ cri . region ] . line_only ) { //ignore otherwise
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 ] = = ' . ' ) {
col = NULL ; //member indexing not allowed
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 + + ;
}
// check for space between name and bracket
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 ;
}
}
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 ;
}