Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Settings API for Toga #90

Open
Ocupe opened this issue Dec 5, 2016 · 9 comments
Open

Settings API for Toga #90

Ocupe opened this issue Dec 5, 2016 · 9 comments
Labels
enhancement New features, or improvements to existing features. not quite right The idea or PR has been reviewed, but more work is needed.

Comments

@Ocupe
Copy link
Contributor

Ocupe commented Dec 5, 2016

Settings API for Toga

I wrote this short text after a brief discussion with @freakboy3742. My intention is to kick off a conversation about a possible 'Settings API' for the toga project. This text is badly written and possibly full of bad ideas. But it is a start! :)

Settings API

The settings API is the attempt to provide us programmers with a abstract interface to easily create settings/preferences panels that work on all platforms. A Mac user should feel just as 'at home' as a windows or linux user. On top of that, it should be possible to port a desktop settings window to mobile without the need of rewriting it.

Native desktop settings in the wild

To get a better understanding about how native settings look like on different platforms I added this small overview.

Desktop Examples:

macos_settings

windows_settings

Mobile Examples:

ios_settings

android_settings

Basic Structure

I think that all settings representations, no matter on what platform, have a common structure. They try to group settings which are related into some kind of settings group. On desktop platforms they use some sort of tab functionality, on mobile the use nested table views. These groups are then again grouped into a settings menu. On desktop often represented in form of a single settings window and on mobile in form of a settings view which holds a table view with navigation.

Basic Hierarchy:

  • Settings Window/View
    • Settings Group
      • Settings Item

Write once, use everywhere

I see the future of the toga settings API as write one, use everywhere! The translation from a desktop settings window to a mobile settings table view shouldn't be the problem as long as all settings items are implemented on all platforms.

'Translations'

In this section I describes possible 'translations' from desktop to mobile.

on Mobile on Desktop
root view independent settings window
root structure TableView with subsections + Navigation Tabs
Settings Group TableView + Navigation Area of the active tab
Settings Items switch checkbox
slider slider
... ...

Settings Items

A list of possible settings items that one can use with the settings api.
I don't see a problem if we allow the user to use all input widgets defined in the toga documentations. Again, as long as we make the settings items available on all platforms or find fitting 'translations' from desktop to mobile and vice versa.

Under the Hood

I don't know a clever way of implementing a Settings API. But at least I can share how I would like to interact with a potential Settings API and what things I would expect from it.

import toga

# Instantiating the settings class takes care of creating the settings window/view.
# All the platform specific bootstrapping should be taken care of and set to native defaults.
settings = toga.Settings()
# A the settings group should have a label and the possibility to add a icon to it.
group = toga.SettingsGroup('General'. icon='icon.png')

# creating setting item
switch = Switch(label='Show line numbers')

# adding setting item to the setting group
group.add(switch)

# adding group to settings
settings.add(group)

# you should be able to open the settings window/view by just calling the open() function.
settings.open()

Notes

  • I'm mostly focused on desktop and mobile. Other platforms should become a part of the equation.
  • Sorry for the spelling :/
  • After some feedback and discussion, I would like to get going with the OSX implementation followed by the iOS version.
  • Basic functionality like callbacks on settings changes and getting the values of settings must be addressed as well.
@freakboy3742
Copy link
Member

Hells yes. This is exactly the sort of thing I had in my head! Thanks for the great writeup.

A couple more ideas to throw into your thoughts:

  • Persistence: Once I've defined a settings object, I should be able to save those settings in a platform appropriate way - dot files/directories, Application Support/ directories, or whatever is appropriate.
  • Project settings: Some apps - especially those that fall into the "developer tool" category - will need both application settings and project settings. For example, you might want to configure exactly how cricket runs a test suite, and have those settings for this project persisted into a configuration file in this project. This is more likely to be a universal file format (.rc file in the project home directory style)
  • Extraction: I should be able to easily extract settings from the app object. "What is the current value of the 'privacy' setting?" I should also be able to register to be notified if a settings are changed (call this method if the user modifies the privacy setting)
  • Versioning: When I put out version 2.0, I should be able to tell that the current settings are from version 1.0, update any existing settings, backfill any new settings, maybe perform any migrations that are required.
  • Opening settings: The app settings should appear automatically in the appropriate application menu (if platform appropriate). On some platforms (e.g., iOS) this means opening the actual settings app, not just a dialog/slide in panel.

