Mac OS text input: defined a small API that user-defined text editing widgets can use to signal marked text.

git-svn-id: file:///fltk/svn/fltk/branches/branch-1.3@9774 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
This commit is contained in:
Manolo Gouy 2012-12-24 11:45:07 +00:00
parent 3c72b20458
commit cf672dba0d
9 changed files with 134 additions and 55 deletions

View File

@ -144,6 +144,11 @@ public: // should be private!
static Fl_Window* grab_; static Fl_Window* grab_;
static int compose_state; static int compose_state;
static void call_screen_init(); // recompute screen number and dimensions static void call_screen_init(); // recompute screen number and dimensions
#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
#endif
#endif #endif
/** /**
If true then flush() will do something. If true then flush() will do something.

View File

@ -41,6 +41,7 @@ public:
Inherited destructor destroys the widget and any value associated with it. Inherited destructor destroys the widget and any value associated with it.
*/ */
Fl_Secret_Input(int X,int Y,int W,int H,const char *l = 0); Fl_Secret_Input(int X,int Y,int W,int H,const char *l = 0);
int handle(int);
}; };
#endif #endif

View File

@ -131,7 +131,8 @@ public:
static CGContextRef none_cursor_image(void); static CGContextRef none_cursor_image(void);
static void *get_carbon_function(const char *name); 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 void screen_work_area(int &X, int &Y, int &W, int &H, int n); // compute work area of a given screen
static void compose_state(int); 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
private: private:
static void relink(Fl_Window*, Fl_Window*); static void relink(Fl_Window*, Fl_Window*);
bool subwindow; bool subwindow;

View File

