Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changes/3026.doc.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Toga's documentation now includes an "implementer's guide" detailing the implementation of new backends.
1 change: 1 addition & 0 deletions docs/en/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
- Style guides
- [Code style guide](how-to/contribute/style/code-style-guide.md)
- [Documentation style guide](how-to/contribute/style/docs-style-guide.md)
- [Implementing a new backend](how-to/implement-backend.md)
- Internal How-to guides
- [How to cut a Toga release](how-to/internal/release.md)
- [Topic guides](topics/index.md)
Expand Down
4 changes: 2 additions & 2 deletions docs/en/about/roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,6 @@ One of the aims of Toga is to provide a rich, feature-driven approach to app dev
- **Cloud data access** - Traditional apps store all their files locally; however, using cloud services or network resources is increasingly common, especially in mobile apps. However, accessing cloud-based files can be very complicated; Toga is in a position to provide a file-like API that abstracts accessing these APIs.
- **QR code scanning** - The ability to scan QR codes and bar codes in an app, and return the encoded data.

## Platforms
## Platforms { #roadmap-platforms }

Toga currently has good support for Cocoa on macOS, GTK on Linux, Winforms on Windows, iOS and Android. Proof-of-concept support exists for single page web apps and consoles. Support for a more modern Windows API would be desirable as well.
Toga currently has good support for Cocoa on macOS, GTK on Linux, Winforms on Windows, iOS and Android. Proof-of-concept support exists for single page web apps, consoles, and KDE (Qt). Support for a more modern Windows API would be desirable as well.
12 changes: 4 additions & 8 deletions docs/en/how-to/contribute/what/implement-feature.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,13 @@ GTK4 support can be enabled by setting the `TOGA_GTK=4` environment variable. To

The code needs to support both GTK3 and GTK4; if there are significant differences in API, you can add conditional branches based on the GTK version. See one of the widgets that *has* been ported (e.g., Label) for examples of how this can be done.

### Implement an entirely new platform backend
### Implement an entirely new platform backend { #implement-backend }

Toga currently has support for 8 backends - but there's room for more!
In order to support multiple platforms natively, Toga uses [a 3-layered architecture](/reference/internals/architecture.md), in which the widgets that are instantiated by the user belongs to the *interface* layer, which does basic bookkeeping and normalizes quirks that exists across multiple platforms. Each interface widget instantiates an internal *implementation* layer widget, a collection of which are provided by platform-specific backends.

