Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to manage key pressings for special purposes in Gtk# TreeView?

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:

  1. 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.

  2. 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.
like image 320
Baltasarq Avatar asked May 07 '15 22:05

Baltasarq


1 Answers

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.

like image 105
azt Avatar answered Nov 08 '22 07:11

azt