@ -358,6 +358,11 @@ int Fl_Input::handle_key() {
else replace(position(), del ? position()-del : mark(), else replace(position(), del ? position()-del : mark(),
Fl::event_text(), Fl::event_length()); Fl::event_text(), Fl::event_length());
} }
#ifdef __APPLE__
if (Fl::marked_text_length()) {
this->mark( this->position() - Fl::marked_text_length() );
}
#endif
return 1; return 1;
} }
@ -585,6 +590,14 @@ int Fl_Input::handle(int event) {
static int dnd_save_position, dnd_save_mark, drag_start = -1, newpos; static int dnd_save_position, dnd_save_mark, drag_start = -1, newpos;
static Fl_Widget *dnd_save_focus; static Fl_Widget *dnd_save_focus;
switch (event) { switch (event) {
#ifdef __APPLE__
case FL_UNFOCUS:
if (Fl::marked_text_length()) {
this->mark( this->position() );
Fl::reset_marked_text();
}
break;
#endif
case FL_FOCUS: case FL_FOCUS:
switch (Fl::event_key()) { switch (Fl::event_key()) {
case FL_Right: case FL_Right:
@ -799,6 +812,15 @@ Fl_Secret_Input::Fl_Secret_Input(int X,int Y,int W,int H,const char *l)
type(FL_SECRET_INPUT); type(FL_SECRET_INPUT);
} }
int Fl_Secret_Input::handle(int event) {
int retval = Fl_Input::handle(event);
#ifdef __APPLE__
if (event == FL_KEYBOARD && Fl::marked_text_length()) {
this->mark( this->position() ); // don't underline marked text
}
#endif
return retval;
}
// //
// End of "$Id$". // End of "$Id$".

View File

@ -339,8 +339,8 @@ void Fl_Input_::drawtext(int X, int Y, int W, int H) {
int offset2; int offset2;
if (pp <= e) x2 = xpos + (float)expandpos(p, pp, buf, &offset2); if (pp <= e) x2 = xpos + (float)expandpos(p, pp, buf, &offset2);
else offset2 = (int) strlen(buf); else offset2 = (int) strlen(buf);
#ifdef __APPLE__ // Mac OS: underline marked ( = selected + Fl::compose_state != 0) text #ifdef __APPLE__ // Mac OS: underline marked ( = selected + Fl::marked_text_length() != 0) text
if (Fl::compose_state) { if (Fl::marked_text_length()) {
fl_color(textcolor()); fl_color(textcolor());
} }
else else
@ -351,8 +351,8 @@ void Fl_Input_::drawtext(int X, int Y, int W, int H) {
fl_color(fl_contrast(textcolor(), selection_color())); fl_color(fl_contrast(textcolor(), selection_color()));
} }
fl_draw(buf+offset1, offset2-offset1, x1, (float)(Y+ypos+desc)); fl_draw(buf+offset1, offset2-offset1, x1, (float)(Y+ypos+desc));
#ifdef __APPLE__ // Mac OS: underline marked ( = selected + Fl::compose_state != 0) text #ifdef __APPLE__ // Mac OS: underline marked ( = selected + Fl::marked_text_length() != 0) text
if (Fl::compose_state) { if (Fl::marked_text_length()) {
fl_color( fl_color_average(textcolor(), color(), 0.6) ); fl_color( fl_color_average(textcolor(), color(), 0.6) );
float width = fl_width(buf+offset1, offset2-offset1); float width = fl_width(buf+offset1, offset2-offset1);
fl_line(x1, Y+ypos+height-1, x1+width, Y+ypos+height-1); fl_line(x1, Y+ypos+height-1, x1+width, Y+ypos+height-1);
@ -372,7 +372,11 @@ void Fl_Input_::drawtext(int X, int Y, int W, int H) {
CONTINUE2: CONTINUE2:
// draw the cursor: // draw the cursor:
if (Fl::focus() == this && (Fl::compose_state || selstart == selend) && if (Fl::focus() == this && (
#ifdef __APPLE__
Fl::marked_text_length() ||
#endif
selstart == selend) &&
position() >= p-value() && position() <= e-value()) { position() >= p-value() && position() <= e-value()) {
fl_color(cursor_color()); fl_color(cursor_color());
// cursor position may need to be recomputed (see STR #2486) // cursor position may need to be recomputed (see STR #2486)

View File

@ -1942,7 +1942,7 @@ void Fl_Text_Display::draw_string(int style,
if (style & PRIMARY_MASK) { if (style & PRIMARY_MASK) {
if (Fl::focus() == (Fl_Widget*)this) { if (Fl::focus() == (Fl_Widget*)this) {
#ifdef __APPLE__ #ifdef __APPLE__
if (Fl::compose_state) background = color();// Mac OS: underline marked text if (Fl::marked_text_length()) background = color();// Mac OS: underline marked text
else else
#endif #endif
background = selection_color(); background = selection_color();
@ -1978,8 +1978,8 @@ void Fl_Text_Display::draw_string(int style,
fl_push_clip(X, Y, toX - X, mMaxsize); fl_push_clip(X, Y, toX - X, mMaxsize);
#endif #endif
fl_draw( string, nChars, X, Y + mMaxsize - fl_descent()); fl_draw( string, nChars, X, Y + mMaxsize - fl_descent());
#ifdef __APPLE__ // Mac OS: underline marked (= selected + Fl::compose_state != 0) text #ifdef __APPLE__ // Mac OS: underline marked (= selected + Fl::marked_text_length() != 0) text
if (Fl::compose_state && (style & PRIMARY_MASK)) { if (Fl::marked_text_length() && (style & PRIMARY_MASK)) {
fl_color( fl_color_average(foreground, background, 0.6) ); fl_color( fl_color_average(foreground, background, 0.6) );
fl_line(X, Y + mMaxsize - 1, X + fl_width(string, nChars), Y + mMaxsize - 1); fl_line(X, Y + mMaxsize - 1, X + fl_width(string, nChars), Y + mMaxsize - 1);
} }
@ -3469,7 +3469,11 @@ void Fl_Text_Display::draw(void) {
// draw the text cursor // draw the text cursor
if (damage() & (FL_DAMAGE_ALL | FL_DAMAGE_SCROLL | FL_DAMAGE_EXPOSE) if (damage() & (FL_DAMAGE_ALL | FL_DAMAGE_SCROLL | FL_DAMAGE_EXPOSE)
&& (Fl::compose_state || !buffer()->primary_selection()->selected()) && && (
#ifdef __APPLE__
Fl::marked_text_length() ||
#endif
!buffer()->primary_selection()->selected()) &&
mCursorOn && Fl::focus() == (Fl_Widget*)this ) { mCursorOn && Fl::focus() == (Fl_Widget*)this ) {
fl_push_clip(text_area.x-LEFT_MARGIN, fl_push_clip(text_area.x-LEFT_MARGIN,
text_area.y, text_area.y,

View File

@ -524,6 +524,16 @@ int Fl_Text_Editor::handle_key() {
if (insert_mode()) insert(Fl::event_text()); if (insert_mode()) insert(Fl::event_text());
else overstrike(Fl::event_text()); else overstrike(Fl::event_text());
} }
#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(); show_insert_position();
set_changed(); set_changed();
if (when()&FL_WHEN_CHANGED) do_callback(); if (when()&FL_WHEN_CHANGED) do_callback();
@ -561,6 +571,13 @@ int Fl_Text_Editor::handle(int event) {
case FL_UNFOCUS: case FL_UNFOCUS:
show_cursor(mCursorOn); // redraws the cursor show_cursor(mCursorOn); // redraws the cursor
#ifdef __APPLE__
if (buffer()->selected() && Fl::marked_text_length()) {
int pos = insert_position();
buffer()->select(pos, pos);
Fl::reset_marked_text();
}
#endif
if (buffer()->selected()) redraw(); // Redraw selections... if (buffer()->selected()) redraw(); // Redraw selections...
case FL_HIDE: case FL_HIDE:
if (when() & FL_WHEN_RELEASE) maybe_do_callback(); if (when() & FL_WHEN_RELEASE) maybe_do_callback();

View File

@ -1929,15 +1929,15 @@ static void q_set_window_title(NSWindow *nsw, const char * name, const char *mi
} else { } else {
received = (NSString*)aString; received = (NSString*)aString;
} }
//NSLog(@"insertText: received=%@ Fl::compose_state%d",received,Fl::compose_state); //NSLog(@"insertText: received=%@ Fl::marked_text_length()%d",received,Fl::marked_text_length());
fl_lock_function(); fl_lock_function();
[FLView prepareEtext:received]; [FLView prepareEtext:received];
// We can get called outside of key events (e.g., from the character palette, from CJK text input). // 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. // Transform character palette actions to FL_PASTE events.
Fl_Window *target = [(FLWindow*)[self window] getFl_Window]; Fl_Window *target = [(FLWindow*)[self window] getFl_Window];
Fl::handle( (in_key_event || Fl::compose_state) ? FL_KEYBOARD : FL_PASTE, target); Fl_X::next_marked_length = 0;
Fl_X::compose_state(0); Fl::handle( (in_key_event || Fl::marked_text_length()) ? FL_KEYBOARD : FL_PASTE, target);
// for some reason, with the palette, the window does not redraw until the next mouse move or button push // 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! // sending a 'redraw()' or 'awake()' does not solve the issue!
@ -1956,18 +1956,18 @@ static void q_set_window_title(NSWindow *nsw, const char * name, const char *mi
// This code creates the OS X behaviour of seeing dead keys as things // This code creates the OS X behaviour of seeing dead keys as things
// are being composed. // are being composed.
[FLView prepareEtext:received]; [FLView prepareEtext:received];
/*NSLog(@"setMarkedText:%@ %d %d Fl::compose_state=%d Fl::e_length=%d", /*NSLog(@"setMarkedText:%@ %d %d Fl::marked_text_length()=%d Fl::e_length=%d",
received, newSelection.location, newSelection.length, Fl::compose_state, Fl::e_length);*/ received, newSelection.location, newSelection.length, Fl::marked_text_length(), Fl::e_length);*/
Fl_Window *target = [(FLWindow*)[self window] getFl_Window]; Fl_Window *target = [(FLWindow*)[self window] getFl_Window];
Fl_X::next_marked_length = Fl::e_length;
Fl::handle(FL_KEYBOARD, target); Fl::handle(FL_KEYBOARD, target);
Fl_X::compose_state(Fl::e_length);
fl_unlock_function(); fl_unlock_function();
} }
- (void)unmarkText { - (void)unmarkText {
fl_lock_function(); fl_lock_function();
Fl_X::compose_state(0); Fl::reset_marked_text();
fl_unlock_function(); fl_unlock_function();
//NSLog(@"unmarkText"); //NSLog(@"unmarkText");
} }
@ -1977,13 +1977,13 @@ static void q_set_window_title(NSWindow *nsw, const char * name, const char *mi
} }
- (NSRange)markedRange { - (NSRange)markedRange {
//NSLog(@"markedRange=%d %d", Fl::compose_state > 0?0:NSNotFound, Fl::compose_state); //NSLog(@"markedRange=%d %d", Fl::marked_text_length() > 0?0:NSNotFound, Fl::marked_text_length());
return NSMakeRange(Fl::compose_state > 0?0:NSNotFound, Fl::compose_state); return NSMakeRange(Fl::marked_text_length() > 0?0:NSNotFound, Fl::marked_text_length());
} }
- (BOOL)hasMarkedText { - (BOOL)hasMarkedText {
//NSLog(@"hasMarkedText %s", Fl::compose_state > 0?"YES":"NO"); //NSLog(@"hasMarkedText %s", Fl::marked_text_length() > 0?"YES":"NO");
return (Fl::compose_state > 0); return (Fl::marked_text_length() > 0);
} }
- (NSAttributedString *)attributedSubstringFromRange:(NSRange)aRange { - (NSAttributedString *)attributedSubstringFromRange:(NSRange)aRange {
@ -2004,13 +2004,10 @@ static void q_set_window_title(NSWindow *nsw, const char * name, const char *mi
if (!focus) focus = wfocus; if (!focus) focus = wfocus;
glyphRect.size.width = 0; glyphRect.size.width = 0;
Fl_Text_Display *current = dynamic_cast<Fl_Text_Display*>(focus); int x, y;
if (current) { if (Fl_X::insertion_point_location(&x, &y)) {
int x, y;
current->position_to_xy( current->insert_position(), &x, &y );
glyphRect.origin.x = (CGFloat)x; glyphRect.origin.x = (CGFloat)x;
glyphRect.origin.y = (CGFloat)y + current->textsize(); glyphRect.origin.y = (CGFloat)y;
glyphRect.size.height = current->textsize();
} else { } else {
if (focus->as_window()) { if (focus->as_window()) {
glyphRect.origin.x = 0; glyphRect.origin.x = 0;
@ -2020,8 +2017,8 @@ static void q_set_window_title(NSWindow *nsw, const char * name, const char *mi
glyphRect.origin.x = focus->x(); glyphRect.origin.x = focus->x();
glyphRect.origin.y = focus->y() + focus->h(); glyphRect.origin.y = focus->y() + focus->h();
} }
glyphRect.size.height = 12;
} }
glyphRect.size.height = 12;
Fl_Window *win = focus->as_window(); Fl_Window *win = focus->as_window();
if (!win) win = focus->window(); if (!win) win = focus->window();
while (win != NULL && win != wfocus) { while (win != NULL && win != wfocus) {
@ -2046,25 +2043,6 @@ static void q_set_window_title(NSWindow *nsw, const char * name, const char *mi
@end @end
void Fl_X::compose_state(int new_val)
{ // select marked text in text widgets
if (Fl::compose_state == 0 && new_val == 0) return;
Fl::compose_state = new_val;
Fl_Widget *widget = Fl::focus();
if (!widget) return;
Fl_Input_* input = dynamic_cast<Fl_Input_*>(widget);
Fl_Text_Display* text;
if (input) {
if ( ! dynamic_cast<Fl_Secret_Input*>(input) )
input->mark( input->position() - Fl::compose_state );
}
else if ( (text = dynamic_cast<Fl_Text_Display*>(widget)) ) {
int pos = text->insert_position();
text->buffer()->select(pos - Fl::compose_state, pos);
}
}
void Fl_Window::fullscreen_x() { void Fl_Window::fullscreen_x() {
_set_fullscreen(); _set_fullscreen();
/* On OS X < 10.6, it is necessary to recreate the window. This is done /* On OS X < 10.6, it is necessary to recreate the window. This is done

View File

@ -21,6 +21,9 @@
#ifndef FL_DOXYGEN #ifndef FL_DOXYGEN
int Fl::compose_state = 0; int Fl::compose_state = 0;
#ifdef __APPLE__
int Fl_X::next_marked_length = 0;
#endif
#endif #endif
#if !defined(WIN32) && !defined(__APPLE__) #if !defined(WIN32) && !defined(__APPLE__)
@ -41,6 +44,23 @@ extern XIC fl_xim_ic;
keys, and del is set to zero. You could insert the text anyways, if keys, and del is set to zero. You could insert the text anyways, if
you don't know what else to do. 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
<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.
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.
<p>Though the current implementation returns immediately, future <p>Though the current implementation returns immediately, future
versions may take quite awhile, as they may pop up a window or do versions may take quite awhile, as they may pop up a window or do
other user-interface things to allow characters to be selected. other user-interface things to allow characters to be selected.
@ -59,11 +79,13 @@ unsigned char ascii = (unsigned char)e_text[0];
condition = (e_state & (FL_ALT | FL_META)) && !(ascii & 128) ; condition = (e_state & (FL_ALT | FL_META)) && !(ascii & 128) ;
#else #else
condition = (e_state & (FL_ALT | FL_META | FL_CTRL)) && !(ascii & 128) ; condition = (e_state & (FL_ALT | FL_META | FL_CTRL)) && !(ascii & 128) ;
#endif #endif // WIN32
#endif #endif // __APPLE__
if (condition) { del = 0; return 0;} // this stuff is to be treated as a function key if (condition) { del = 0; return 0;} // this stuff is to be treated as a function key
del = Fl::compose_state; del = Fl::compose_state;
#ifndef __APPLE__ #ifdef __APPLE__
Fl::compose_state = Fl_X::next_marked_length;
#else
Fl::compose_state = 0; Fl::compose_state = 0;
// Only insert non-control characters: // Only insert non-control characters:
if ( (!Fl::compose_state) && ! (ascii & ~31 && ascii!=127)) { return 0; } if ( (!Fl::compose_state) && ! (ascii & ~31 && ascii!=127)) { return 0; }
@ -71,6 +93,35 @@ unsigned char ascii = (unsigned char)e_text[0];
return 1; return 1;
} }
#ifdef __APPLE__
int Fl::marked_text_length() {
return (Fl::compose_state ? Fl::compose_state : Fl_X::next_marked_length);
}
static int insertion_point_x = 0;
static int insertion_point_y = 0;
static bool insertion_point_location_is_valid = false;
void Fl::reset_marked_text() {
Fl::compose_state = 0;
Fl_X::next_marked_length = 0;
insertion_point_location_is_valid = false;
}
int Fl_X::insertion_point_location(int *px, int *py)
// 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;
return true;
}
void Fl::insertion_point_location(int x, int y) {
insertion_point_location_is_valid = true;
insertion_point_x = x;
insertion_point_y = y;
}
#endif // __APPLE__
/** /**
If the user moves the cursor, be sure to call Fl::compose_reset(). If the user moves the cursor, be sure to call Fl::compose_reset().
The next call to Fl::compose() will start out in an initial state. In The next call to Fl::compose() will start out in an initial state. In
@ -79,14 +130,10 @@ unsigned char ascii = (unsigned char)e_text[0];
*/ */
void Fl::compose_reset() void Fl::compose_reset()
{ {
#ifdef __APPLE__
Fl_X::compose_state(0);
#else
Fl::compose_state = 0; Fl::compose_state = 0;
#if !defined(WIN32) #if !defined(WIN32) && !defined(__APPLE__)
if (fl_xim_ic) XmbResetIC(fl_xim_ic); if (fl_xim_ic) XmbResetIC(fl_xim_ic);
#endif #endif
#endif
} }
// //