GNOME Bugzilla – Bug 592405
Enhance GtkTextView to support fine-grained control of its behaviors to user actions.
Last modified: 2018-02-10 03:24:36 UTC
Right now GtkTextView provides several signals and virtual functions to help customer controls its behaviors. For example, "backspace" signal can be intercepted to adjust the behavior of GtkTextView when user presses Backspace key. But such facilities provided by GtkTextView are far from enough. For exapmle, some applications may want to adjust the behavior of GtkTextView when user presses Enter and Tab key. GtkSourceView is a very good example for it. For now, application must intercept "key-press-event" to achieve such goal, but it causes many problems, mainly related to im context and key bindings. You may refer to GtkSourceView's source code, especially the gtk_source_view_key_press_event() function, to see how tricky it is. I think Mac OSX's NSTextView would be very good example in this area, which provides much more action messages to help customize its behavior, see: http://developer.apple.com/documentation/Cocoa/Conceptual/TextEditing/TextEditing.html http://developer.apple.com/documentation/Cocoa/Reference/ApplicationKit/Classes/NSResponder_Class/Reference/Reference.html For example, NSTextView supports following action messages that can be intercepted by customer: * – cancelOperation: * – capitalizeWord: * – centerSelectionInVisibleArea: * – changeCaseOfLetter: * – complete: * – deleteBackward: * – deleteBackwardByDecomposingPreviousCharacter: * – deleteForward: * – deleteToBeginningOfLine: * – deleteToBeginningOfParagraph: * – deleteToEndOfLine: * – deleteToEndOfParagraph: * – deleteToMark: * – deleteWordBackward: * – deleteWordForward: * – indent: * – insertBacktab: * – insertContainerBreak: * – insertLineBreak: * – insertNewline: * – insertNewlineIgnoringFieldEditor: * – insertParagraphSeparator: * – insertTab: * – insertTabIgnoringFieldEditor: * – insertText: * – lowercaseWord: * – moveBackward: * – moveBackwardAndModifySelection: * – moveDown: * – moveDownAndModifySelection: * – moveForward: * – moveForwardAndModifySelection: * – moveLeft: * – moveLeftAndModifySelection: * – moveRight: * – moveRightAndModifySelection: * – moveToBeginningOfDocument: * – moveToBeginningOfLine: * – moveToBeginningOfParagraph: * – moveToEndOfDocument: * – moveToEndOfLine: * – moveToEndOfParagraph: * – moveUp: * – moveUpAndModifySelection: * – moveWordBackward: * – moveWordBackwardAndModifySelection: * – moveWordForward: * – moveWordForwardAndModifySelection: * – moveWordLeft: * – moveWordRight: * – moveWordRightAndModifySelection: * – moveWordLeftAndModifySelection: * – pageDown: * – pageUp: * – scrollLineDown: * – scrollLineUp: * – scrollPageDown: * – scrollPageUp: * – selectAll: * – selectLine: * – selectParagraph: * – selectToMark: * – selectWord: * – setMark: * – showContextHelp: * – swapWithMark: * – transpose: * – transposeWords: * – uppercaseWord: * – yank: For exapmle, one can intercept insertTab message to change the behavior of Tab key press without interfering with key event handler and input method. So my proposal is to add similar action signals to GtkTextView (at least some of them) to make it much easier to control GtkTextView's behavior. IMHO, following action signals would be very useful: "insert-tab" (default binding is Tab key) "insert-back-tab" (default binding is shift-Tab key) "insert-new-line" (default binding is Enter key) "insert-line-break" (default binding is ctrl-Enter key) "cancel-operation" (default binding is Escape key) With these action signals, applications like GtkSourceView would not need to access GtkTextView's im_context anymore and the code would be simplified.
Created attachment 153494 [details] [review] new signals v1 This patch isn't finished: 1) the methods names sucks a bit 2) Also the enums names, the thing here is that in text view all makes the same. ie. control+return makes the same as return, shit+tab it makes the same as tab. 3) The doc needs to be completed.
Hmm, looking at the gtksourceview code, I don't actually see the use of C-Enter. And if we are just looking at Tab/S-Tab/Enter/S-Enter, I think I prefer the "insert-tab" "insert-back-tab" "insert-new-line" "insert-line-break" approach.
Created attachment 153529 [details] [review] new signals v2 Ok, added the 4 signals. insert-new-line and insert-line-back uses the same handler, and insert-tab and insert-back-tab uses also the same same. Hope this is ok.
(In reply to comment #3) > Created an attachment (id=153529) [details] [review] > new signals v2 +/** + * gtk_text_buffer_insert_new_line: + * @buffer: a #GtkTextBuffer + * @iter: a position in @buffer + * @interactive: whether the deletion is caused by user interaction + * @default_editable: whether the buffer is editable by default + * + * Performs the appropriate action as if the user hit the enter + * key with the cursor at the position specified by @iter. + * + * Because the buffer is modified, all outstanding iterators become + * invalid after calling this function; however, the @iter will be + * re-initialized to point to the location where text was deleted. + * + * Since: 2.20 + **/ Look like convenience functions to me. Are those commonly used in applications? + /** + * GtkTextView::insert-back-tab: + * @text_view: the object which received the signal + * + * The ::insert-back-tab signal is a + * <link linkend="keybinding-signals">keybinding signal</link> + * which gets emitted when the user asks for it. + * + * The default bindings for this signal is + * GDK_ISO_Left_Tab. + */ + signals[INSERT_BACK_TAB] = + g_signal_new (I_("insert-back-tab"), + G_OBJECT_CLASS_TYPE (gobject_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkTextViewClass, insert_back_tab), + NULL, NULL, + _gtk_marshal_VOID__VOID, + G_TYPE_NONE, 0); There is no explanation what insert-back-tab does, or how it is different from insert-tab and it looks like the implementation is indentical. What's the use case? All signals are missing Since tags.
(In reply to comment #4) > (In reply to comment #3) > > Created an attachment (id=153529) [details] [review] [details] [review] > > new signals v2 > > +/** > + * gtk_text_buffer_insert_new_line: > + * @buffer: a #GtkTextBuffer > + * @iter: a position in @buffer > + * @interactive: whether the deletion is caused by user interaction > + * @default_editable: whether the buffer is editable by default > + * > + * Performs the appropriate action as if the user hit the enter > + * key with the cursor at the position specified by @iter. > + * > + * Because the buffer is modified, all outstanding iterators become > + * invalid after calling this function; however, the @iter will be > + * re-initialized to point to the location where text was deleted. > + * > + * Since: 2.20 > + **/ > > Look like convenience functions to me. Are those commonly used in applications? Well, I just followed the case of the backspace, they could be removed easily though, mclasen told me that they were ok. > > + /** > + * GtkTextView::insert-back-tab: > + * @text_view: the object which received the signal > + * > + * The ::insert-back-tab signal is a > + * <link linkend="keybinding-signals">keybinding signal</link> > + * which gets emitted when the user asks for it. > + * > + * The default bindings for this signal is > + * GDK_ISO_Left_Tab. > + */ > + signals[INSERT_BACK_TAB] = > + g_signal_new (I_("insert-back-tab"), > + G_OBJECT_CLASS_TYPE (gobject_class), > + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, > + G_STRUCT_OFFSET (GtkTextViewClass, insert_back_tab), > + NULL, NULL, > + _gtk_marshal_VOID__VOID, > + G_TYPE_NONE, 0); > > There is no explanation what insert-back-tab does, or how it is different from > insert-tab and it looks like the implementation is indentical. What's the use > case? About this, right now textview manages that cases in the same way that's why they use the same handler. Also the reason for this is because we need to listen to this signals in gtksourceview. So to get rid to the call of textview->im_context we need this. > > All signals are missing Since tags. Sorry about the since.
(In reply to comment #5) > (In reply to comment #4) > > (In reply to comment #3) > > > Created an attachment (id=153529) [details] [review] [details] [review] [details] [review] > > > new signals v2 > > + * gtk_text_buffer_insert_new_line: > > [...] > > Look like convenience functions to me. Are those commonly used in applications? > Well, I just followed the case of the backspace, they could be removed easily > though, mclasen told me that they were ok. gtk_text_buffer_backspace is more complex and requires knowledge of text buffer internals. By comparison, gtk_text_buffer_insert_new_line is a no-brainer. That's why I asked where it's needed outside of GTK+. > > + /** > > + * GtkTextView::insert-back-tab: > > + * @text_view: the object which received the signal > > [...] > > There is no explanation what insert-back-tab does, or how it is different from > > insert-tab and it looks like the implementation is indentical. What's the use > > case? > About this, right now textview manages that cases in the same way that's why > they use the same handler. Also the reason for this is because we need to > listen to this signals in gtksourceview. So to get rid to the call of > textview->im_context we need this. I think you should just mention what it can be used for. And if insert-back-tab is handled differently from insert-tab, give an example.
> gtk_text_buffer_backspace is more complex and requires knowledge of text buffer > internals. By comparison, gtk_text_buffer_insert_new_line is a no-brainer. > That's why I asked where it's needed outside of GTK+. I guess Christian is right, we can actually do fine without introducing this new api. > I think you should just mention what it can be used for. And if insert-back-tab > is handled differently from insert-tab, give an example. While an example would be appreciated, I will point out that back-tab actually is a keycap on your tab key. The original meaning was pretty clearly to move back to the previous tab position (while tab will move forward to the next tab position. But that is in navigation, not insertion.
Created attachment 153654 [details] [review] new signals v3 Removed the buffer api, added the since and a comment for the back tab that I'm not sure about it.
Looks good to me now, but it is missing the part where you take out the hardcoded handling of Tab and Enter in the key-press handler, no ?
Created attachment 153656 [details] [review] new signals v4 I think now it should be, but see that with this patch we only manage control+(Kinds of return) and Return keys, this can be a regression as we also manage right now shift and control+shift.
Created attachment 153673 [details] [review] new signals v5 Same as previous one but as discussed in irc with shift and control+shift.
We were discussing about this and this approach will not work for us because we would have to override the signals and we would endup without beeing able to reset or filter the im_context so we could go with: * emit the signal from the key-press event after reset the im_context instead of bind the keys. * another approach would be gtk_text_view_filter_keypress that proxies im_context filter keypress. Though not sure if we would need also a gtk_text_view_reset_im_context as in gedit-view we override one of the textview signals. Any thoughts about this?
For empathy, we need a way to intercept ENTER keypress to send the IM message instead of adding newline. How is that working with the proposed signals? 1) I connet "insert-new-line" signal on the GtkTextView object. 2) in the callback, I use g_signal_stop_emission_by_name() to make sure the default implementation is not called and the newline is not inserted. If that's correct, I guess I also need a way to set the "need_im_reset" flag GtkTextView has. But that's bug #163251 I think.
*** Bug 615660 has been marked as a duplicate of this bug. ***
We're moving to gitlab! As part of this move, we are closing bugs that haven't seen activity in more than 5 years. If this issue is still imporant to you and still relevant with GTK+ 3.22 or master, please consider creating a gitlab issue for it.