Mac OS: added support for the text input feature introduced in OS 10.7 "Lion" where pressing and holding
some key opens a window with possible accented characters. This feature is used by the Fl_Input_ and Fl_Text_Editor widgets. User-defined text input widgets can optionally use this feature, but the default behavior is to not use it. Fl_Secret_Input turns it off, for example. git-svn-id: file:///fltk/svn/fltk/branches/branch-1.3@9792 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
This commit is contained in:
parent
e042966ae7
commit
482c4a5e0a
2
FL/Fl.H
2
FL/Fl.H
@ -147,7 +147,7 @@ public: // should be private!
|
||||
#ifdef __APPLE__
|
||||
static int marked_text_length(void); // returns length of marked text
|
||||
static void reset_marked_text(); // resets marked text
|
||||
static void insertion_point_location(int x, int y); // sets window coordinates of insertion point
|
||||
static void insertion_point_location(int x, int y, int height); // sets window coordinates & height of insertion point
|
||||
#endif
|
||||
#endif
|
||||
/**
|
||||
|
@ -171,6 +171,7 @@ protected:
|
||||
GROUP_RELATIVE = 1<<16, ///< position this widget relative to the parent group, not to the window
|
||||
COPIED_TOOLTIP = 1<<17, ///< the widget tooltip is internally copied, its destruction is handled by the widget
|
||||
FULLSCREEN = 1<<18, ///< a fullscreen window (Fl_Window)
|
||||
MAC_USE_ACCENTS_MENU = 1<<19, ///< On the Mac OS platform, pressing and holding a key on the keyboard opens an accented-character menu window (Fl_Input_, Fl_Text_Editor)
|
||||
// (space for more flags)
|
||||
USERFLAG3 = 1<<29, ///< reserved for 3rd party extensions
|
||||
USERFLAG2 = 1<<30, ///< reserved for 3rd party extensions
|
||||
@ -976,6 +977,10 @@ public:
|
||||
*/
|
||||
virtual class Fl_Gl_Window* as_gl_window() {return 0;}
|
||||
|
||||
/** Returns non zero if MAC_USE_ACCENTS_MENU flag is set, 0 otherwise.
|
||||
*/
|
||||
int use_accents_menu() { return flags() & MAC_USE_ACCENTS_MENU; }
|
||||
|
||||
/** For back compatibility only.
|
||||
\deprecated Use selection_color() instead.
|
||||
*/
|
||||
|
2
FL/mac.H
2
FL/mac.H
@ -132,7 +132,7 @@ public:
|
||||
static void *get_carbon_function(const char *name);
|
||||
static void screen_work_area(int &X, int &Y, int &W, int &H, int n); // compute work area of a given screen
|
||||
static int next_marked_length; // next length of marked text after current marked text will have been replaced
|
||||
static int insertion_point_location(int *px, int *py); // computes window coordinates of insertion point
|
||||
static int insertion_point_location(int *px, int *py, int *pheight); // computes window coordinates & height of insertion point
|
||||
private:
|
||||
static void relink(Fl_Window*, Fl_Window*);
|
||||
bool subwindow;
|
||||
|
@ -780,12 +780,14 @@ Fl_Float_Input::Fl_Float_Input(int X,int Y,int W,int H,const char *l)
|
||||
: Fl_Input(X,Y,W,H,l)
|
||||
{
|
||||
type(FL_FLOAT_INPUT);
|
||||
clear_flag(MAC_USE_ACCENTS_MENU);
|
||||
}
|
||||
|
||||
|
||||
Fl_Int_Input::Fl_Int_Input(int X,int Y,int W,int H,const char *l)
|
||||
: Fl_Input(X,Y,W,H,l) {
|
||||
type(FL_INT_INPUT);
|
||||
clear_flag(MAC_USE_ACCENTS_MENU);
|
||||
}
|
||||
|
||||
|
||||
@ -810,6 +812,7 @@ Fl_Multiline_Output::Fl_Multiline_Output(int X,int Y,int W,int H,const char *l)
|
||||
Fl_Secret_Input::Fl_Secret_Input(int X,int Y,int W,int H,const char *l)
|
||||
: Fl_Input(X,Y,W,H,l) {
|
||||
type(FL_SECRET_INPUT);
|
||||
clear_flag(MAC_USE_ACCENTS_MENU);
|
||||
}
|
||||
|
||||
int Fl_Secret_Input::handle(int event) {
|
||||
|
@ -388,6 +388,9 @@ void Fl_Input_::drawtext(int X, int Y, int W, int H) {
|
||||
} else {
|
||||
fl_rectf((int)(xpos+curx+0.5), Y+ypos, 2, height);
|
||||
}
|
||||
#ifdef __APPLE__
|
||||
Fl::insertion_point_location(xpos+curx, Y+ypos+height, height);
|
||||
#endif
|
||||
}
|
||||
|
||||
CONTINUE:
|
||||
@ -1119,6 +1122,7 @@ Fl_Input_::Fl_Input_(int X, int Y, int W, int H, const char* l)
|
||||
maximum_size_ = 32767;
|
||||
shortcut_ = 0;
|
||||
set_flag(SHORTCUT_LABEL);
|
||||
set_flag(MAC_USE_ACCENTS_MENU);
|
||||
tab_nav(1);
|
||||
}
|
||||
|
||||
|
@ -2067,6 +2067,9 @@ void Fl_Text_Display::draw_cursor( int X, int Y ) {
|
||||
if ( X < text_area.x - 1 || X > text_area.x + text_area.w )
|
||||
return;
|
||||
|
||||
#ifdef __APPLE__
|
||||
Fl::insertion_point_location(X, bot, fontHeight);
|
||||
#endif
|
||||
/* For cursors other than the block, make them around 2/3 of a character
|
||||
width, rounded to an even number of pixels so that X will draw an
|
||||
odd number centered on the stem at x. */
|
||||
|
@ -73,6 +73,7 @@ Fl_Text_Editor::Fl_Text_Editor(int X, int Y, int W, int H, const char* l)
|
||||
mCursorOn = 1;
|
||||
insert_mode_ = 1;
|
||||
key_bindings = 0;
|
||||
set_flag(MAC_USE_ACCENTS_MENU);
|
||||
|
||||
// handle the default key bindings
|
||||
add_default_key_bindings(&key_bindings);
|
||||
@ -526,12 +527,8 @@ int Fl_Text_Editor::handle_key() {
|
||||
}
|
||||
#ifdef __APPLE__
|
||||
if (Fl::marked_text_length()) {
|
||||
int x, y;
|
||||
int pos = this->insert_position();
|
||||
this->buffer()->select(pos - Fl::marked_text_length(), pos);
|
||||
this->position_to_xy( this->insert_position(), &x, &y);
|
||||
y += this->textsize();
|
||||
Fl::insertion_point_location(x, y);
|
||||
}
|
||||
#endif
|
||||
show_insert_position();
|
||||
|
@ -1668,9 +1668,14 @@ static void q_set_window_title(NSWindow *nsw, const char * name, const char *mi
|
||||
}
|
||||
|
||||
|
||||
@interface FLView : NSView <NSTextInput> {
|
||||
@interface FLView : NSView <NSTextInput
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
|
||||
, NSTextInputClient
|
||||
#endif
|
||||
> {
|
||||
BOOL in_key_event;
|
||||
NSInteger identifier;
|
||||
NSRange selectedRange;
|
||||
}
|
||||
+ (void)prepareEtext:(NSString*)aString;
|
||||
- (id)init;
|
||||
@ -1949,27 +1954,34 @@ static void q_set_window_title(NSWindow *nsw, const char * name, const char *mi
|
||||
}
|
||||
|
||||
// These functions implement text input.
|
||||
// On the way to fully support CJK text input, this is the way to go.
|
||||
- (void)doCommandBySelector:(SEL)aSelector {
|
||||
}
|
||||
|
||||
- (void)insertText:(id)aString {
|
||||
[self insertText:aString replacementRange:NSMakeRange(NSNotFound, 0)];
|
||||
}
|
||||
- (void)insertText:(id)aString replacementRange:(NSRange)replacementRange {
|
||||
NSString *received;
|
||||
if ([aString isKindOfClass:[NSAttributedString class]]) {
|
||||
received = [(NSAttributedString*)aString string];
|
||||
} else {
|
||||
received = (NSString*)aString;
|
||||
}
|
||||
//NSLog(@"insertText: received=%@ Fl::marked_text_length()=%d",received,Fl::marked_text_length());
|
||||
|
||||
/*NSLog(@"insertText=%@ l=%d Fl::marked_text_length()=%d range=%d,%d",
|
||||
received,strlen([received UTF8String]),Fl::marked_text_length(),replacementRange.location,replacementRange.length);*/
|
||||
fl_lock_function();
|
||||
Fl_Window *target = [(FLWindow*)[self window] getFl_Window];
|
||||
while (replacementRange.length--) { // delete replacementRange.length characters before insertion point
|
||||
int saved_keysym = Fl::e_keysym;
|
||||
Fl::e_keysym = FL_BackSpace;
|
||||
Fl::handle(FL_KEYBOARD, target);
|
||||
Fl::e_keysym = saved_keysym;
|
||||
}
|
||||
[FLView prepareEtext:received];
|
||||
// We can get called outside of key events (e.g., from the character palette, from CJK text input).
|
||||
// Transform character palette actions to FL_PASTE events.
|
||||
Fl_Window *target = [(FLWindow*)[self window] getFl_Window];
|
||||
Fl_X::next_marked_length = 0;
|
||||
Fl::handle( (in_key_event || Fl::marked_text_length()) ? FL_KEYBOARD : FL_PASTE, target);
|
||||
|
||||
selectedRange = NSMakeRange(100, 0); // 100 is an arbitrary value
|
||||
// for some reason, with the palette, the window does not redraw until the next mouse move or button push
|
||||
// sending a 'redraw()' or 'awake()' does not solve the issue!
|
||||
Fl::flush();
|
||||
@ -1977,6 +1989,10 @@ static void q_set_window_title(NSWindow *nsw, const char * name, const char *mi
|
||||
}
|
||||
|
||||
- (void)setMarkedText:(id)aString selectedRange:(NSRange)newSelection {
|
||||
[self setMarkedText:aString selectedRange:newSelection replacementRange:NSMakeRange(NSNotFound, 0)];
|
||||
}
|
||||
|
||||
- (void)setMarkedText:(id)aString selectedRange:(NSRange)newSelection replacementRange:(NSRange)replacementRange {
|
||||
NSString *received;
|
||||
if ([aString isKindOfClass:[NSAttributedString class]]) {
|
||||
received = [(NSAttributedString*)aString string];
|
||||
@ -1984,15 +2000,21 @@ static void q_set_window_title(NSWindow *nsw, const char * name, const char *mi
|
||||
received = (NSString*)aString;
|
||||
}
|
||||
fl_lock_function();
|
||||
// This code creates the OS X behaviour of seeing dead keys as things
|
||||
// are being composed.
|
||||
[FLView prepareEtext:received];
|
||||
/*NSLog(@"setMarkedText:%@ %d %d Fl::marked_text_length()=%d Fl::e_length=%d",
|
||||
received, newSelection.location, newSelection.length, Fl::marked_text_length(), Fl::e_length);*/
|
||||
/*NSLog(@"setMarkedText:%@ l=%d newSelection=%d,%d Fl::marked_text_length()=%d replacement=%d,%d",
|
||||
received, strlen([received UTF8String]), newSelection.location, newSelection.length, Fl::marked_text_length(),
|
||||
replacementRange.location, replacementRange.length);*/
|
||||
Fl_Window *target = [(FLWindow*)[self window] getFl_Window];
|
||||
while (replacementRange.length--) { // delete replacementRange.length characters before insertion point
|
||||
Fl::e_keysym = FL_BackSpace;
|
||||
Fl::compose_state = 0;
|
||||
Fl_X::next_marked_length = 0;
|
||||
Fl::handle(FL_KEYBOARD, target);
|
||||
Fl::e_keysym = 'a'; // pretend a letter key was hit
|
||||
}
|
||||
[FLView prepareEtext:received];
|
||||
Fl_X::next_marked_length = Fl::e_length;
|
||||
Fl::handle(FL_KEYBOARD, target);
|
||||
|
||||
selectedRange = NSMakeRange(100, newSelection.length);
|
||||
fl_unlock_function();
|
||||
}
|
||||
|
||||
@ -2004,6 +2026,8 @@ static void q_set_window_title(NSWindow *nsw, const char * name, const char *mi
|
||||
}
|
||||
|
||||
- (NSRange)selectedRange {
|
||||
Fl_Widget *w = Fl::focus();
|
||||
if (w && w->use_accents_menu()) return selectedRange;
|
||||
return NSMakeRange(NSNotFound, 0);
|
||||
}
|
||||
|
||||
@ -2018,6 +2042,9 @@ static void q_set_window_title(NSWindow *nsw, const char * name, const char *mi
|
||||
}
|
||||
|
||||
- (NSAttributedString *)attributedSubstringFromRange:(NSRange)aRange {
|
||||
return [self attributedSubstringForProposedRange:aRange actualRange:NULL];
|
||||
}
|
||||
- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange {
|
||||
//NSLog(@"attributedSubstringFromRange: %d %d",aRange.location,aRange.length);
|
||||
return nil;
|
||||
}
|
||||
@ -2027,7 +2054,10 @@ static void q_set_window_title(NSWindow *nsw, const char * name, const char *mi
|
||||
}
|
||||
|
||||
- (NSRect)firstRectForCharacterRange:(NSRange)aRange {
|
||||
//NSLog(@"firstRectForCharacterRange %d %d",aRange.location, aRange.length);
|
||||
return [self firstRectForCharacterRange:aRange actualRange:NULL];
|
||||
}
|
||||
- (NSRect)firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange {
|
||||
//NSLog(@"firstRectForCharacterRange %d %d actualRange=%p",aRange.location, aRange.length,actualRange);
|
||||
NSRect glyphRect;
|
||||
fl_lock_function();
|
||||
Fl_Widget *focus = Fl::focus();
|
||||
@ -2035,8 +2065,8 @@ static void q_set_window_title(NSWindow *nsw, const char * name, const char *mi
|
||||
if (!focus) focus = wfocus;
|
||||
glyphRect.size.width = 0;
|
||||
|
||||
int x, y;
|
||||
if (Fl_X::insertion_point_location(&x, &y)) {
|
||||
int x, y, height;
|
||||
if (Fl_X::insertion_point_location(&x, &y, &height)) {
|
||||
glyphRect.origin.x = (CGFloat)x;
|
||||
glyphRect.origin.y = (CGFloat)y;
|
||||
} else {
|
||||
@ -2048,8 +2078,9 @@ static void q_set_window_title(NSWindow *nsw, const char * name, const char *mi
|
||||
glyphRect.origin.x = focus->x();
|
||||
glyphRect.origin.y = focus->y() + focus->h();
|
||||
}
|
||||
height = 12;
|
||||
}
|
||||
glyphRect.size.height = 12;
|
||||
glyphRect.size.height = height;
|
||||
Fl_Window *win = focus->as_window();
|
||||
if (!win) win = focus->window();
|
||||
while (win != NULL && win != wfocus) {
|
||||
@ -2060,6 +2091,7 @@ static void q_set_window_title(NSWindow *nsw, const char * name, const char *mi
|
||||
// Convert the rect to screen coordinates
|
||||
glyphRect.origin.y = wfocus->h() - glyphRect.origin.y;
|
||||
glyphRect.origin = [[self window] convertBaseToScreen:glyphRect.origin];
|
||||
if (actualRange) *actualRange = aRange;
|
||||
fl_unlock_function();
|
||||
return glyphRect;
|
||||
}
|
||||
|
@ -16,6 +16,12 @@
|
||||
// http://www.fltk.org/str.php
|
||||
//
|
||||
|
||||
/**
|
||||
\file Fl_compose.cxx
|
||||
Utility functions to support text input.
|
||||
*/
|
||||
|
||||
|
||||
#include <FL/Fl.H>
|
||||
#include <FL/x.H>
|
||||
|
||||
@ -44,22 +50,27 @@ extern XIC fl_xim_ic;
|
||||
keys, and del is set to zero. You could insert the text anyways, if
|
||||
you don't know what else to do.
|
||||
|
||||
<p>On the Mac OS platform, text editing widgets should preferentially signal
|
||||
marked text, that is, temporary text replaced by other text during the text
|
||||
input process. Such signaling is usually done underlining marked text. Widgets can call
|
||||
<p>On the Mac OS platform, text input can involve marked text, that is,
|
||||
temporary text replaced by other text during the input process. This occurs,
|
||||
e.g., when using dead keys or when entering CJK characters.
|
||||
Text editing widgets should preferentially signal
|
||||
marked text, usually underlining it. Widgets can call
|
||||
<tt>int Fl::marked_text_length()</tt> <i>after</i> having called Fl::compose(int&)
|
||||
to obtain the length in bytes of marked text that always finishes at the
|
||||
current insertion point. It's the widget's task to underline marked text.
|
||||
Widgets should also call <tt>void Fl::reset_marked_text()</tt> when processing FL_UNFOCUS events.
|
||||
Optionally, widgets can also call
|
||||
<tt>void Fl::insertion_point_location(int x, int y)</tt> to indicate the window
|
||||
coordinates of the bottom of the current insertion point.
|
||||
Widgets should also call <tt>void Fl::reset_marked_text()</tt> when processing FL_UNFOCUS
|
||||
events. Optionally, widgets can also call
|
||||
<tt>void Fl::insertion_point_location(int x, int y, int height)</tt> to indicate the window
|
||||
coordinates of the bottom of the current insertion point and the line height.
|
||||
This way, auxiliary windows that help choosing among alternative characters
|
||||
appear just below the insertion point. If widgets don't do that,
|
||||
auxiliary windows appear at the widget's bottom. The
|
||||
Fl_Input and Fl_Text_Editor widgets signal marked text underlining it.
|
||||
If none of this is done by a user-defined text editing widget, complex
|
||||
(e.g., CJK) text input will work, but will not signal to the user what text is marked.
|
||||
Fl_Input and Fl_Text_Editor widgets underline marked text.
|
||||
If none of this is done by a user-defined text editing widget,
|
||||
text input will work, but will not signal to the user what text is marked.
|
||||
Finally, text editing widgets should call <tt>set_flag(MAC_USE_ACCENTS_MENU);</tt>
|
||||
in their constructor if they want to use the feature introduced with Mac OS 10.7 "Lion"
|
||||
where pressing and holding a key on the keyboard opens an accented-character menu window.
|
||||
|
||||
<p>Though the current implementation returns immediately, future
|
||||
versions may take quite awhile, as they may pop up a window or do
|
||||
@ -100,6 +111,7 @@ int Fl::marked_text_length() {
|
||||
|
||||
static int insertion_point_x = 0;
|
||||
static int insertion_point_y = 0;
|
||||
static int insertion_point_height = 0;
|
||||
static bool insertion_point_location_is_valid = false;
|
||||
|
||||
void Fl::reset_marked_text() {
|
||||
@ -107,18 +119,20 @@ void Fl::reset_marked_text() {
|
||||
Fl_X::next_marked_length = 0;
|
||||
insertion_point_location_is_valid = false;
|
||||
}
|
||||
int Fl_X::insertion_point_location(int *px, int *py)
|
||||
int Fl_X::insertion_point_location(int *px, int *py, int *pheight)
|
||||
// return true if the current coordinates of the insertion point are available
|
||||
{
|
||||
if ( ! insertion_point_location_is_valid ) return false;
|
||||
*px = insertion_point_x;
|
||||
*py = insertion_point_y;
|
||||
*pheight = insertion_point_height;
|
||||
return true;
|
||||
}
|
||||
void Fl::insertion_point_location(int x, int y) {
|
||||
void Fl::insertion_point_location(int x, int y, int height) {
|
||||
insertion_point_location_is_valid = true;
|
||||
insertion_point_x = x;
|
||||
insertion_point_y = y;
|
||||
insertion_point_height = height;
|
||||
}
|
||||
#endif // __APPLE__
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user