Skip to content

Conversation

mstv
Copy link
Member

@mstv mstv commented Dec 28, 2024

Fixes #1326

Proposed changes

FileStatusList:

  • Replace ListView with TreeView
  • Implement sorting as "group by"
  • Remove GitItemStatusFileExtensionComparer, GitItemStatusNameComparer, GitStatusListSorter, ImageIndexListSorter

Screenshots

Before

image

After

image image image

image image image

Modes:

image

FormCommit - Before

image

After: flat list by name / by path / tree

image image image

Prepared for replacing "File tree" in a follow-up:

image

Test methodology

  • existing and added tests

Please do not squash merge


✒️ I contribute this code under The Developer Certificate of Origin.

@mstv mstv self-assigned this Dec 28, 2024
@gerhardol
Copy link
Member

gerhardol commented Dec 28, 2024

Works fine.
I would prefer a separate flat/tree setting (even if switching may require one more step):
image

When sorting by status, Range should be at the bottom.
Sort modified Both,B,A, then New etc. Why submodules separate (there are no special icons, should be handled as modified with both/B/A)?
image

Can rangediff be in top view, no need for a folder?
Similar for groups with just one item (but may not be feasible there).
image

Nice that collapsing is automatic, but skip the folder icon
image

When scrolling to next, nice that next group expands.
It would be nice to have collapse/expand as for side panel

Should selecting a folder with leafs have some info in the fileviewer? First file?
Action when rightclicking should be as if all subnodes are selected.
So maybe selecting a folder selects all nodes?

RangeDiff should have a new icon, reusing it for yet another purpose is a little confusing (since is was added for groups)

@gerhardol gerhardol mentioned this pull request Dec 28, 2024
Copy link
Member

@gerhardol gerhardol left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few comments, mostly about functionality.
Reviewing together with #12115

@mstv
Copy link
Member Author

mstv commented Dec 29, 2024

Works fine. I would prefer a separate flat/tree setting (even if switching may require one more step):
image

Opening a submenu of a context menu would be a pretty expensive second step.
I could add an additional context menu item "As tree" (but will be much code).

When sorting by status, Range should be at the bottom.

I agree.

Sort modified Both,B,A, then New etc. Why submodules separate (there are no special icons, should be handled as modified with both/B/A)?
image

I do not fully understand the point about submodules. It might become resolved when considering the DiffStatus for the grouping, too.

Can rangediff be in top view, no need for a folder? Similar for groups with just one item (but may not be feasible there).

👍 Should not be difficult for grouping (same as the already implemented handling if there is only one group).

It would be nice to have collapse/expand as for side panel
Action when rightclicking should be as if all subnodes are selected. So maybe selecting a folder selects all nodes?

I am adding Collapse, Expand and Select all. In the follow-up for adding a file-tree mode or later, special menu items or behavior can be added.

Should selecting a folder with leaves have some info in the fileviewer? First file?

Yes, if one has a good idea. But not the first file - same as in the "File tree".

RangeDiff should have a new icon, reusing it for yet another purpose is a little confusing (since is was added for groups)

I agree. My main use of range diff is to see equal (and skipped) revisions. So a different icon would be good. Proposals?
For the additional diff groups, I was thinking of overlays "B", "A", "C"ombined. An "R" overlay might suffice for range diff, too.

@gerhardol
Copy link
Member

Opening a submenu of a context menu would be a pretty expensive second step. I could add an additional context menu item "As tree" (but will be much code).

I meant a separate menu on the top level, as appearance

I do not fully understand the point about submodules. It might become resolved when considering the DiffStatus for the grouping, too.

I consider a changed submodule as a changed file.
There is no special icon for both changed (or only A/B) for submodules

RangeDiff should have a new icon, reusing it for yet another purpose is a little confusing (since is was added for groups)

I agree. My main use of range diff is to see equal (and skipped) revisions. So a different icon would be good. Proposals? For the additional diff groups, I was thinking of overlays "B", "A", "C"ombined. An "R" overlay might suffice for range diff, too.

