summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortoofar <toofar@spalge.com>2023-12-31 14:12:53 +1300
committertoofar <toofar@spalge.com>2024-01-06 21:58:43 +1300
commit1d6bea7f2260e03a08ee3f2c832ff4ead5827d32 (patch)
treeb5f41faad097419cb7a2475fa794139f60b6dc0b
parent1f84108a48a73e27633a7b3c98955cee3237e647 (diff)
downloadqutebrowser-1d6bea7f2260e03a08ee3f2c832ff4ead5827d32.tar.gz
qutebrowser-1d6bea7f2260e03a08ee3f2c832ff4ead5827d32.zip
Add initial treetab docs.
TODO: * convert to asciidoc or make scripts/asciidoc2html.py support markdown input too. * for now I'm using `pandoc -f markdown -t html -o qutebrowser/html/doc/treetabs.html doc/treetabs.md` * should be able to asciidoc with pandoc too * the last section and the rest of the second to last * review and refactor * post on the PR and figure out how we can contribute to it collectively (put on main branch so people can open PRs? create new integration repo where people can open issues, discussions and PRs?) * maybe add more examples showing the effect of various operations * (when including in a release) consider removing the implementation section, or refactoring it to be more high level broad strokes and less low level stuff that will inevitably drift from the actual implementation These docs are not supposed to be in their final state. My plan for them so far is to provide a guide to the changes for PR contributors. I think it's currently difficult to hold the scope of the changes in your head the description on the PR is currently very limited. I would like to encourage discussion of usability and feature set as well as highlighting some implementation details to point out upsides, downsides, implications and alternatives. I've modified asciidoc2html to copy all images over. It was only doing specific ones before. But there are more images in there, why are they in doc/img if they aren't images used in the documentation? Anyway, storage is cheap, none of the images are sensitive and not having to hardcode individual files names is good.
-rw-r--r--doc/img/treetabs/tree_tabs_new_tab_types.pngbin0 -> 4869 bytes
-rw-r--r--doc/img/treetabs/tree_tabs_overview_detail.pngbin0 -> 11259 bytes
-rw-r--r--doc/treetabs.md212
-rwxr-xr-xscripts/asciidoc2html.py10
4 files changed, 217 insertions, 5 deletions
diff --git a/doc/img/treetabs/tree_tabs_new_tab_types.png b/doc/img/treetabs/tree_tabs_new_tab_types.png
new file mode 100644
index 000000000..fdca0e01d
--- /dev/null
+++ b/doc/img/treetabs/tree_tabs_new_tab_types.png
Binary files differ
diff --git a/doc/img/treetabs/tree_tabs_overview_detail.png b/doc/img/treetabs/tree_tabs_overview_detail.png
new file mode 100644
index 000000000..fc5610ecf
--- /dev/null
+++ b/doc/img/treetabs/tree_tabs_overview_detail.png
Binary files differ
diff --git a/doc/treetabs.md b/doc/treetabs.md
new file mode 100644
index 000000000..7b1496201
--- /dev/null
+++ b/doc/treetabs.md
@@ -0,0 +1,212 @@
+# Tree Style Tabs
+
+## Intro
+
+Tree style tabs allow you to group and manage related tabs together. Related
+tabs will be shown in a hierarchical fashion in the tab bar when it is on the
+left or right side of the browser window. It can be enabled by setting
+`tabs.tree_tabs` to `true`. That setting only applies to new windows create
+after it is enabled (including via saving and loading a session or
+`:restart`).
+
+![](img/treetabs/tree_tabs_overview_detail.png)
+
+When a tab is being opened it will be classified as one of *unrelated*
+(default), *sibling* or *related* to the current tab.
+
+![](img/treetabs/tree_tabs_new_tab_types.png)
+
+* *unrelated* tabs are created at the top level of the tree for the current
+ browser window. They can be created by opening a new tab using `:open -t`.
+* *sibling* tabs are created at the same level as the current tab. They can be
+ created by running `:open -t -S`.
+* *related* tabs are created as children of the current tab. They can be
+ created by following a link in a new tab (middle click, `F` hinting mode) or
+ by running `:open -t -r`.
+
+## Enabling Tree Tabs
+
+TODO: more words here
+
+* `tabs.tree_tabs`
+* check default settings: title format, padding, elide
+* steps to take when downgrading if you don't want to lose settings
+
+## Manipulating the Tree
+
+todo: add animated illustrations?
+
+You can change how tabs relate to each other after they are created too.
+
+* `:open`, as described in the into, has picked up some new behaviour to
+ decide where in relation to the current tab a new one should go. It has a
+ new `--sibling` argument and the `--related` argument, as well as `--tab` and
+ `-background`, has some additional meaning.
+* `:tab-move` will move a tab and its children within the tree
+ * With a `+` or `-` argument tabs will only move within their siblings
+ (wrapping at the top or bottom)
+ * With a count or integer argument tabs will move to the absolute position
+ specified, which may include changing level in the hierarchy.
+* Tabs can be moved up and down a hierarchy with the commands
+ `:tree-tab-promote` and `:tree-tab-demote`
+* `:tab-give --recursive` will move a tab and its children to another window.
+ They will be placed at the top level.
+* Some methods of moving tabs do *not* yet understand tab groups, these are:
+ * `:tab-take`
+ * moving tabs with a mouse or other pointer
+
+Other pre-existing commands that understand tab groups are:
+
+* `:tab-close --recursive` will close a tab and all its children. If
+ `:tab-close` is used without `--recursive` the first of a tabs children will
+ be promoted in its place.
+* `:tab-focus parent` will switch focus to a tab's parent, so that you don't
+ have to cycle through a tab's siblings to get there.
+* `:tab-next --sibling` and `:tab-prev --sibling` will switch the focus to a
+ tab's sibling, skipping any child tabs.
+
+## Working with Tab Groups
+
+Beyond the commands above for manipulating the tree, there are a few new
+commands introduced to take advantage of the tab grouping feature.
+
+* `:tree-tab-create-group {name}` will create a new placeholder tab with a
+ title of `{name}`. This is a light weight way of creating a "named group" by
+ putting a tab with a meaningful title at the top level of it. It can
+ create tabs at the top level of the window or under the current tab with the
+ `--related` argument. The placeholder tab contains an ascii art picture of a
+ tree. The title of the tab comes from the URL path.
+* `:tree-tab-toggle-hide` will collapse, or reveal, a tab group, which will
+ hide any children tabs from the hierarchy shown in the tab bar as well as
+ making children unelectable via `:tab-focus`, `tab-select` and `:tab-take`.
+ The tabs will still be running in the background.
+* `:tree-tab-cycle-hide` will hide successive levels of a tab's hierarchy of
+ children. For example, the first time you run it will hide the outermost
+ generation of leaf nodes, the next time will hide the next level up and so
+ on.
+* `:tree-tab-suspend-children` will suspend all of the children of a tab via
+ the lazy load mechanism (`qute://back/`). Tabs will be un-suspended when
+ they are next focused. This apply for any children which are hidden too.
+
+## Settings
+
+There are some existing settings who's behaviour will be modified when tree
+tabs are enabled:
+
+* `tabs.new_position.related`: this is essentially replaced by
+ `tabs.new_position.new_child`
+* `tabs.new_position.unrelated`: this is essentially replaced by
+ `tabs.new_position.new_toplevel`
+* the settings `tabs.title.format`, `tabs.title.format_pinned` and
+ `window.title_format` have gained two new template variables: `{tree}` and
+ `{collapsed}`. These are for displaying the tree structure in the tab bar and
+ the default value for `tabs.title.format` now has `{tree}{collapsed}` at the
+ start of it.
+
+There are a few new settings introduced to control where tabs are places in
+the tree structure as a result of various operations. All of these settings
+accept the options `first`, `last`, `next` or `prev`; apart from `new_child`
+and `demote` which only accept `first` or `last`.
+
+* `tabs.new_position.promote`
+* `tabs.new_position.demote`
+* `tabs.new_position.new_toplevel`
+* `tabs.new_position.new_sibling`
+* `tabs.new_position.new_child`
+
+## Bindings
+
+There are various new default bindings introduced to make accessing the new
+and changed commands easy. They all start with the letter `z`:
+
+TODO: more words here? Are any of these bindings analogous to existing
+ones? Any theme to them?
+
+* `zH`: `tree-tab-promote`
+* `zL`: `tree-tab-demote`
+* `zK`: `tab-prev -s` - cycle tab focus upwards among siblings
+* `zJ`: `tab-next -s` - cycle tab focus downwards among siblings
+* `zd`: `tab-close -r` - r = recursive
+* `zg`: `set-cmd-text -s :tree-tab-create-group -r` - r = related
+* `zG`: `set-cmd-text -s :tree-tab-create-group`
+* `za`: `tree-tab-toggle-hide` - same binding as vim folds
+* `zp`: `tab-focus parent`
+* `zo`: `set-cmd-text --space :open -tr` - r = related
+* `zO`: `set-cmd-text --space :open -tS` - S = sibling
+
+## Implementation
+
+The core tree data structure is in `qutebrowser/misc/notree.py`, inspired by
+the `anytree` python library. It defines a `Node` type. A Node can have a
+parent, a list of child nodes, and `value` attribute - which in qutebrowser's
+case is always a browser tab. A tree of nodes is always modified by changing
+either the parent or children of a node via property setters. Beyond those two
+setters nodes have `promote()` and `demote()` helper functions used by the
+corresponding commands.
+
+Beyond those four methods to manipulate tree the tree structures nodes have
+methods for:
+
+* traversing the tree:
+ * `traverse()` return all descendant nodes (including self)
+ * `path()` return all nodes from self up to the tree root, inclusive
+ * `depth()` return depth in tree
+* collapsing a node
+ * this just sets an attribute on a node, the traversal function respects it
+ * but beyond that it's up to callers to know that an un-collapsed node may
+ be hidden if a parent node is collapsed, there are a few pieces of
+ calling code which do implement different behaviour for collapsed nodes
+* rendering unicode tree segments to be used in tab titles
+ * our tab bar itself doesn't understand the tree structure for now, it's
+ just being represented by drawing unicode line and angle characters to
+ the left of the tab titles which happen to line up
+ * this does generally put some restrictions on some tab bar related
+ settings. `tabs.title.format` needs to have `{tree}{collapsed}` in it,
+ `tabs.padding` needs to have 0 for the top and bottom padding,
+ `tabs.title.elide` can't be on the same side as the tree related format strings.
+
+Beyond the core data structure most of the changes are in places where tabs
+need to relate to each other. There are two new subclasses of existing core
+classes:
+
+*TreeTabbedBrowser* inherits the main TabbedBrowser and has overriden methods
+to make sure tabs are correctly positioned when opening a tab, closing a tab
+and undoing a tab close. After tabs are opened they are placed into the
+correct position in the tree based on the new `tabs.new_position.*` settings
+and then into order in the tab widget corresponding to the tree traversal
+order. When tabs are closed the new `--recursive` flag is handled, children
+are re-parented in the tree and extra details are added to undo entries. When
+a tab close is undone its position in the tree is restored, including demoting
+any child that was promoted when the tab was closed. TreeTabbedBrowsers will
+be created by MainWindow when the new `tabs.tree_tabs` setting is set.
+
+*TreeTabWidget* handles making sure the new `{tree}` and `{collapsed}` are
+filled in for the tab title template string, with a lot of help from the data
+structure. It also handles hiding or showing tabs for collapsed
+groups/branches. Hidden tabs are children of tabs with the `collapsed`
+property set, they remain in the tree structure (which is held by the tabbed
+browser) but they are removed entirely from the tab widget. It also handles
+making sure tabs are moved to indexes corresponding to their traversal order
+in the tree if any changes to the tree structure happen via the
+`tree_tab_update()` method that is called from several places.
+
+A fair amount of tree tab specific code lives in *commands.py*. The six new
+commands have been added, as well as a customization so that these commands
+don't show up in the command completion if the tree tabs feature isn't
+enabled. The commands for manipulating the tree structure do very little but
+call out to other pieces of code, either the browser or the tree structure.
+Of note are the two commands `tree_tab_create_group()` and
+`tree_tab_suspend_children()` which use the scheme handlers `qute://treegroup`
+(new) and `qute://back` (existing).
+
+Beyond those six new commands quite a few existing commands to do with
+manipulating tabs have seen some tree tab specific code paths added, some of
+them quite complex and with little shared with the existing code paths. Common
+themes beyond handling new arguments are dealing with recursive operations and
+collapsed nodes.
+
+something something sessions.py
+
+Other stuff, like tree group page
+
+## Outstanding issues? Questions?
diff --git a/scripts/asciidoc2html.py b/scripts/asciidoc2html.py
index 09405c3e7..81c63032f 100755
--- a/scripts/asciidoc2html.py
+++ b/scripts/asciidoc2html.py
@@ -109,11 +109,11 @@ class AsciiDoc:
"""Copy image files to qutebrowser/html/doc."""
print("Copying files...")
dst_path = DOC_DIR / 'img'
- dst_path.mkdir(exist_ok=True)
- for filename in ['cheatsheet-big.png', 'cheatsheet-small.png']:
- src = REPO_ROOT / 'doc' / 'img' / filename
- dst = dst_path / filename
- shutil.copy(src, dst)
+ try:
+ shutil.rmtree(dst_path)
+ except FileNotFoundError:
+ pass
+ shutil.copytree(REPO_ROOT / 'doc' / 'img', dst_path)
def _build_website_file(self, root: pathlib.Path, filename: str) -> None:
"""Build a single website file."""