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.
Source Code Guide
Most of the relevant code is in common/settings
and common/project
.
C++ Class | Description |
---|---|
|
Loads and unloads settings files and projects |
|
The base class for all settings objects. Represents a single JSON file. |
|
A |
|
A parameter: helper class for storing data inside a |
|
Base class for application (frame) settings |
|
A subclass of |
|
The settings available to every part of KiCad |
|
A |
|
A |
Where Settings are Stored
There are four main places a setting might be stored:
-
In
COMMON_SETTINGS
: this is a setting that is shared between all parts of KiCad. -
In an application settings object (subclass of
APP_SETTINGS_BASE
). These objects, such asEESCHEMA_SETTINGS
andPCBNEW_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 ofcommon
. -
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 aPROJECT
object will always be available. Because of this, theSETTINGS_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. -
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. 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:
-
You can split up large sets of settings in to more manageable pieces
-
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:
-
Increment the schema version
-
Write a migration that makes the necessary changes to the underlying
nlohmann::json
object -
Call
JSON_SETTINGS::registerMigration
in the constructor for the object