Implement NSTextInputClient protocol for IME
This commit is contained in:
parent
b5c6b0cf1c
commit
8aa86cb9bc
@ -63,6 +63,8 @@ class OS {
|
|||||||
void *_stack_bottom;
|
void *_stack_bottom;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
typedef void (*ImeCallback)(void *p_inp, String p_text, Point2 p_selection);
|
||||||
|
|
||||||
enum RenderThreadMode {
|
enum RenderThreadMode {
|
||||||
|
|
||||||
RENDER_THREAD_UNSAFE,
|
RENDER_THREAD_UNSAFE,
|
||||||
@ -183,6 +185,7 @@ public:
|
|||||||
virtual bool get_borderless_window() { return 0; }
|
virtual bool get_borderless_window() { return 0; }
|
||||||
|
|
||||||
virtual void set_ime_position(const Point2 &p_pos) {}
|
virtual void set_ime_position(const Point2 &p_pos) {}
|
||||||
|
virtual void set_ime_intermediate_text_callback(ImeCallback p_callback, void *p_inp) {}
|
||||||
|
|
||||||
virtual Error open_dynamic_library(const String p_path, void *&p_library_handle) { return ERR_UNAVAILABLE; }
|
virtual Error open_dynamic_library(const String p_path, void *&p_library_handle) { return ERR_UNAVAILABLE; }
|
||||||
virtual Error close_dynamic_library(void *p_library_handle) { return ERR_UNAVAILABLE; }
|
virtual Error close_dynamic_library(void *p_library_handle) { return ERR_UNAVAILABLE; }
|
||||||
|
@ -104,6 +104,10 @@ public:
|
|||||||
Size2 window_size;
|
Size2 window_size;
|
||||||
Rect2 restore_rect;
|
Rect2 restore_rect;
|
||||||
|
|
||||||
|
Point2 im_position;
|
||||||
|
ImeCallback im_callback;
|
||||||
|
void *im_target;
|
||||||
|
|
||||||
power_osx *power_manager;
|
power_osx *power_manager;
|
||||||
|
|
||||||
float _mouse_scale(float p_scale) {
|
float _mouse_scale(float p_scale) {
|
||||||
@ -203,6 +207,8 @@ public:
|
|||||||
|
|
||||||
virtual void set_borderless_window(int p_borderless);
|
virtual void set_borderless_window(int p_borderless);
|
||||||
virtual bool get_borderless_window();
|
virtual bool get_borderless_window();
|
||||||
|
virtual void set_ime_position(const Point2 &p_pos);
|
||||||
|
virtual void set_ime_intermediate_text_callback(ImeCallback p_callback, void *p_inp);
|
||||||
|
|
||||||
virtual PowerState get_power_state();
|
virtual PowerState get_power_state();
|
||||||
virtual int get_power_seconds_left();
|
virtual int get_power_seconds_left();
|
||||||
|
@ -261,10 +261,12 @@ static bool mouse_down_control = false;
|
|||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface GodotContentView : NSView {
|
@interface GodotContentView : NSView <NSTextInputClient> {
|
||||||
NSTrackingArea *trackingArea;
|
NSTrackingArea *trackingArea;
|
||||||
|
NSMutableAttributedString *markedText;
|
||||||
|
bool imeMode;
|
||||||
}
|
}
|
||||||
|
- (void)cancelComposition;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation GodotContentView
|
@implementation GodotContentView
|
||||||
@ -278,16 +280,128 @@ static bool mouse_down_control = false;
|
|||||||
- (id)init {
|
- (id)init {
|
||||||
self = [super init];
|
self = [super init];
|
||||||
trackingArea = nil;
|
trackingArea = nil;
|
||||||
|
imeMode = false;
|
||||||
[self updateTrackingAreas];
|
[self updateTrackingAreas];
|
||||||
[self registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]];
|
[self registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]];
|
||||||
|
markedText = [[NSMutableAttributedString alloc] init];
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dealloc {
|
- (void)dealloc {
|
||||||
[trackingArea release];
|
[trackingArea release];
|
||||||
|
[markedText release];
|
||||||
[super dealloc];
|
[super dealloc];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const NSRange kEmptyRange = { NSNotFound, 0 };
|
||||||
|
|
||||||
|
- (BOOL)hasMarkedText {
|
||||||
|
return (markedText.length > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSRange)markedRange {
|
||||||
|
return (markedText.length > 0) ? NSMakeRange(0, markedText.length - 1) : kEmptyRange;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSRange)selectedRange {
|
||||||
|
return kEmptyRange;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setMarkedText:(id)aString selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange {
|
||||||
|
if ([aString isKindOfClass:[NSAttributedString class]]) {
|
||||||
|
[markedText initWithAttributedString:aString];
|
||||||
|
} else {
|
||||||
|
[markedText initWithString:aString];
|
||||||
|
}
|
||||||
|
if (OS_OSX::singleton->im_callback) {
|
||||||
|
imeMode = true;
|
||||||
|
String ret;
|
||||||
|
ret.parse_utf8([[markedText mutableString] UTF8String]);
|
||||||
|
OS_OSX::singleton->im_callback(OS_OSX::singleton->im_target, ret, Point2(selectedRange.location, selectedRange.length));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)doCommandBySelector:(SEL)aSelector {
|
||||||
|
if ([self respondsToSelector:aSelector])
|
||||||
|
[self performSelector:aSelector];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)unmarkText {
|
||||||
|
imeMode = false;
|
||||||
|
[[markedText mutableString] setString:@""];
|
||||||
|
if (OS_OSX::singleton->im_callback)
|
||||||
|
OS_OSX::singleton->im_callback(OS_OSX::singleton->im_target, "", Point2());
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSArray *)validAttributesForMarkedText {
|
||||||
|
return [NSArray array];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSUInteger)characterIndexForPoint:(NSPoint)aPoint {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSRect)firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange {
|
||||||
|
const NSRect contentRect = [OS_OSX::singleton->window_view frame];
|
||||||
|
NSRect pointInWindowRect = NSMakeRect(OS_OSX::singleton->im_position.x / OS_OSX::singleton->display_scale, contentRect.size.height - (OS_OSX::singleton->im_position.y / OS_OSX::singleton->display_scale) - 1, 0, 0);
|
||||||
|
NSPoint pointOnScreen = [[OS_OSX::singleton->window_view window] convertRectToScreen:pointInWindowRect].origin;
|
||||||
|
|
||||||
|
return NSMakeRect(pointOnScreen.x, pointOnScreen.y, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)cancelComposition {
|
||||||
|
[self unmarkText];
|
||||||
|
NSInputManager *currentInputManager = [NSInputManager currentInputManager];
|
||||||
|
[currentInputManager markedTextAbandoned:self];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)insertText:(id)aString {
|
||||||
|
[self insertText:aString replacementRange:NSMakeRange(0, 0)];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)insertText:(id)aString replacementRange:(NSRange)replacementRange {
|
||||||
|
NSEvent *event = [NSApp currentEvent];
|
||||||
|
Ref<InputEventKey> k;
|
||||||
|
k.instance();
|
||||||
|
|
||||||
|
get_key_modifier_state([event modifierFlags], k);
|
||||||
|
k->set_pressed(true);
|
||||||
|
k->set_echo(false);
|
||||||
|
k->set_scancode(0);
|
||||||
|
|
||||||
|
NSString *characters;
|
||||||
|
if ([aString isKindOfClass:[NSAttributedString class]]) {
|
||||||
|
characters = [aString string];
|
||||||
|
} else {
|
||||||
|
characters = (NSString *)aString;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSUInteger i, length = [characters length];
|
||||||
|
|
||||||
|
NSCharacterSet *ctrlChars = [NSCharacterSet controlCharacterSet];
|
||||||
|
NSCharacterSet *wsnlChars = [NSCharacterSet whitespaceAndNewlineCharacterSet];
|
||||||
|
if ([characters rangeOfCharacterFromSet:ctrlChars].length && [characters rangeOfCharacterFromSet:wsnlChars].length == 0) {
|
||||||
|
NSInputManager *currentInputManager = [NSInputManager currentInputManager];
|
||||||
|
[currentInputManager markedTextAbandoned:self];
|
||||||
|
[self cancelComposition];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < length; i++) {
|
||||||
|
const unichar codepoint = [characters characterAtIndex:i];
|
||||||
|
if ((codepoint & 0xFF00) == 0xF700)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
k->set_unicode(codepoint);
|
||||||
|
OS_OSX::singleton->push_input(k);
|
||||||
|
}
|
||||||
|
[self cancelComposition];
|
||||||
|
}
|
||||||
|
|
||||||
- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender {
|
- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender {
|
||||||
return NSDragOperationCopy;
|
return NSDragOperationCopy;
|
||||||
}
|
}
|
||||||
@ -634,15 +748,12 @@ static int translateKey(unsigned int key) {
|
|||||||
NSString *characters = [event characters];
|
NSString *characters = [event characters];
|
||||||
NSUInteger i, length = [characters length];
|
NSUInteger i, length = [characters length];
|
||||||
|
|
||||||
if (length > 0 && keycode_has_unicode(k->get_scancode())) {
|
//disable raw input in IME mode
|
||||||
for (i = 0; i < length; i++) {
|
if (!imeMode)
|
||||||
k->set_unicode([characters characterAtIndex:i]);
|
|
||||||
OS_OSX::singleton->push_input(k);
|
OS_OSX::singleton->push_input(k);
|
||||||
k->set_scancode(0);
|
|
||||||
}
|
if ((OS_OSX::singleton->im_position.x != 0) && (OS_OSX::singleton->im_position.y != 0))
|
||||||
} else {
|
[self interpretKeyEvents:[NSArray arrayWithObject:event]];
|
||||||
OS_OSX::singleton->push_input(k);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)flagsChanged:(NSEvent *)event {
|
- (void)flagsChanged:(NSEvent *)event {
|
||||||
@ -761,6 +872,18 @@ inline void sendScrollEvent(int button, double factor, int modifierFlags) {
|
|||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
void OS_OSX::set_ime_intermediate_text_callback(ImeCallback p_callback, void *p_inp) {
|
||||||
|
im_callback = p_callback;
|
||||||
|
im_target = p_inp;
|
||||||
|
if (!im_callback) {
|
||||||
|
[window_view cancelComposition];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OS_OSX::set_ime_position(const Point2 &p_pos) {
|
||||||
|
im_position = p_pos;
|
||||||
|
}
|
||||||
|
|
||||||
int OS_OSX::get_video_driver_count() const {
|
int OS_OSX::get_video_driver_count() const {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -1735,6 +1858,9 @@ OS_OSX::OS_OSX() {
|
|||||||
mouse_mode = OS::MOUSE_MODE_VISIBLE;
|
mouse_mode = OS::MOUSE_MODE_VISIBLE;
|
||||||
main_loop = NULL;
|
main_loop = NULL;
|
||||||
singleton = this;
|
singleton = this;
|
||||||
|
im_position = Point2();
|
||||||
|
im_callback = NULL;
|
||||||
|
im_target = NULL;
|
||||||
autoreleasePool = [[NSAutoreleasePool alloc] init];
|
autoreleasePool = [[NSAutoreleasePool alloc] init];
|
||||||
|
|
||||||
eventSource = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
|
eventSource = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
|
||||||
|
@ -641,6 +641,35 @@ void LineEdit::_notification(int p_what) {
|
|||||||
if (char_ofs >= t.length())
|
if (char_ofs >= t.length())
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
if (char_ofs == cursor_pos) {
|
||||||
|
if (ime_text.length() > 0) {
|
||||||
|
int ofs = 0;
|
||||||
|
while (true) {
|
||||||
|
if (ofs >= ime_text.length())
|
||||||
|
break;
|
||||||
|
|
||||||
|
CharType cchar = (pass && !text.empty()) ? '*' : ime_text[ofs];
|
||||||
|
CharType next = (pass && !text.empty()) ? '*' : ime_text[ofs + 1];
|
||||||
|
int im_char_width = font->get_char_size(cchar, next).width;
|
||||||
|
|
||||||
|
if ((x_ofs + im_char_width) > ofs_max)
|
||||||
|
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(x_ofs, y_ofs + caret_height), Size2(im_char_width, 3)), font_color);
|
||||||
|
} else {
|
||||||
|
VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(x_ofs, y_ofs + caret_height), Size2(im_char_width, 1)), font_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
font->draw_char(ci, Point2(x_ofs, y_ofs + font_ascent), cchar, next, font_color);
|
||||||
|
|
||||||
|
x_ofs += im_char_width;
|
||||||
|
ofs++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
CharType cchar = (pass && !text.empty()) ? '*' : t[char_ofs];
|
CharType cchar = (pass && !text.empty()) ? '*' : t[char_ofs];
|
||||||
CharType next = (pass && !text.empty()) ? '*' : t[char_ofs + 1];
|
CharType next = (pass && !text.empty()) ? '*' : t[char_ofs + 1];
|
||||||
int char_width = font->get_char_size(cchar, next).width;
|
int char_width = font->get_char_size(cchar, next).width;
|
||||||
@ -657,24 +686,54 @@ void LineEdit::_notification(int p_what) {
|
|||||||
font->draw_char(ci, Point2(x_ofs, y_ofs + font_ascent), cchar, next, selected ? font_color_selected : font_color);
|
font->draw_char(ci, Point2(x_ofs, y_ofs + font_ascent), cchar, next, selected ? font_color_selected : font_color);
|
||||||
|
|
||||||
if (char_ofs == cursor_pos && draw_caret) {
|
if (char_ofs == cursor_pos && draw_caret) {
|
||||||
VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(
|
if (ime_text.length() == 0) {
|
||||||
Point2(x_ofs, y_ofs), Size2(1, caret_height)),
|
VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(x_ofs, y_ofs), Size2(1, caret_height)), cursor_color);
|
||||||
cursor_color);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
x_ofs += char_width;
|
x_ofs += char_width;
|
||||||
char_ofs++;
|
char_ofs++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (char_ofs == cursor_pos) {
|
||||||
|
if (ime_text.length() > 0) {
|
||||||
|
int ofs = 0;
|
||||||
|
while (true) {
|
||||||
|
if (ofs >= ime_text.length())
|
||||||
|
break;
|
||||||
|
|
||||||
|
CharType cchar = (pass && !text.empty()) ? '*' : ime_text[ofs];
|
||||||
|
CharType next = (pass && !text.empty()) ? '*' : ime_text[ofs + 1];
|
||||||
|
int im_char_width = font->get_char_size(cchar, next).width;
|
||||||
|
|
||||||
|
if ((x_ofs + im_char_width) > ofs_max)
|
||||||
|
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(x_ofs, y_ofs + caret_height), Size2(im_char_width, 3)), font_color);
|
||||||
|
} else {
|
||||||
|
VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(x_ofs, y_ofs + caret_height), Size2(im_char_width, 1)), font_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
font->draw_char(ci, Point2(x_ofs, y_ofs + font_ascent), cchar, next, font_color);
|
||||||
|
|
||||||
|
x_ofs += im_char_width;
|
||||||
|
ofs++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (char_ofs == cursor_pos && draw_caret) { //may be at the end
|
if (char_ofs == cursor_pos && draw_caret) { //may be at the end
|
||||||
VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(
|
if (ime_text.length() == 0) {
|
||||||
Point2(x_ofs, y_ofs), Size2(1, caret_height)),
|
VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(x_ofs, y_ofs), Size2(1, caret_height)), cursor_color);
|
||||||
cursor_color);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (has_focus()) {
|
if (has_focus()) {
|
||||||
|
|
||||||
OS::get_singleton()->set_ime_position(get_global_position() + Point2(x_ofs, y_ofs + caret_height));
|
OS::get_singleton()->set_ime_position(get_global_position() + Point2(x_ofs, y_ofs + caret_height));
|
||||||
|
OS::get_singleton()->set_ime_intermediate_text_callback(_ime_text_callback, this);
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
case NOTIFICATION_FOCUS_ENTER: {
|
case NOTIFICATION_FOCUS_ENTER: {
|
||||||
@ -685,6 +744,7 @@ void LineEdit::_notification(int p_what) {
|
|||||||
|
|
||||||
Point2 cursor_pos = Point2(get_cursor_pos(), 1) * get_minimum_size().height;
|
Point2 cursor_pos = Point2(get_cursor_pos(), 1) * get_minimum_size().height;
|
||||||
OS::get_singleton()->set_ime_position(get_global_position() + cursor_pos);
|
OS::get_singleton()->set_ime_position(get_global_position() + cursor_pos);
|
||||||
|
OS::get_singleton()->set_ime_intermediate_text_callback(_ime_text_callback, this);
|
||||||
|
|
||||||
if (OS::get_singleton()->has_virtual_keyboard())
|
if (OS::get_singleton()->has_virtual_keyboard())
|
||||||
OS::get_singleton()->show_virtual_keyboard(text, get_global_rect());
|
OS::get_singleton()->show_virtual_keyboard(text, get_global_rect());
|
||||||
@ -693,6 +753,9 @@ void LineEdit::_notification(int p_what) {
|
|||||||
case NOTIFICATION_FOCUS_EXIT: {
|
case NOTIFICATION_FOCUS_EXIT: {
|
||||||
|
|
||||||
OS::get_singleton()->set_ime_position(Point2());
|
OS::get_singleton()->set_ime_position(Point2());
|
||||||
|
OS::get_singleton()->set_ime_intermediate_text_callback(NULL, NULL);
|
||||||
|
ime_text = "";
|
||||||
|
ime_selection = Point2();
|
||||||
|
|
||||||
if (OS::get_singleton()->has_virtual_keyboard())
|
if (OS::get_singleton()->has_virtual_keyboard())
|
||||||
OS::get_singleton()->hide_virtual_keyboard();
|
OS::get_singleton()->hide_virtual_keyboard();
|
||||||
@ -1231,6 +1294,13 @@ bool LineEdit::get_expand_to_text_length() const {
|
|||||||
return expand_to_text_length;
|
return expand_to_text_length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LineEdit::_ime_text_callback(void *p_self, String p_text, Point2 p_selection) {
|
||||||
|
LineEdit *self = (LineEdit *)p_self;
|
||||||
|
self->ime_text = p_text;
|
||||||
|
self->ime_selection = p_selection;
|
||||||
|
self->update();
|
||||||
|
}
|
||||||
|
|
||||||
void LineEdit::_text_changed() {
|
void LineEdit::_text_changed() {
|
||||||
|
|
||||||
if (expand_to_text_length)
|
if (expand_to_text_length)
|
||||||
|
@ -70,6 +70,8 @@ private:
|
|||||||
String text;
|
String text;
|
||||||
String placeholder;
|
String placeholder;
|
||||||
float placeholder_alpha;
|
float placeholder_alpha;
|
||||||
|
String ime_text;
|
||||||
|
Point2 ime_selection;
|
||||||
|
|
||||||
PopupMenu *menu;
|
PopupMenu *menu;
|
||||||
|
|
||||||
@ -92,6 +94,7 @@ private:
|
|||||||
|
|
||||||
Timer *caret_blink_timer;
|
Timer *caret_blink_timer;
|
||||||
|
|
||||||
|
static void _ime_text_callback(void *p_self, String p_text, Point2 p_selection);
|
||||||
void _text_changed();
|
void _text_changed();
|
||||||
bool expand_to_text_length;
|
bool expand_to_text_length;
|
||||||
|
|
||||||
|
@ -987,6 +987,33 @@ void TextEdit::_notification(int p_what) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int caret_w = (str[j] == '\t') ? cache.font->get_char_size(' ').width : char_w;
|
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;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
cache.font->draw_char(ci, Point2(char_ofs + char_margin, ofs_y + ascent), cchar, next, color);
|
||||||
|
|
||||||
|
char_ofs += im_char_width;
|
||||||
|
ofs++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ime_text.length() == 0) {
|
||||||
if (draw_caret) {
|
if (draw_caret) {
|
||||||
if (insert_mode) {
|
if (insert_mode) {
|
||||||
int caret_h = (block_caret) ? 4 : 1;
|
int caret_h = (block_caret) ? 4 : 1;
|
||||||
@ -997,6 +1024,7 @@ void TextEdit::_notification(int p_what) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (cursor.column == j && cursor.line == line && block_caret && draw_caret && !insert_mode) {
|
if (cursor.column == j && cursor.line == line && block_caret && draw_caret && !insert_mode) {
|
||||||
color = cache.caret_background_color;
|
color = cache.caret_background_color;
|
||||||
@ -1024,7 +1052,33 @@ void TextEdit::_notification(int p_what) {
|
|||||||
if (insert_mode) {
|
if (insert_mode) {
|
||||||
cursor_pos.y += (get_row_height() - 3);
|
cursor_pos.y += (get_row_height() - 3);
|
||||||
}
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
cache.font->draw_char(ci, Point2(char_ofs + char_margin, ofs_y + ascent), cchar, next, color);
|
||||||
|
|
||||||
|
char_ofs += im_char_width;
|
||||||
|
ofs++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ime_text.length() == 0) {
|
||||||
if (draw_caret) {
|
if (draw_caret) {
|
||||||
if (insert_mode) {
|
if (insert_mode) {
|
||||||
int char_w = cache.font->get_char_size(' ').width;
|
int char_w = cache.font->get_char_size(' ').width;
|
||||||
@ -1038,6 +1092,7 @@ void TextEdit::_notification(int p_what) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (line_length_guideline) {
|
if (line_length_guideline) {
|
||||||
int x = xmargin_beg + cache.font->get_char_size('0').width * line_length_guideline_col - cursor.x_ofs;
|
int x = xmargin_beg + cache.font->get_char_size('0').width * line_length_guideline_col - cursor.x_ofs;
|
||||||
@ -1197,6 +1252,7 @@ void TextEdit::_notification(int p_what) {
|
|||||||
|
|
||||||
if (has_focus()) {
|
if (has_focus()) {
|
||||||
OS::get_singleton()->set_ime_position(get_global_position() + cursor_pos + Point2(0, get_row_height()));
|
OS::get_singleton()->set_ime_position(get_global_position() + cursor_pos + Point2(0, get_row_height()));
|
||||||
|
OS::get_singleton()->set_ime_intermediate_text_callback(_ime_text_callback, this);
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
case NOTIFICATION_FOCUS_ENTER: {
|
case NOTIFICATION_FOCUS_ENTER: {
|
||||||
@ -1207,6 +1263,7 @@ void TextEdit::_notification(int p_what) {
|
|||||||
|
|
||||||
Point2 cursor_pos = Point2(cursor_get_column(), cursor_get_line()) * get_row_height();
|
Point2 cursor_pos = Point2(cursor_get_column(), cursor_get_line()) * get_row_height();
|
||||||
OS::get_singleton()->set_ime_position(get_global_position() + cursor_pos);
|
OS::get_singleton()->set_ime_position(get_global_position() + cursor_pos);
|
||||||
|
OS::get_singleton()->set_ime_intermediate_text_callback(_ime_text_callback, this);
|
||||||
|
|
||||||
if (OS::get_singleton()->has_virtual_keyboard())
|
if (OS::get_singleton()->has_virtual_keyboard())
|
||||||
OS::get_singleton()->show_virtual_keyboard(get_text(), get_global_rect());
|
OS::get_singleton()->show_virtual_keyboard(get_text(), get_global_rect());
|
||||||
@ -1218,6 +1275,9 @@ void TextEdit::_notification(int p_what) {
|
|||||||
case NOTIFICATION_FOCUS_EXIT: {
|
case NOTIFICATION_FOCUS_EXIT: {
|
||||||
|
|
||||||
OS::get_singleton()->set_ime_position(Point2());
|
OS::get_singleton()->set_ime_position(Point2());
|
||||||
|
OS::get_singleton()->set_ime_intermediate_text_callback(NULL, NULL);
|
||||||
|
ime_text = "";
|
||||||
|
ime_selection = Point2();
|
||||||
|
|
||||||
if (OS::get_singleton()->has_virtual_keyboard())
|
if (OS::get_singleton()->has_virtual_keyboard())
|
||||||
OS::get_singleton()->hide_virtual_keyboard();
|
OS::get_singleton()->hide_virtual_keyboard();
|
||||||
@ -1228,6 +1288,13 @@ void TextEdit::_notification(int p_what) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TextEdit::_ime_text_callback(void *p_self, String p_text, Point2 p_selection) {
|
||||||
|
TextEdit *self = (TextEdit *)p_self;
|
||||||
|
self->ime_text = p_text;
|
||||||
|
self->ime_selection = p_selection;
|
||||||
|
self->update();
|
||||||
|
}
|
||||||
|
|
||||||
void TextEdit::_consume_pair_symbol(CharType ch) {
|
void TextEdit::_consume_pair_symbol(CharType ch) {
|
||||||
|
|
||||||
int cursor_position_to_move = cursor_get_column() + 1;
|
int cursor_position_to_move = cursor_get_column() + 1;
|
||||||
|
@ -184,6 +184,9 @@ class TextEdit : public Control {
|
|||||||
bool chain_backward;
|
bool chain_backward;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
String ime_text;
|
||||||
|
Point2 ime_selection;
|
||||||
|
|
||||||
TextOperation current_op;
|
TextOperation current_op;
|
||||||
|
|
||||||
List<TextOperation> undo_stack;
|
List<TextOperation> undo_stack;
|
||||||
@ -299,6 +302,8 @@ class TextEdit : public Control {
|
|||||||
void _scroll_lines_up();
|
void _scroll_lines_up();
|
||||||
void _scroll_lines_down();
|
void _scroll_lines_down();
|
||||||
|
|
||||||
|
static void _ime_text_callback(void *p_self, String p_text, Point2 p_selection);
|
||||||
|
|
||||||
//void mouse_motion(const Point& p_pos, const Point& p_rel, int p_button_mask);
|
//void mouse_motion(const Point& p_pos, const Point& p_rel, int p_button_mask);
|
||||||
Size2 get_minimum_size() const;
|
Size2 get_minimum_size() const;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user