Add a first pass at documentation for the settings framework
This commit is contained in:
parent
56e1afb12e
commit
b31ebae651
|
@ -0,0 +1,134 @@
|
||||||
|
# Settings Framework
|
||||||
|
|
||||||
|
The settings framework manages application settings, as well as projects. This document explains
|
||||||
|
how to make use of the framework as well as some of its inner workings.
|
||||||
|
|
||||||
|
[TOC]
|
||||||
|
|
||||||
|
## Source Code Guide
|
||||||
|
|
||||||
|
Most of the relevant code is in `common/settings` and `common/project`.
|
||||||
|
|
||||||
|
C++ Class | Description
|
||||||
|
:------------------------|:-------------
|
||||||
|
`SETTINGS_MANAGER` | Loads and unloads settings files and projects
|
||||||
|
`JSON_SETTINGS` | The base class for all settings objects. Represents a single JSON file.
|
||||||
|
`NESTED_SETTINGS` | A `JSON_SETTINGS` object stored within another (i.e. without its own file)
|
||||||
|
`PARAM` | A parameter: helper class for storing data inside a `JSON_SETTINGS`
|
||||||
|
`APP_SETTINGS_BASE` | Base class for application (frame) settings
|
||||||
|
`COLOR_SETTINGS` | A subclass of `JSON_SETTINGS` designed for storing color themes
|
||||||
|
`COMMON_SETTINGS` | The settings available to every part of KiCad
|
||||||
|
`PROJECT_FILE` | A `JSON_SETTINGS` representing a project (`.kicad_pro`) file
|
||||||
|
`PROJECT_LOCAL_SETTINGS` | A `JSON_SETTINGS` representing a project local state (`.kicad_prl`) file
|
||||||
|
|
||||||
|
## Where Settings are Stored
|
||||||
|
|
||||||
|
There are four main places a setting might be stored:
|
||||||
|
|
||||||
|
1) In `COMMON_SETTINGS`: this is a setting that is shared between all parts of KiCad.
|
||||||
|
2) In an application settings object (subclass of `APP_SETTINGS_BASE`). These objects, such as
|
||||||
|
`EESCHEMA_SETTINGS` and `PCBNEW_SETTINGS`, store settings that are specific to a portion of
|
||||||
|
KiCad. In particular, these objects are compiled inside the context of their respective
|
||||||
|
application, so they have access to data types that may not be part of `common`.
|
||||||
|
3) In the `PROJECT_FILE`, where they will be specific to a loaded project. This is true of most of
|
||||||
|
the settings found in the Board / Schematic Setup dialogs. Currently, KiCad only supports having
|
||||||
|
one project loaded at a time, and a number of places in the code expect that a `PROJECT` object
|
||||||
|
will always be available. Because of this, the `SETTINGS_MANAGER` will always ensure that a
|
||||||
|
"dummy" `PROJECT_FILE` is available even when no project has been loaded by the user. This dummy
|
||||||
|
project can be modified in memory but not saved to disk.
|
||||||
|
4) In the `PROJECT_LOCAL_SETTINGS` object, where they will be specific to a loaded project. This
|
||||||
|
file is for settings that are "local state", such as which board layers are visible, that should
|
||||||
|
(for many users, at least) not be checked in to source control. Any setting here should be
|
||||||
|
transient, meaning there will be no ill effect if the entire file is deleted.
|
||||||
|
|
||||||
|
## JSON_SETTINGS
|
||||||
|
|
||||||
|
The `JSON_SETTINGS` class is the backbone of the settings infrastructure. It is a subclass of the
|
||||||
|
`nlohmann::json::basic_json` class provided by `thirdparty/nlohmann_json/nlohmann/json.hpp`. As
|
||||||
|
such, anytime raw manipulation of the underlying JSON data is needed, you can use the [standard
|
||||||
|
`nlohmann::json` API](https://nlohmann.github.io/json/api/basic_json/). The JSON contents represent
|
||||||
|
the **state of the file on disk**, not the state of the data exposed to C++. Synchronization
|
||||||
|
between the two is done via parameters (see below) and takes place right after loading from disk and
|
||||||
|
right before saving to disk.
|
||||||
|
|
||||||
|
## Parameters
|
||||||
|
|
||||||
|
Parameters establish the link between C++ data and content in the JSON file. In general, parameters
|
||||||
|
consist of a **path**, **pointer**, and **default value**. The path is a string of the form
|
||||||
|
`"x.y.z"`, where each component represents a nested JSON dictionary key. The pointer is a pointer
|
||||||
|
to the C++ member variable that holds the data accessible to consumers of the `JSON_SETTINGS`. The
|
||||||
|
default value is used to update the pointer when the data is missing from the JSON file.
|
||||||
|
|
||||||
|
Parameters are subclasses of `PARAM_BASE` in `include/settings/parameters.h`. There are a number of
|
||||||
|
helpful subclasses created to make it easier to store complex data in JSON files.
|
||||||
|
|
||||||
|
The basic `PARAM` class is templated and is useful for storing any data type can be serialized to
|
||||||
|
JSON automatically. A basic instantiation of a `PARAM` might look like:
|
||||||
|
|
||||||
|
m_params.emplace_back( new PARAM<int>( "appearance.icon_scale",
|
||||||
|
&m_Appearance.icon_scale, 0 ) );
|
||||||
|
|
||||||
|
Here, `m_Appearance.icon_scale` is a public member of the settings object (an `int` inside a
|
||||||
|
`struct`). `"appearance.icon_scale"` is the **path** to store the value in the JSON file, and `0`
|
||||||
|
is the default value. This would result in JSON looking like this:
|
||||||
|
|
||||||
|
{
|
||||||
|
"appearance": {
|
||||||
|
"icon_scale": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Note that it is possible to use custom types with `PARAM<>` as long as they have a `to_json` and
|
||||||
|
`from_json` defined. See `COLOR4D` for an example of this.
|
||||||
|
|
||||||
|
For storing complex data types, it is sometimes easiest to use `PARAM_LAMBDA<>`, which allows you
|
||||||
|
to define a "getter" and "setter" as part of the parameter definition. You can use this to build
|
||||||
|
a `nlohmann::json` object and store it as the "value" of your parameter. For examples of how this
|
||||||
|
is done, see `NET_SETTINGS`.
|
||||||
|
|
||||||
|
## NESTED_SETTINGS
|
||||||
|
|
||||||
|
The `NESTED_SETTINGS` class is like a `JSON_SETTINGS` but instead of a file for a backing store, it
|
||||||
|
uses another `JSON_SETTINGS` object. The entire contents of a `NESTED_SETTINGS` are stored as the
|
||||||
|
value of a particular key in the parent file. This has two key benefits:
|
||||||
|
|
||||||
|
1) You can split up large sets of settings in to more manageable pieces
|
||||||
|
2) You can hide knowledge about the nested settings from the parent settings object
|
||||||
|
|
||||||
|
For example, many portions of the project file are stored as `NESTED_SETTINGS` objects inside the
|
||||||
|
`PROJECT_FILE`. These objects, including `SCHEMATIC_SETTINGS`, `NET_SETTINGS`, and
|
||||||
|
`BOARD_DESIGN_SETTINGS`, are compiled as part of eeschema or pcbnew, so they have access to data
|
||||||
|
types not available in common (where `PROJECT_FILE` is compiled).
|
||||||
|
|
||||||
|
When the outer file is loaded, all of the data for the nested settings is there in the underlying
|
||||||
|
`nlohmann::json` data store -- it's just not used until the appropriate `NESTED_SETTINGS` is loaded.
|
||||||
|
|
||||||
|
`NESTED_SETTINGS` objects can have shorter lifecycles than the parent. This is required because in
|
||||||
|
some cases (such as with the project file), the parent can stay resident in one frame (the KiCad
|
||||||
|
manager, for example) while a frame that uses a nested settings inside it can be created and
|
||||||
|
destroyed. When the nested settings is destroyed, it ensures that its data is stored to the JSON
|
||||||
|
data of the parent. The parent `JSON_SETTINGS` can then be saved to disk, if desired.
|
||||||
|
|
||||||
|
## Schema Version and Migrations
|
||||||
|
|
||||||
|
Settings objects have a **schema version**, which is a const integer that can be incremented when a
|
||||||
|
migration is needed. The schema version in the code is compared to that in the loaded file, and if
|
||||||
|
the file version is lower (older), **migrations** are run to bring the data in the file up to date.
|
||||||
|
|
||||||
|
**Migrations** are functions that are responsible for making the necessary changes from one schema
|
||||||
|
version to another. They act on the **underlying JSON data**, before parameter loading has taken
|
||||||
|
place.
|
||||||
|
|
||||||
|
Migrations are not always needed when changing a settings file. You are free to add or remove
|
||||||
|
parameters without changing the schema version or writing migrations. If you add parameters, they
|
||||||
|
will be added to the JSON file and initialized to their default value. If you remove parameters,
|
||||||
|
they will be silently dropped from the JSON file the next time the settings are saved. Migration is
|
||||||
|
only needed when you need to make changes to the JSON file that depend on the current state. For
|
||||||
|
example, if you decide to rename a settings key, but want to preserve the user's present setting.
|
||||||
|
|
||||||
|
If you need to make a "breaking change" to a settings file:
|
||||||
|
|
||||||
|
1) Increment the schema version
|
||||||
|
2) Write a migration that makes the necessary changes to the underlying `nlohmann::json` object
|
||||||
|
3) Call `JSON_SETTINGS::registerMigration` in the constructor for the object
|
||||||
|
|
Loading…
Reference in New Issue