The first steps of any new platform backend are always the same:
Toga currently has support for [8 backends](/reference/platforms/index.md) - but [there's room for more][roadmap-platforms]! In particular, we would be interested in seeing a backend using a more modern Windows API.

1. Implement enough of the Toga Application and Window classes to allow you to create an empty application window, integrated with the Python `asyncio` event loop.
2. Work out how to use native platform APIs to position a widget at a specific position on the window. Most widget frameworks will have some sort of native layout scheme; we need to replace that scheme with Toga's layout algorithm. If you can work out how to place a button with a fixed size at a specific position on the screen, that's usually sufficient.
3. Get Tutorial 0 working. This is the simple case of a single box that contains a single button. To get this tutorial working, you'll need to implement the factory class for your new backend so that Toga can instantiate widgets on your new backend, and connect the Toga style applicator methods on the base widget that sets the position of widgets on the screen.

Once you have those core features in place, you can start implementing widgets and other Toga features (like fonts, images, and so on).
Hints for setting up and writing code for a backend exists in the [Implementing a new backend](/how-to/implement-backend.md) How-To Guide.

### Improve the testing API for application writers

Expand Down
76 changes: 76 additions & 0 deletions docs/en/how-to/implement-backend.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Implementing a new backend


## Extensibility and unofficial backends

In order to support multiple platforms natively, Toga uses [a 3-layered architecture](/reference/internals/architecture.md), in which the widgets that are instantiated by the user belongs to the *interface* layer, which does basic bookkeeping and normalizes quirks that exists across multiple platforms. Each interface widget instantiates an internal *implementation* layer widget, a collection of which are provided by platform-specific backends.

On an interface layer object, using ``._impl`` often gets the implementation layer object. On the implementation layer object, using ``.interface`` often gets the interface layer object.

Toga accomplishes backend discovery by virtue of [entry points](https://packaging.python.org/en/latest/specifications/entry-points/), and specifically, the following syntax is utilized by Toga backends in their ``pyproject.toml`` files:
```
[project.entry-points."toga.backends"]
PLATFORM = "BACKEND"
```
For example, in the Qt backend:
```
[project.entry-points."toga.backends"]
linux = "toga_qt"
```
The backend should then define a ``factory`` module that imports all of its implementation layer functionalities and widgets, which will be used by the interface layer.

Toga, by virtue of being an open-source project, allows third parties to provide independently maintained unofficial backends for Toga. These backends have the ability to wrap a native GUI toolkit currently unsupported by Toga. If the backend is for a commonly used but unsupported platform, can work reliably with no significant blockers, and is mature enough to be maintainable, then you may consider [proposing](contribute/what/propose-feature.md) and [submitting][implement-backend] the backend officially.

However, if you do decide to maintain a backend unofficially, please release the package for your backend using a ``togax-`` prefix in its name, instead of a ``toga-`` prefix. The latter is reserved for official BeeWare and Toga packages only.

## Research

Preliminarily determining the steps in order to perform a number of tasks in the native GUI toolkit you plan to wrap is helpful before starting the implementation. This includes the following:

### ``asyncio`` integration

All modern GUI toolkits has an *event loop*, which, when approximated by way of pseudocode, amounts approximately to the following:
```
while not app.quit_requested():
app.process_events()
app.redraw()
```
The method names shall exist for demonstration purposes only, and are fictional. Processing events partly consists of invoking handlers and taking into account the consequences of interactions with elements such as a button, the scrollbar, etc; redrawing will display the updated GUI stemming from these consequences.

(By virtue of the approximative nature of the above demonstration, there may also exist additional phases in toolkits such as GTK, including performing layout.)

Often, however, users would like to wait on other events in this loop, and perform tasks such as waiting for a long-standing operation to finish. All these can be accomplished by virtue of an event loop -- Pythonically, a module named `asyncio` can orchestrate event loops quite well, and in Toga it is the solution used to provide the user with means to perform asynchronous tasks like those above. However, native GUI event loops are often different from `asyncio`.

For various GUI toolkits, there exists packages created that can integrate their event loop into `asyncio`. Search for those that uses the Python `asyncio` interface clearly and is fully compatible with it (e.g. QtAsyncio would not be a viable option for Qt by virtue of it reimplementing some of the `asyncio` APIs by themselves incompatibly), and research how that library can be utilized to dispatch tasks asynchronously in an application built with that native GUI toolkit. This will often involve creating an event loop and running it in a certain way.

### Replacement of the layout system

Toga aims to be native in various ways, with the exception of layout, mostly due to the diversity of how different platforms perform this task. The implementation of layout is better explained in [this topic guide](/topics/layout.md), but since the algorithm is cross-platform, it is implemented in the interface layer, facilitated by a library named Travertino.

Thus, for the purpose of implementing a backend, one must procure the method in order to put an arbitrary widget at any location with any size in the GUI toolkit they plan to wrap. For a more complex system, such as GTK which has an organized way of handling layout at scale that may cause difficulties, you may wish to investigate how to integrate a custom layout system into the widget toolkit, using the layout values produced by Toga to cooperate with the native layout.

---

The above two are the major tasks, that if handled, should make the actual implementation much more straightforward. Now we could begin.

## Implementation

The sections below encompasses what would be the most important to implement, in a recommended order. General tips / explanations are provided, followed by expandable explanations of specific potential areas of confusion.

### ``Window`` and ``App``

Start by implementing 2 basic classes: [`Window`][toga.Window] and [`App`][toga.App]. At this stage, it is imperative to note that the implementations need not be feature-complete, and to [avoid scope creep](contribute/how/scope-creep.md); having a window to manage its size, position, lifecycle, and visibility is often sufficient, and similarly, App need not manage current window, dialogs, cursors, and commands, but should have the correct startup and main loop sequence and be able to instantiate the GUI elements. In the process, if an error is raised about a functionality not being available, it is acceptable to stub it with ``pass`` or returning a similar structure, such as an empty array. In the process, ``self.interface.factory.not_implemented("brief description / function name")`` (where ``self`` is most of the classes in the implementation layer) can be used to indicate that a functionality is not implemented yet.

For understanding what should be done in the classes, you can take a look at ``toga_dummy`` for the necessary APIs that should be implemented -- however, they may not be representative of typical implementations of the functionalities. The actual code for the other backends may also be a good reference -- but it is recommended to take a look at multiple other backends to see how things are achieved; each backend can and should have its own, slightly different shape, influenced by the differences of the public interface API and the native layer.

??? abstract "App startup handling"

Remarks about App.interface.startup

??? abstract "Window Close Handling"

Remarks about sWindow on close handlers

### Widgets and Layout

Containers, etc.
4 changes: 4 additions & 0 deletions docs/en/how-to/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ How-to guides are recipes that take the user through steps in key subjects. They

* [Contributing to Toga](contribute/index.md) - The many ways you can contribute to Toga, including what you can do and how to do it.

## [Implementing a new backend](implement-backend.md)

* [Implementing a new backend](implement-backend.md) - How to create a new backend for Toga for use with a currently unsupported GUI toolkit, including tips on how to maintain unofficial backends.

## Internal guides

* [How to cut a Toga release](internal/release.md) - The procedure for cutting a new release of Briefcase.
8 changes: 8 additions & 0 deletions docs/spelling_wordlist
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ APIs
App
AppKit
AppleTV
approximative
apps
async
asyncio
Expand Down Expand Up @@ -102,6 +103,7 @@ Monetization
MultilineTextInput
MultilineTextInputs
namespace
natively
NumberInput
NumberInput's
NumLock
Expand All @@ -120,11 +122,13 @@ PNG
POSIX
Postel's
pre
preliminarily
prepending
prereqs
programmatically
ProgressBar
PRs
pseudocode
px
PyCon
Pygame
Expand All @@ -137,7 +141,9 @@ PyScript
PySide
pytest
pythonic
Pythonically
Qt's
QtAsyncio
QuickLook
Quickstart
radiusx
Expand All @@ -146,6 +152,7 @@ README
Refactored
rehint
rehinted
reimplementing
RemoteCommand
Ren
resizable
Expand All @@ -158,6 +165,7 @@ sandboxed
scalable
ScrollContainer
scrollable
scrollbar
scrollers
ScrollLock
SDK
Expand Down