Overlay in general would be good for these icons. The variants (both changed etc) need to be changed for dark mode anyway.
I am not a designer, it takes time to get opinions and a lot more to implement changes, so no great proposal myself.

@gerhardol
Copy link
Member

Right click on folder should enable more context menu options

image

image

This is new functionality, nice to have for RevDiff but needed if FileTree is updated too (or integrated in RevDiff).
Add todo in Description?

@mstv mstv force-pushed the feature/filestatuslist_tree branch from c9d1c97 to bc0783c Compare December 29, 2024 22:22
gerhardol added a commit to gerhardol/gitextensions that referenced this pull request Dec 30, 2024
Dark mode adjusted icons for FileStatusList

Note:
* Only that only Modified is changed, not added/removed/copied/renamed,
pending feedback
* Icons are amodified for dark mode, will likely require separate light/dark mode
* Colors added to separate B (blue-violet), A (yellow) and same (green)
(unequal was red already). Should probably be applied to light icons too.
Implementation of dual icons are pending update of FileStatusList in gitextensions#12116
gerhardol added a commit to gerhardol/gitextensions that referenced this pull request Dec 30, 2024
Dark mode adjusted icons for FileStatusList

Note:
* Only that only Modified is changed, not added/removed/copied/renamed,
pending feedback

* Icons are modified for dark mode, will maybe need separate light/dark mode
Implementation of dual icons are pending update of FileStatusList in gitextensions#12116

* Colors added to separate B (blue-violet), A (yellow) and same (green)
(unequal was red already). Should probably be applied to light icons too,
if they are separate.
@mstv mstv force-pushed the feature/filestatuslist_tree branch from 669f9e0 to c52fb55 Compare December 31, 2024 13:46
@gerhardol
Copy link
Member

gerhardol commented Jan 1, 2025

A few comments on this PR as it is now:
[ ] Remove folder icons for files, appears on the first items
[ ] Remove icons if top is matched (file status, will affect all but submodules)
[ ] Lazy for GitItemStatus #12116 (comment)
[ ] Comment for the unexpected shift-click behavior
* The dependency is fine in principle, need to check some details.

Questions:

  • Should changed submodules be in modified status gruop? Now they are separated
    * Separate the flat/tree setting?

For future PRs?
* context menu for folders #12116 (comment)

  • Optional group for all files, to replace file tree tab (maybe). Added similar to grep, i.e. after diff.

--
For first item
image

@mstv
Copy link
Member Author

mstv commented Jan 1, 2025

Thank you for the detailed feedback. I am aware of the TODOs.

TreeView has several limitations, particularly icons:
StateImages are limited to 14 (only a runtime error when trying to use a higher index). StateImages are scaled in a special way.
Images work better. But it looks ugly if some nodes have no icon.
With OwnerDrawAll, one needs to draw everything including tree lines, which feels like implementing the tree view control from scratch, what I do not want to do.

Tooltips are automatically provided - if Text matches the owner-drawn length. I saw the wrong selection highlight length, too. I am adapting this.

Should changed submodules be in modified status group? Now they are separated.

"Sort by status" is a TODO yet and should yield the same ordering as before. New subgroups or changes in the ordering should be added in a follow-up.

Separate the flat/tree setting?

I prefer a one-stop shop. A "tree / flat" toggle could be useful in addition.
Later, we should add a toolbar with sort options and filtering.
FormCommit and artificial commits could use a separate sorting setting.

@mstv mstv force-pushed the feature/filestatuslist_tree branch from affc6af to 3dcbdc4 Compare January 3, 2025 19:44
@mstv
Copy link
Member Author

mstv commented Jan 3, 2025

folder icons for files, appears on the first items

If a file is the only file in a subfolder, no subfolder-node is created but the folder icon is added to the file as StateImage (with arbitrary scaling by the TreeView control).
Without the folder icon, one could miss the fact that the file is located in a subfolder.

(sorted by path)
image

(grouped by status)
image

It is not the "first" item but a single item - between the surrounding sibling folders:
image

Not used in flat view:
image


Should changed submodules be in modified status gruop? Now they are separated

I think the current sorting is the same as in master.

