mono / xwt

A cross-platform UI toolkit for creating desktop applications with .NET and Mono
MIT License
1.36k stars 241 forks source link

Not possible to select and activate/focus row of TreeView #376

Closed bigpcz closed 9 years ago

bigpcz commented 10 years ago

It is not possible to activate tree view node. There is possibility to select node but the new position cannot be set from code (I didn't find how). Only a user can do that by mouse clicking or by keyboard.

I wanted to add a new node on pressing 'Insert' key. So I called InsertNodeAfter method on TreeStore. Then I set some values on the new node and selected it. The new node is selected however not activated. The previous node is still activated.

        var view1 = new MyTreeView();
        view1.InsertNewRow += (TreePosition selectedRow) => {
            var node = store.InsertNodeAfter(selectedRow);
            node.SetValue(text, "subitem").SetValue(num, "0");

            view1.UnselectAll();
            view1.SelectRow(node.CurrentPosition);

            // node is selected but only visually, focus stays still on the old item

            //view1.ScrollToRow(node.CurrentPosition);

        };

xwt_treeview

bigpcz commented 9 years ago

It was GTK actually, but it behaves similarly in WPF:

GTK: The focus or the current cursor position stays on the 'item' with value '2'. tree

WPF: The focus or the current cursor position stays on the item marked as the blue rectangle. treewpf

I'd like to add a new item after pressing e.g. "Insert" key and move the focus from the current position to the newly created item. So that I could move over the tree from the newly created item afterwards not from the previous one. Currently, that isn't possible.

sevoku commented 9 years ago

Hhm, I can reproduce this partially on Wpf, but not on Gtk (tried Windows and Linux). On Wpf the selection is set correctly (not like in your example), but the keyboard focus remains on the old row. For the test I've simply added the following lines to the TreeView example (TreeView.cs in samples app):

//view.SelectionMode = SelectionMode.Multiple; //tested in both modes
view.KeyPressed += (sender, e) => {
    if (e.Key == Key.Insert) {
        TreeNavigator n;
        if (view.SelectedRow != null)
            n = store.InsertNodeAfter (view.SelectedRow).SetValue (text, "Inserted").SetValue (desc, "Desc");
        else
            n = store.AddNode ().SetValue (text, "Inserted").SetValue (desc, "Desc");
        view.ExpandToRow (n.CurrentPosition);
        view.ScrollToRow (n.CurrentPosition);
        view.UnselectAll ();
        view.SelectRow (n.CurrentPosition);
    }
};

And thats how it looks like: Wpf (confirmed: insert pressed twice on row 2, row 4 selected correctly, focus still in row 2): wpf tree selection bug Gtk (unconfirmed: insert pressed twice, selection/focus follows): gtk tree selection bug unconfirmed

It looks like you use some custom TreeView implementation with an InsertNewRow event. I think you should double check that. So, well, there seems to be a bug in Wpf (keyboard focus does not follow the changed selection).

sevoku commented 9 years ago

Ah, ok, pressing up/down keys shows the wrong thing. So confirmed for Gtk too.

sevoku commented 9 years ago

On Gtk it could be easily fixed by adding Widget.SetCursor(...) in TreeViewBackend.SelectRow. Wpf needs more investigation. EDIT: WPF: the TreeViewItem must be focused (Item.Focus()) EDIT2: this is not the right approach. We need a Cursor property, which sets/gets the current keyboard focus.

bigpcz commented 9 years ago

Yes, there is a problem with setting the focus. The Cursor property already exists on the TreeView. However it's intended for changing the appearance of the mouse cursor.

Are you thinking about a method like SetCursor?

        treeView.InsertNewRow += currentRowPosition => {
            var insertedRowNodeNavi = store.InsertNodeAfter(currentRowPosition);
            insertedRowNodeNavi.SetValue(text, "subitem").SetValue(num, "0");

            treeView.ExpandToRow(insertedRowNodeNavi.CurrentPosition);
            treeView.ScrollToRow(insertedRowNodeNavi.CurrentPosition);
            treeView.UnselectAll();
            treeView.SelectRow(insertedRowNodeNavi.CurrentPosition);
            treeView.SetCursor(insertedRowNodeNavi.CurrentPosition); // <---
        };
sevoku commented 9 years ago

Yes, exactly. Since Cursor exists, it could be called FocusRow maybe.

sevoku commented 9 years ago

I've quickly added a Gtk implementation on https://github.com/sevoku/xwt/tree/add-tree-focusrow, if you'd like to try. Wpf and Mac still missing. It introduces a new FocusRow property to set/get the current row with the keyboard focus.

bigpcz commented 9 years ago

I've compiled and tested your fork and it works fine. Thanks! What surprised me is that it is implemented as a property setter instead of a method.

treeView.FocusRow = insertedRowNodeNavi.CurrentPosition;

vs

treeView.FocusRow(insertedRowNodeNavi.CurrentPosition);

Just thinking if the 'FocusedRow' isn't better in that case?

treeView.FocusedRow = insertedRowNodeNavi.CurrentPosition;

It could be part of ListBox and ListView too.

sevoku commented 9 years ago

Yes, FocusedRow is better. ListView is already implemented.

sevoku commented 9 years ago

I've added WPF support and ListBox.FocusedRow impl. Give it a try!

bigpcz commented 9 years ago

Works as expected. Thanks!

sevoku commented 9 years ago

I'll take a look on Mac next days and add a PR.

sevoku commented 9 years ago

After some research (I'm not a Mac pro) there seems to be no way to manipulate the keyboard focus on Mac. The keyboard focus is always the same as the selected item (s). NSCell does not allow to make it the first responder (only for editing purposes, which is rarely the case). I've simply mapped FocusedRow to the first SelectedRow. You can find the final PR here: #499