I have a KeyPressed signal in Gtk#/mono C# for two different purposes which are not present in the default TreeView: a) go to the next cell by pressing TAB, and b) start editing by pressing any key.
The TreeView is simple, it has a ListStore showing only rows and columns, i.e. it holds tabular data.
The code I have is below.
[GLib.ConnectBefore]
protected void OnTableKeyPressed(object o, Gtk.KeyPressEventArgs args)
{
int rowIndex;
int colIndex;
// Do not "eat" the key, by default
args.RetVal = false;
// Get the current position, needed in both cases.
this.GetCurrentCell( out rowIndex, out colIndex );
// Adapt the column
colIndex += NumFixedColumns;
if ( args.Event.Key != Gdk.Key.ISO_Enter ) {
if ( args.Event.Key == Gdk.Key.Tab
|| args.Event.Key == Gdk.Key.ISO_Left_Tab )
{
if( args.Event.State == Gdk.ModifierType.ShiftMask ) {
// Back
colIndex -= 1;
if ( colIndex < 1 ) {
colIndex = document.Columns;
--rowIndex;
}
rowIndex = Math.Max( 0, rowIndex );
} else {
// Advance
colIndex += 1;
if ( colIndex > document.Columns ) {
colIndex = 1;
++rowIndex;
}
rowIndex = Math.Min( rowIndex, document.Rows );
}
this.SetCurrentCell( rowIndex, colIndex );
args.RetVal = true; // Eat the TAB
} else {
this.SetCurrentCell( rowIndex, colIndex, true );
}
}
return;
}
I have two questions:
How can I signal to the TreeView that a cell has been finished editing? The problem is that if you press TAB when no cell is being edited, everything works fine. However, if the user is editing a cell, then the contents entered so far is lost. So, in case the user is editing a cell, I want to signal to the TreeView to finish the edition, and the carry on with the current behaviour.
How can I avoid losing the first key when editing a cell? Say you are over a cell. You press the keys 1, 2, 3, and 4. My handler correctly interferes, and puts the current cell in edition mode. However, the cell only gets 2, 3, and 4, though I am setting arg.RetVal
to false.
Info about my functions
GetCurrentCell(row, col)
translates the current cell from a TreePath to a pair of ints.SetCurrentCell(row, col, [edit])
uses TreeView.SetCursor()
in order to make a cell current. edit
can be true or false. If true, then the cell is put in edition. If it is false, nothing is edited.I am no expert in GTK, in fact I have never worked with it. But I have played with standard Controls in order to trick them into a non-default behavior. I was especially modifying the Menu bar, which trapped all Input when the Alt key was pressed, but I needed the Alt key as a modifier for various interactions. So I can offer you some general advice about tricking your TreeView
into what you need.
Question 1:
From what you describe, I suppose the default behavior would be to press Enter
for a successful edit, and to leave the Cell to cancel the edit. This might be acceptable in many applications. Others (e.g. Microsoft Excel) tend to accept the edit even on leaving the cell. So I can understand that you want that behavior.
If there is no such built in behaviour, you can simulate the Action a user has to do to signal the TreeView
to finish editing, e.g. pressing Enter. You can send a fake Key event using the approach described here or if GTK builds on WPF like here. The second approach is even more lowlevel as it really implants the fake key event in the windows event queue. I guess that should work in any case, as long as your platform is Windows. But I'm sure there are similar mechanisms in other OSes.
Then only AFTER that, translate to the next cell, TreeView
gets the lost focus event, but it is no longer in edit mode and nothing should happen.
Question 2:
What I think happens is the following: A key is pressed, TreeView
is not in edit mode, so ignores the event. You get the event, and set it in edit mode. But then the event will not return to TreeView
, so no input is made any longer.
You could try above approach and manually re-send the key event. Another way is to capture the event earlier, then when TreeView
processes it. In WPF there is often the PreviewOn*
event (e.g. see here). so maybe there is such an event for your control?
You could also hook yourself even deeper. In WPF there is the InputManager.Current.PreProcessInput
event which sits just above the windows message loop and lets you filter and process all kinds of inputs.
Here is a snipped from my code which may help you:
InputManager.Current.PreProcessInput += (sender, e) =>
{
if (e.StagingItem.Input is MouseButtonEventArgs)
{
var earg = (MouseButtonEventArgs)e.StagingItem.Input;
if (earg.RoutedEvent == Mouse.PreviewMouseDownOutsideCapturedElementEvent)
OnPreviewMouseDownOutsideCapturedElement(sender, earg);
}
};
For more lowlevel hooks see for example this question.
Good luck, and please comment, if you have more specific questions.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With