image

image

The mentioned more logical sorting could simply be achieved in a separate PR by moving up the submodule icons in CreateImageListData.

I prefer current order changed,added,removed then by both,B,A

Then we could remove DiffStatus from GetStatusKey again and rely on the order of images in CreateImageListData.

            (string imageKey, Bitmap icon)[] images =
            [
                (nameof(Images.FileStatusUnknown), ScaleHeight(Images.FileStatusUnknown)),
                (nameof(Images.FileStatusModified), ScaleHeight(Images.FileStatusModified)),
                (nameof(Images.FileStatusModifiedOnlyA), ScaleHeight(Images.FileStatusModifiedOnlyA)),
                (nameof(Images.FileStatusModifiedOnlyB), ScaleHeight(Images.FileStatusModifiedOnlyB)),
                (nameof(Images.FileStatusModifiedSame), ScaleHeight(Images.FileStatusModifiedSame)),
                (nameof(Images.FileStatusModifiedUnequal), ScaleHeight(Images.FileStatusModifiedUnequal)),
                (nameof(Images.FileStatusAdded), ScaleHeight(Images.FileStatusAdded)),
                (nameof(Images.FileStatusAddedOnlyA), ScaleHeight(Images.FileStatusAddedOnlyA)),
                (nameof(Images.FileStatusAddedOnlyB), ScaleHeight(Images.FileStatusAddedOnlyB)),
                (nameof(Images.FileStatusAddedSame), ScaleHeight(Images.FileStatusAddedSame)),
                (nameof(Images.FileStatusAddedUnequal), ScaleHeight(Images.FileStatusAddedUnequal)),
                (nameof(Images.FileStatusRemoved), ScaleHeight(Images.FileStatusRemoved)),
                (nameof(Images.FileStatusRemovedOnlyA), ScaleHeight(Images.FileStatusRemovedOnlyA)),
                (nameof(Images.FileStatusRemovedOnlyB), ScaleHeight(Images.FileStatusRemovedOnlyB)),
                (nameof(Images.FileStatusRemovedSame), ScaleHeight(Images.FileStatusRemovedSame)),
                (nameof(Images.FileStatusRemovedUnequal), ScaleHeight(Images.FileStatusRemovedUnequal)),
                (nameof(Images.Unmerged), ScaleHeight(Images.Unmerged)),
                (nameof(Images.FileStatusRenamed), ScaleHeight(Images.FileStatusRenamed.AdaptLightness())),
                (nameof(Images.FileStatusRenamedOnlyA), ScaleHeight(Images.FileStatusRenamedOnlyA)),
                (nameof(Images.FileStatusRenamedOnlyB), ScaleHeight(Images.FileStatusRenamedOnlyB)),
                (nameof(Images.FileStatusRenamedSame), ScaleHeight(Images.FileStatusRenamedSame)),
                (nameof(Images.FileStatusRenamedUnequal), ScaleHeight(Images.FileStatusRenamedUnequal)),
                (nameof(Images.FileStatusCopied), ScaleHeight(Images.FileStatusCopied)),
                (nameof(Images.FileStatusCopiedOnlyA), ScaleHeight(Images.FileStatusCopiedOnlyA)),
                (nameof(Images.FileStatusCopiedOnlyB), ScaleHeight(Images.FileStatusCopiedOnlyB)),
                (nameof(Images.FileStatusCopiedSame), ScaleHeight(Images.FileStatusCopiedSame)),
                (nameof(Images.FileStatusCopiedUnequal), ScaleHeight(Images.FileStatusCopiedUnequal)),
                (nameof(Images.SubmodulesManage), ScaleHeight(Images.SubmodulesManage)),
                (nameof(Images.FolderSubmodule), ScaleHeight(Images.FolderSubmodule)),
                (nameof(Images.SubmoduleDirty), ScaleHeight(Images.SubmoduleDirty)),
                (nameof(Images.SubmoduleRevisionUp), ScaleHeight(Images.SubmoduleRevisionUp)),
                (nameof(Images.SubmoduleRevisionUpDirty), ScaleHeight(Images.SubmoduleRevisionUpDirty)),
                (nameof(Images.SubmoduleRevisionDown), ScaleHeight(Images.SubmoduleRevisionDown)),
                (nameof(Images.SubmoduleRevisionDownDirty), ScaleHeight(Images.SubmoduleRevisionDownDirty)),
                (nameof(Images.SubmoduleRevisionSemiUp), ScaleHeight(Images.SubmoduleRevisionSemiUp)),
                (nameof(Images.SubmoduleRevisionSemiUpDirty), ScaleHeight(Images.SubmoduleRevisionSemiUpDirty)),
                (nameof(Images.SubmoduleRevisionSemiDown), ScaleHeight(Images.SubmoduleRevisionSemiDown)),
                (nameof(Images.SubmoduleRevisionSemiDownDirty), ScaleHeight(Images.SubmoduleRevisionSemiDownDirty)),
                (nameof(Images.ViewFile), ScaleHeight(Images.ViewFile)),
                (nameof(Images.Diff), ScaleHeight(Images.Diff))
            ];