@Ocupe
Copy link
Contributor Author

Ocupe commented Mar 9, 2017

@freakboy3742 and everyone else. Before I run in the completely wrong direction I would like to get your feedback on this.
Especially the part of 'The Normalized Settings Structure'

@Ocupe
Copy link
Contributor Author

Ocupe commented Mar 9, 2017

Lowest Common Denominator (work in progress)

In this section we really go deep and try to find the lowest common denominator for a general settings API.
It is clear that the integration of settings on iOS and Android is similar but still different. Because we try to provide a common interface, that works for all platforms, we are left with the challenging and tedious job to compare all the existing API's and then find a good way to generalise them into a toga settings API.
The following table shows what platform specific 'things'/widgets your specified toga SettingsItem would resolves to.
For example, when you specify a toga toggle, you get a Toggle Switch on iOS and something called SwitchPreference on Android.

Links to the official documentations:

toga iOS android macOS Windows Linux
toggle Toggle Switch Element CheckBoxPreference, SwitchPreference  
text field Text Field Element EditTextPreference  
pick one Radio Group Element ListPreference  
multi value Multi Value Element MultiSelectListPreference  
slider Slider Element  

The Normalized Settings Structure

This is a proposal how the interface between toga.core and the respective system integrations of toga could look like. The following idea is derived from the fact that iOS and android store their settings in a .plist and .xml files. I though we could translate the in code specified settings to a normalised from. This agreed upon normalized form than acts as the base of all the system specific integrations.

Example (simplified)
# defining the settings
settings = toga.Settings(version='1.0.0')
switch = toga.SettingsItem('switch', label='My Switch', default=True)
slider = toga.SettingsItem('slider', label='My Slider', default=5, min=0, max=10)
group = toga.SettingsGroup('My Settings', [switch, slider])
settings.add_group(group)

# When printing the normalized form we end up whit something like this.
pprint(settings.get_normal_form())
{'settings': 
    {
    'version': '1.0.0',
    'groups': 
        [{'group':
            {
            'title': 'My Settings',
            'items':
                [{  
                    'default': True,
                    'key': 'my_switch',
                    'label': 'My Switch',
                    'type': 'switch'},
                {   
                    'default': 5,
                    'key': 'my_slider',
                    'label': 'My Slider',
                    'max': 10,
                    'min': 0,
                    'type': 'slider'
                }]
            }
        }]
    }
}

This normalized form, here proposed as a dictionary, can now be passed to the platform specific integration of toga.
As a nice side effect we end up with something that can easily be stored in a xml or similar file format and is platform independent.

@freakboy3742
Copy link
Member

YES - this looks almost exactly what I had in mind. No particular feedback, other than "MOAR OF THIS". 😸

@freakboy3742 freakboy3742 added enhancement New features, or improvements to existing features. up-for-grabs not quite right The idea or PR has been reviewed, but more work is needed. labels Mar 29, 2022
@Codep3
Copy link
Contributor

Codep3 commented Nov 12, 2024

Hi,
I've created a proof of concept settings widget and data source here
It uses the schema library and generates a data source which can be fed into the settings widget, validating user input before saving.
I've only tried it on GTK and Textual so any comments would be appreciated.

@freakboy3742
Copy link
Member

