Big Picture App navigation #4271
Replies: 3 comments 18 replies
-
|
Bravo — great writeup!!! There's a few points of confusion I noticed, though:
Do you mean each item ins an OptionScaffold will provide an independent navigation context? I don't see how this is necessary for simple OptionContainers with the frames usage pattern... pushing and popping in a small frame is rather awkward, and small frames don't usually need navigation functionality.
Not neccessarily implemented using a "frame" styled SplitContainer that we have today... for example on macOS, the split actually extends into the toolbar area:
But that's kind of a moot point from a big-structure standpoint — this implementation pattern will be similar to mobile platforms, so this is more of a minor accuracy issue than blocking anything. Also. here you say
(emphasis mine) This is not particularly accurate... WinUI (see: native settings app) has push navigation functionality. Certain apps in macOS like Movies also uses push navigation. On Linux there's https://gnome.pages.gitlab.gnome.org/libadwaita/doc/1.0/class.NavigationView.html So push navigation can also apply to desktops, even though it's less common.
The "higher level" panel concept is relevant only for specific "lower level" panels to navigate to, not for any type of general action that may go inside a sidebar in small screens... for general actions I think a menu on the left would suffice, instead of an additional level of navigation. Also note, SidebarScaffold on iPadOS is a global Sidebar, at least the case with destination selections. By definition it cannot be used in every pane of a tabbar scaffold... the tabbar scaffold is rendered differently when inside a sidebar scaffold. Unless we're talking about visionOS here... |
Beta Was this translation helpful? Give feedback.
-
|
Overall this looks good, just a couple of questions:
When the navigation context is pushed, does this mean the window's entire content is saved and replaced with something else, or only the content of the context container?
I think that depends very much on what's in the table and how big the screen is. With a reasonably small number of columns, and a small amount of text in each, an actual table could be fine on a tablet or even a phone. The main benefit of a table is being able to see lots of information at once, and forcing mobile platforms into a multi-screen interface would lose that. It would also make it difficult to link the table with other UI elements, such as selecting a table row and then clicking an action button. Another possible way of presenting a table on mobile would be something more similar to a DetailedList, with each logical row being displayed across multiple physical rows. I used that pattern in this app, with each item containing up to 5 fields spread across 3 lines. This also has the advantage that it makes each item tall enough to be accurately picked with a finger – notice I forced that in the "Network" screenshot even though the 2 fields are small enough to fit on 1 line. Gmail's web interface does something similar, in which wide screens use a single-line "spreadsheet-like" layout:
While narrow screens use multiple lines:
Toga could automatically guess where to put the line breaks based on the table content, but we'd likely need to provide an API to override that, much like table column widths in a single-line layout. |
Beta Was this translation helpful? Give feedback.
-
|
Hi all, As mentioned before on several occasions, I like Toga a lot and I think it has great potential, especially in combination with Briefcase. Post like this do make me wonder what Toga's ambitions are however. Does it aspire to be a library of base components as mentioned in the tagline on the homepage? Or does it aspire to be some kind of higher-level application development framework as I interpret from this post. Can it do both well at the same time? I had a similar feeling in the post about context menus, where an argument for not implementing them was unclear behavior on mobile platforms. I have been using Toga for two years as a lightweight toolkit for desktop apps and I have come to like it a lot. I use it as a modern lightweight variant of tkinter. I think it's not quite there yet for desktop apps but it is close. With some extra features it seems it will be in a very interesting tradeoff between complexity and features. In fact, I believe it already is uniquely positioned. I think it would be a shame if that would get lost by pushing it too far towards a higher-level application development framework. I wonder if this kind of advanced functionality should somehow not be layered on top of what Toga currently is and offers. Just my 2 cents. |
Beta Was this translation helpful? Give feedback.






Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Toga is at a point where the basic collection of widgets is complete or near-complete on all desktop and mobile platforms. There are some notable gaps; but most of the main ones are there.
The limitations that we currently face as a framework relate to what I consider "big picture" navigation issues - things like:
In some cases, these issues overlap with widgets that are missing. For example, the lack of a Table and Tree widget on iOS and Android is primarily due to the natural interface for that style of content requires depth navigation, so we can't implement those widgets without a general answer for how depth navigation works.
The complication is finding representations that:
This post is an attempt to lay out a plan for the next steps of Toga's development, resolving these "big picture" navigation issues.
OptionContainer
First - a small digression: One of the outcomes of the discussion on #4059 is to highlight an issue with the way we're currently using OptionContainer on mobile.
Toga's OptionContainer widget can be placed anywhere in a content layout. You can place an OptionContainer as part of a page; you can put content vertically above and below an OptionContainer in a layout, and so on.
However, Android and iOS style guides both dictate that the widgets we're using for OptionContainers should only be used as the top level widget. It should be used as a controller for the entire app, not just a part of an app window.
Both iOS and Android provide for a different rendering of OptionContainers - Android Material calls these secondary tabs; iOS has a UISegmentedControl. These layouts are a lot more like desktop-style tab controls - they're at the top of the page, and can be wrapped around parts of content on a page.
So - as an immediate mitigation, we need to modify OptionContainer on iOS and Android to use a rendering closer to the desktop rendering. This allows end users to put an OptionContainer anywhere they want in a layout (including potentially nesting OptionContainers inside OptionContainers).
App Scaffolds
However, this digression highlights a key insight that I think is a key to the rest of our "big picture" navigation issues - there are some aspects of apps that don't fit into a traditional "widget and container" structure.
The requirement for "bottom tab" OptionContainers are one manifestation of this; but there are other gross structural patterns that have similar constraints - and accommodating those structural patterns will be difficult to do with purely "container" widgets that describe a specific physical appearance.
For example - desktop platforms provide "SplitContainer" widgets that can be deployed almost arbitrarily in a layout. However mobile platforms don't have a directly analogous concept of a "split" widget. What they do have, however, are structural app patterns that are able to adapt between different device sizes.
The idea of 2 or 3 column layouts, where left-to-right panels of content represent increasing specificity, are a very common design pattern:
By imposing a relationship between layers of content, it gives mobile platforms the flexibility to display that content in different ways, depending on display constraints. For example, iOS uses UISplitViewController; on a phone layout, depth-based navigation is used, where you drill into the increasing levels of detail, and pop out to view higher levels of detail. On a tablet layout, the "left most" panel can be treated as a flyout menu in portrait layout; or actually be displayed as a left column in landscape layout. A similar structural pattern exist on Android. Email apps are a good example of this pattern used in practice.
On desktop, these patterns are still implemented using a SplitContainer - but the key detail is that the public API defines the functional relationship between panels of data (context and detail), not the physical manifestation (a left/right SplitContainer).
To support this idea, I propose introducing a new concept to the Toga lexicon: Scaffolds. A Scaffold is effectively a container widget - but it can only be used at the top level of an app. A Scaffold is used to hold other content, but it does so in a structured way, representing relationships between content, and providing other hooks for app-level behavior.
Internally, the Scaffold is also used to manage things like navigation controllers for depth-based navigation on mobile platforms. Every widget will have a
scaffoldattribute; this provides a path to the navigation controller if the platform requires it. However, the public API doesn't presume or expose anything about the specific rendering of the content (i.e., "push navigation" isn't an API that is provided, as that won't mean anything on desktop platforms).Scaffolds need to be a different type of object, and not just new Container classes, for two reasons:
There are at least 4 immediately obvious scaffolds that could be built:
Initially, we only need a Default Scaffold, which provides guaranteed availability of depth-based navigation. I've deliberately avoided defining an API for SidebarScaffold and DetailScaffold here, as the specific API design for those Scaffolds shouldn't matter here - all that matters is that they will define high level application structure; and that they will provide a depth-based navigation context for the content they manage.
Other scaffold classes may be possible; we can add them as the need arises, or if we find other patterns for app layout that make sense. We (or third parties) are able to define new Scaffold types without altering the efficacy of any existing scaffold.
A default scaffold will be implied if
main_window.contentisn't assigned an instance of Scaffold. Assigning a Scaffold instance asmain_window.contentwill also set thescaffoldfor the window.As a migration aid, we might want to consider adding handling so that if OptionContainer is assigned as the content of a Window, it is converted into an OptionScaffold (with a warning). We may also want to add an option on OptionContainer to not do that conversion so that it's possible to have a "secondary" tab widget as the root widget in a window.
One wrinkle associated with OptionScaffold is that each item in an
OptionContainerOptionScaffold will need to provide an independent navigation context; in theory, each OptionItem could actually be a scaffold. This is whyscaffoldneeds to be an attribute on the widget, not the window (although the window will likely need to have ascaffoldattribute to describe the root-level scaffold). However, we don't need to support Scaffolds a children of OptionContainer until a later iteration.Introducing Scaffolds has one added benefit - it removes a source of ambiguity in our existing naming scheme - the
Containerclass provided on most backends that has more to do with providing OS-level "containment" of content, such as constraints, geometry management and so on, and isn't strictly related to "widget containers". In this new setup, a scaffold is a Container in both senses of the word - in that it contains widgets, and it manages window-level content management. This second usage will still be needed for handling the layout of subcontainers (ScrollContainer, etc); I suggest we adopt the term "Frame" to describe this type of usage (open to other suggestions on this naming), so that a Container will manage Frames, and a Scaffold will be both a container and a frame.DetailContainer
This separation of conceptual representation from physical manifestation, and the fact that the Scaffold is responsible for providing features like depth navigation, then lends itself to defining other widgets that have or exploit those features.
One new widget would be a "DetailContainer". This functionally expresses the "show the user a list of things, and provide the ability to display detail about those things" use case, but it allows the manifestation of this to be sensitive to platform constraints.
A DetailContainer consists of 2 sub-views - "context" and "detail". The "context" can be any widget that has an
on_selecthandler. Setting thecontextwidget sets the DetailContainer as theon_selecthandler for the widget; on mobile platforms, it may also modify the internal rendering of items on the view used as context (such as providing the right-pointing arrows that suggest navigation will occur). The DetailContainer has anon_show_detail(widget, selection, **kwargs)handler; this handler is expected to ensure that thedetailwidget is in a "presentable" state (updating the values, or replacing the entire detail view, based on the selection provided).On desktop, a DetailContainer renders as a split container. This is a pattern that can be set up today using a SplitContainer, with Table/DetailedList/Tree widgets acting as the context.
On mobile, the context container is displayed by itself. When a selection occurs, the navigation context is pushed, and the detail container is displayed. However, the use of "depth-based" navigation is implementation-dependent, not something that is exposed as part of a public API.
A DetailContainer would have a similar API to a DetailScaffold - but the underlying implementation would be different. A DetailScaffold is able to rely on top-level structural widgets (like UISplitViewController on iOS) to provide multiple panel of contents that are potentially displayed side-by-side; DetailContainer would not be able to support side-by-side rendering and would need to rely on depth-based navigation. However, this means a DetailScaffold could use a DetailContainer as part of the layout in the detail panel of the DetailScaffold.
Table on mobile platforms
On desktop platforms, a spreadsheet-like UI makes sense for tables, because there is ample horizontal and vertical real estate. However, this isn't the case for mobile platforms. Mobile platforms need to take advantage of limited screen real-estate and provide a mobile-optimized UX.
I suggest we implement Tables using the same underlying widget as DetailedList - each row of a table is visualized with a primary title and icon coming from the first column of the data, and secondary title coming from the second column of the data. We can also add a
summaryattribute to Table, accepting either a single Column, or a pair of Column objects; this allows the user to override the default "first two columns" rendering.If the user selects a row from the table, the navigation context is pushed, and the user is shown a "form"-like view of the entire row - a label / value display of each column. The navigation context uses the same primary title as is used on the full table view, and the back arrow to pops the navigation stack to return to the full table view.
This approach poses a problem for the use of Tables in a app where the main window is an instance of Window, rather than MainWindow. A Window doesn't currently expose a navigation bar; and on iOS, doesn't have a NavigationController. To address this, I'd suggest that every scaffold on mobile must provide a navigation context; but when used in a simple Window, the navigation bar will be programmatically hidden, and only made visible when a navigation context is pushed.
Tree on mobile platforms
Tree widgets are then handled in a similar way to Table, but with additional DetailedList views for each level of the tree. Leaf nodes on a tree open a detail page for that Leaf. Parent nodes push the navigation context to display all the children of the parent node. The navigation title is the name of the parent currently being displayed; a back icon in the top left allows navigation back to the root of the tree.
Scaffolds and mobile app menus
One final issue resolved by introducing the concept of a Scaffold is how to handle app-level menus.
On both iOS and Android, implementing SidebarScaffold and DetailScaffold means populating a button on the top left of the titlebar of an app. Pressing this button will expose a navigation rail on Android; on iOS, it will navigate to a "higher level" panel that shows the sidebar or selections that the scaffold provides.
I propose that we implement this "top left" menu on every mobile Scaffold, with commands in the App command group being added to the the sidebar display by default. The SidebarScaffold/DetailScaffold API defines how additional items are added to that navigation rail. This means that every item on an OptionContainer will end up duplicating the app menu; but the content will be consistent by default.
This would also be an opportune time to add support for menus and toolbars to iOS, following the implementation on Android (
UIBarButtonItem,UIMenuandUIMenuItemall exist in iOS 14+, providing a clean implementation path). iPadOS allows for full application menus; we can useplatform.ios_ver()to differentiate that case.As part of this change, it would also make sense that the
Appcommand group isn't displayed as part of menu bar. That would mean the "About" menu item (which always exists on Android) will appear on the top-left menu item, rather than the top-right as it does today.Summary
There's obviously a lot of detail missing here; in particular, specific APIs for the new Scaffold classes. The aim of this thread is to establish the "big picture"; if there's agreement about this as a broad direction, I'd expect to see tickets opened where specifics can be sorted out. Comments and feedback welcome - especially if anyone can spot a gap where platform constraints are incompatible with what has been described here.
Beta Was this translation helpful? Give feedback.
All reactions