I think I have addressed every discussed point.

@gerhardol
Copy link
Member

Regression from #12115: When moving to next file on bottom file with scrollwheel, the selection restarts at the top file.
I implemented it this way in the original PR, but got requests to change.
Now I am used to stop at the bottom file and have been confused a couple of times over the number of changes...

@@ -14,6 +14,7 @@
<PackageVersion Include="LibGit2Sharp" Version="0.27.2" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="1.1.1" />
<PackageVersion Include="Microsoft.Toolkit.HighPerformance" Version="7.1.2" />
<PackageVersion Include="StrongOf" Version="1.2.4" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need another external dependency?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO yes, because it is a light-weight, mature cure for Primitive Obsession (refer to https://github.com/BenjaminAbt/StrongOf/blob/main/README.md).

@gerhardol
Copy link
Member

Heading needed in FormCommit for sort by status

Collapsed
image

Expanded (as it is default, so no major issue)
image

@RussKie
Copy link
Member

RussKie commented Jan 4, 2025

Heading needed in FormCommit for sort by status

Collapsed image

Expanded (as it is default, so no major issue) image

Why? Why do we need the separation?

@gerhardol
Copy link
Member

Heading needed in FormCommit for sort by status
Collapsed image
Expanded (as it is default, so no major issue) image

Why? Why do we need the separation?

When you collapse the group, it looks like something is missing

@mstv
Copy link
Member Author

mstv commented Jan 4, 2025

Why do we need the separation?

In order to be able to collapse an uninteresting group of files instead of scrolling a lot.
These grouping nodes could be omitted in flat mode. I would say they are needed in tree mode.

Heading needed in FormCommit for sort by status

Really? If yes, we will need a TranslatedString for each status image (follow-up PR).
These nodes are expanded on click. I thought this would be self-explanatory enough.

mstv added a commit to mstv/gitextensions that referenced this pull request Jan 30, 2025
mstv added a commit to mstv/gitextensions that referenced this pull request Jan 30, 2025
mstv added a commit to mstv/gitextensions that referenced this pull request Jan 30, 2025
@mstv mstv force-pushed the feature/filestatuslist_tree branch from 4130865 to 46313ef Compare January 30, 2025 20:43
@mstv mstv force-pushed the feature/filestatuslist_tree branch from 46313ef to a9dfc19 Compare January 30, 2025 22:01
Copy link
Member

@gerhardol gerhardol left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest this is merged even if there are no more reviewers.
There are follow ups and this is needed for dark mode

@mstv mstv merged commit a9dfc19 into gitextensions:master Feb 3, 2025
3 checks passed
@mstv mstv removed the status: ready label Feb 3, 2025
@mstv mstv deleted the feature/filestatuslist_tree branch February 3, 2025 21:37
@pmiossec
Copy link
Member

pmiossec commented Feb 6, 2025

@mstv A little UX improvement that I would like to see added:
When there is only one "Diff" group, clicking on it just select it and do not fold it.
Folding it bring no value (because that's the only thing to display) and if I click on it for any reason, I have to click on it again (and it's frustrating 😉 )

@mdonatas-trafi
Copy link

I hope it's not too late to ask. Is it possible to have tree-view as an option? Currently UX has so many subtle and not so subtle sharp edges that it makes it hardly usable (for me).
I've created 3 issues related to this but there are more quirks than that.

@mstv
Copy link
Member Author

mstv commented Feb 9, 2025

I hope it's not too late to ask. Is it possible to have tree-view as an option?

Sorry that it broke how you use it.
It is not too late because the answer is the same: In principle, yes because it is soft ware. But it would be much effort for little benefit.

Multi-selection works with Ctrl+click and Shift+click. (Ctrl+A works the same as before.)
At least with #12149, I do not see much need for or value in features like

  • be able to have no selection
  • select multiple entries from bottom with the mouse
    With FileStatusList: Add toolbar and actions on folders #12149, just select a diff group or a folder and operate on all contained items.
    It should not be too difficult to add a generic click and drag for selecting multiple files - to MultiSelectTreeView.
    (Though changing the focused node on mouse down is hard to achieve with TreeView.)

@RussKie
Copy link
Member

RussKie commented Feb 22, 2025

The tree expand/collapse action must be bound to a double click not a single click. Otherwise, it's extremely annoying....

12116.mp4

Also, note the selected image background - it doesn't look being correctly reset.

@mstv
Copy link
Member Author

mstv commented Feb 22, 2025

The tree expand/collapse action must be bound to a double click not a single click.

This is only if there multiple diff groups. There is no real use case to select an expanded group other than collapsing it.

Also, note the selected image background - it doesn't look being correctly reset.

This is Windows Explorer style.
This TreeView is just used in TreeViewDrawMode.OwnerDrawText. The TreeView lives its own life (as have already complained).

@gerhardol
Copy link
Member

I update #12150 with the inactive selected in FileStatus

@mstv
Copy link
Member Author

mstv commented Feb 22, 2025

This is only if there multiple diff groups.

Implemented in #12149.

@mstv
Copy link
Member Author

mstv commented Feb 22, 2025

I update #12150 with the inactive selected in FileStatus

(I am not completely sure about what you are going to.)
If there is no item selected, no TreeNode shall be highlighted. This is important for the UX in FormComit.

@RussKie
Copy link
Member

RussKie commented Feb 23, 2025

The tree expand/collapse action must be bound to a double click not a single click.

This is only if there multiple diff groups. There is no real use case to select an expanded group other than collapsing it.

First and foremost, this is an accessibility issue. Users (like me in this case) can click on the group accidentally.
Secondly, this is behaviour is inconsistent with other part of the app - we have groups in the Diff tab and we have a treeview in the left panel, and none of those collapse on a single click.

@mstv
Copy link
Member Author

mstv commented Feb 23, 2025

Groups in the Diff tab were collapsible with single click - but only if all filenames fit into the control. This limitation used to be a nuisance.

image

image

Enabling ShowRootLines (as in Left Panel) if there are multiple diff groups, would enable collapse/expand with single click - but would waste precious horizontal space.

image

I have restricted the single-click collapse/expand to the icons of the root nodes (added to #12149).

image

@pmiossec
Copy link
Member

We lost the feature to be able to drag a file from the tree (and drop it in an external application).

@RussKie
Copy link
Member

RussKie commented Feb 27, 2025

The whole mechanic of staging/unstaging has been seriously affected, which has significant usability implications.
That is, when the files were in a flat list, it was easy and natural just to double click on a file to stage/unstage.

The new UI introduced the concept of folders, which traditionally (i.e., in Windows) have a different meaning for double-click (if clicked on a icon/text and not on the [+] button) - that's expand/collapse/open.
Having a single-click on a folder node is a frustrating experience, so it has to remain (or be reinstated).
It's still possible to double-click on a file, but it's not possible to stage/unstage a folder - not with a button or by any other means.

@mstv
Copy link
Member Author

mstv commented Feb 27, 2025

Try #12149, please.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Diff: optionally show file list as tree
5 participants