Thanks for that contribution. I've taken a quick look, and while it looks like there's a good starting point there, there's a lot missing from the design discussion raised above.

  1. I'm not familiar with schema as a library; I can't rule it out, but there are other much better known schema management tools out there (pydantic and marshmallow being two that I'm familiar with)
  2. We're unlikely to use YAML as a default markup format, as it isn't supported in the standard library, and has myriad problems as a format (ranging from "Yes or Norway" to potential security vulnerabilities). TOML would be the most likely candidate, given the prominence of the format in the Python ecosystem.
  3. It looks like you're hard coding 2 types of input (text and numbers); that's definitely enough for a proof of concept, but there's clearly going to be a lot of other possibilities here, including booleans, selections from a static list, files, and possibly more. This suggests to me that there's a need for a separate piece of work related to form handling that can be used outside settings. Django's form framework is one example that could be used as inspiration here.
  4. Your code seems to stop at "can create a box full of widgets"; that's definitely part of what is required; but there's a lot more work needed to integrate settings into the app. A settings API for Toga means that the app has a settings menu item, and a settings panel that appears in a platform-appropriate location (which, on mobile, may not be in the app).

Obviously, none of this stops you from using your package externally (and thank you for using the togax- prefix on your package name); but if you're aiming to have that work merged into Toga's core, these are issues that will need to be addressed (and there's likely more that a detailed teardown would reveal).

@Codep3
Copy link
Contributor

Codep3 commented Nov 14, 2024

Thank you for taking a look, I appreciate it

  1. Thanks for the tips on pydantic and marshmallow, I'll take a look at these and see if they can be used. Do you have any preference on these? I selected schema as it seemed like a simple way to define the schema with no extra dependencies but I'm happy to change it.
  2. I chose YAML as I had some other projects using that. It shouldn't be a problem to swap this out, or have it as an option along with other formats.
  3. Yep, its just a proof of concept so I can see the extra items being added. But I thought I'd get feedback early, especially if I need to change the libraries as in point 1. And I'll take a look at the django form framework
  4. While the code does create the box of widgets, it also creates the data source for the settings which could be useful for the settings API, but again, this is just a POC.
    I don't really know much about iOS development and how the settings api would work on there, so I probably won't be able to work much on that right now.

I'll keep working on this as an external project for now, but the closer it stays to upstream standards, the better
Thanks again, and thanks for the great project!

@freakboy3742
Copy link
Member

  1. Thanks for the tips on pydantic and marshmallow, I'll take a look at these and see if they can be used. Do you have any preference on these? I selected schema as it seemed like a simple way to define the schema with no extra dependencies but I'm happy to change it.

I don't have a hard preference. If anything, my preference is really "can we do this without a third party dependency at all?". After all, this isn't a user-modifiable file - we're in control of reading and writing the file. In those conditions... what do we gain by using a schema library?

That said - of the candidates on the table, Pydantic is the one that I have seen getting the most attention of late - at least in part because it can also be used as the core of an ORM, as well as some other use cases. On that basis, it would be my (very weak) preference... although it also has a Rust-based binary component, and we don't currently have a compilation recipe for it on iOS or Android, so that complicates adoption. However, I'm also willing to be convinced of the merits of another option (based on feature set, filesize, community vitality etc).

I don't really know much about iOS development and how the settings api would work on there, so I probably won't be able to work much on that right now.

To be clear - we don't expect a contributor to implement every API for every platform. If nothing else, it's a rare developer who has a Mac, Windows, Linux, iOS and Android machine available for development and testing. We don't even expect a developer to become an expert the APIs of all those frameworks. However, we do need to have some degree of confidence that the API being proposed could be implemented on all platforms by someone who has access to the hardware and knowledge.

I'll keep working on this as an external project for now, but the closer it stays to upstream standards, the better Thanks again, and thanks for the great project!

Glad you're enjoying it!

@Codep3
Copy link
Contributor

Codep3 commented Nov 21, 2024

I've already added a backup settings function, which also backs up the file if it fails the validation. I was thinking about having an import settings function to use with this, and it was my thinking that its better to validate the data being provided, although this could be done without the schema library.

Thanks for the info!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New features, or improvements to existing features. not quite right The idea or PR has been reviewed, but more work is needed.
Projects
None yet
Development

No branches or pull requests

3 participants