diff --git a/code_samples/settings/README.md b/code_samples/settings/README.md index cef98198a..84927e9e7 100644 --- a/code_samples/settings/README.md +++ b/code_samples/settings/README.md @@ -4,8 +4,8 @@ ## Quickstart This project illustrates a custom Application-level Settings through the implementation of: -- `AppSettingsConfigurable` is analogous to a Controller in the MVC model - it interacts with the other two Settings classes and the IntelliJ Platform, -- `AppSettingsState` is like a Model because it stores the Settings persistently, +- `AppSettingsConfigurable` is analogous to a Controller in the MVC model – it interacts with the other two Settings classes and the IntelliJ Platform, +- `AppSettings` is like a Model because it stores the Settings persistently, - `AppSettingsComponent` is similar to a View because it displays and captures edits to the values of the Settings. ### Extension Points @@ -13,7 +13,7 @@ This project illustrates a custom Application-level Settings through the impleme | Name | Implementation | Extension Point Class | |----------------------------------------|---------------------------------------------------------|----------------------------| | `com.intellij.applicationConfigurable` | [AppSettingsConfigurable][file:AppSettingsConfigurable] | `Configurable` | -| `com.intellij.applicationService` | [AppSettingsState][file:AppSettingsState] | `PersistentStateComponent` | +| `com.intellij.applicationService` | [AppSettings][file:AppSettings] | `PersistentStateComponent` | *Reference: [Plugin Extension Points in IntelliJ SDK Docs][docs:ep]* @@ -23,5 +23,4 @@ This project illustrates a custom Application-level Settings through the impleme [docs:ep]: https://plugins.jetbrains.com/docs/intellij/plugin-extensions.html [file:AppSettingsConfigurable]: ./src/main/java/org/intellij/sdk/settings/AppSettingsConfigurable.java -[file:AppSettingsState]: ./src/main/java/org/intellij/sdk/settings/AppSettingsState.java - +[file:AppSettings]: ./src/main/java/org/intellij/sdk/settings/AppSettings.java diff --git a/code_samples/settings/src/main/java/org/intellij/sdk/settings/AppSettings.java b/code_samples/settings/src/main/java/org/intellij/sdk/settings/AppSettings.java new file mode 100644 index 000000000..ac8a3ac08 --- /dev/null +++ b/code_samples/settings/src/main/java/org/intellij/sdk/settings/AppSettings.java @@ -0,0 +1,49 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. + +package org.intellij.sdk.settings; + +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.components.PersistentStateComponent; +import com.intellij.openapi.components.State; +import com.intellij.openapi.components.Storage; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +/* + * Supports storing the application settings in a persistent way. + * The {@link com.intellij.openapi.components.State State} and {@link Storage} + * annotations define the name of the data and the filename where these persistent + * application settings are stored. + */ + +@State( + name = "org.intellij.sdk.settings.AppSettings", + storages = @Storage("SdkSettingsPlugin.xml") +) +final class AppSettings + implements PersistentStateComponent { + + static class State { + @NonNls + public String userId = "John Smith"; + public boolean ideaStatus = false; + } + + private State myState = new State(); + + static AppSettings getInstance() { + return ApplicationManager.getApplication() + .getService(AppSettings.class); + } + + @Override + public State getState() { + return myState; + } + + @Override + public void loadState(@NotNull State state) { + myState = state; + } + +} diff --git a/code_samples/settings/src/main/java/org/intellij/sdk/settings/AppSettingsComponent.java b/code_samples/settings/src/main/java/org/intellij/sdk/settings/AppSettingsComponent.java index 1de5e949c..e3f35c592 100644 --- a/code_samples/settings/src/main/java/org/intellij/sdk/settings/AppSettingsComponent.java +++ b/code_samples/settings/src/main/java/org/intellij/sdk/settings/AppSettingsComponent.java @@ -1,4 +1,4 @@ -// Copyright 2000-2022 JetBrains s.r.o. and other contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package org.intellij.sdk.settings; @@ -17,11 +17,11 @@ public class AppSettingsComponent { private final JPanel myMainPanel; private final JBTextField myUserNameText = new JBTextField(); - private final JBCheckBox myIdeaUserStatus = new JBCheckBox("Do you use IntelliJ IDEA? "); + private final JBCheckBox myIdeaUserStatus = new JBCheckBox("IntelliJ IDEA user"); public AppSettingsComponent() { myMainPanel = FormBuilder.createFormBuilder() - .addLabeledComponent(new JBLabel("Enter user name: "), myUserNameText, 1, false) + .addLabeledComponent(new JBLabel("User name:"), myUserNameText, 1, false) .addComponent(myIdeaUserStatus, 1) .addComponentFillVertically(new JPanel(), 0) .getPanel(); diff --git a/code_samples/settings/src/main/java/org/intellij/sdk/settings/AppSettingsConfigurable.java b/code_samples/settings/src/main/java/org/intellij/sdk/settings/AppSettingsConfigurable.java index d8fb641e7..e38a60874 100644 --- a/code_samples/settings/src/main/java/org/intellij/sdk/settings/AppSettingsConfigurable.java +++ b/code_samples/settings/src/main/java/org/intellij/sdk/settings/AppSettingsConfigurable.java @@ -6,6 +6,7 @@ import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.Nullable; import javax.swing.*; +import java.util.Objects; /** * Provides controller functionality for application settings. @@ -14,8 +15,8 @@ final class AppSettingsConfigurable implements Configurable { private AppSettingsComponent mySettingsComponent; - // A default constructor with no arguments is required because this implementation - // is registered in an applicationConfigurable EP + // A default constructor with no arguments is required because + // this implementation is registered as an applicationConfigurable @Nls(capitalization = Nls.Capitalization.Title) @Override @@ -37,24 +38,26 @@ final class AppSettingsConfigurable implements Configurable { @Override public boolean isModified() { - AppSettingsState settings = AppSettingsState.getInstance(); - boolean modified = !mySettingsComponent.getUserNameText().equals(settings.userId); - modified |= mySettingsComponent.getIdeaUserStatus() != settings.ideaStatus; - return modified; + AppSettings.State state = + Objects.requireNonNull(AppSettings.getInstance().getState()); + return !mySettingsComponent.getUserNameText().equals(state.userId) || + mySettingsComponent.getIdeaUserStatus() != state.ideaStatus; } @Override public void apply() { - AppSettingsState settings = AppSettingsState.getInstance(); - settings.userId = mySettingsComponent.getUserNameText(); - settings.ideaStatus = mySettingsComponent.getIdeaUserStatus(); + AppSettings.State state = + Objects.requireNonNull(AppSettings.getInstance().getState()); + state.userId = mySettingsComponent.getUserNameText(); + state.ideaStatus = mySettingsComponent.getIdeaUserStatus(); } @Override public void reset() { - AppSettingsState settings = AppSettingsState.getInstance(); - mySettingsComponent.setUserNameText(settings.userId); - mySettingsComponent.setIdeaUserStatus(settings.ideaStatus); + AppSettings.State state = + Objects.requireNonNull(AppSettings.getInstance().getState()); + mySettingsComponent.setUserNameText(state.userId); + mySettingsComponent.setIdeaUserStatus(state.ideaStatus); } @Override diff --git a/code_samples/settings/src/main/java/org/intellij/sdk/settings/AppSettingsState.java b/code_samples/settings/src/main/java/org/intellij/sdk/settings/AppSettingsState.java deleted file mode 100644 index 665cc58de..000000000 --- a/code_samples/settings/src/main/java/org/intellij/sdk/settings/AppSettingsState.java +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. - -package org.intellij.sdk.settings; - -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.components.PersistentStateComponent; -import com.intellij.openapi.components.State; -import com.intellij.openapi.components.Storage; -import com.intellij.util.xmlb.XmlSerializerUtil; -import org.jetbrains.annotations.NotNull; - -/** - * Supports storing the application settings in a persistent way. - * The {@link State} and {@link Storage} annotations define the name of the data and the file name where - * these persistent application settings are stored. - */ -@State( - name = "org.intellij.sdk.settings.AppSettingsState", - storages = @Storage("SdkSettingsPlugin.xml") -) -final class AppSettingsState implements PersistentStateComponent { - - public String userId = "John Q. Public"; - public boolean ideaStatus = false; - - static AppSettingsState getInstance() { - return ApplicationManager.getApplication().getService(AppSettingsState.class); - } - - @Override - public AppSettingsState getState() { - return this; - } - - @Override - public void loadState(@NotNull AppSettingsState state) { - XmlSerializerUtil.copyBean(state, this); - } - -} diff --git a/code_samples/settings/src/main/resources/META-INF/plugin.xml b/code_samples/settings/src/main/resources/META-INF/plugin.xml index 1d3c6b92b..575dadd5a 100644 --- a/code_samples/settings/src/main/resources/META-INF/plugin.xml +++ b/code_samples/settings/src/main/resources/META-INF/plugin.xml @@ -1,5 +1,4 @@ - - + @@ -35,7 +34,7 @@ - + diff --git a/images/tutorials/img/settings_defaults.png b/images/tutorials/img/settings_defaults.png index cc9d177e5..31cabe644 100644 Binary files a/images/tutorials/img/settings_defaults.png and b/images/tutorials/img/settings_defaults.png differ diff --git a/images/tutorials/img/settings_persisted.png b/images/tutorials/img/settings_persisted.png deleted file mode 100644 index b3b19c7a8..000000000 Binary files a/images/tutorials/img/settings_persisted.png and /dev/null differ diff --git a/topics/tutorials/settings_tutorial.md b/topics/tutorials/settings_tutorial.md index 9386b2be6..3cd8777e8 100644 --- a/topics/tutorials/settings_tutorial.md +++ b/topics/tutorials/settings_tutorial.md @@ -13,7 +13,7 @@ Custom Settings are displayed and function just like those native to the IDE. Using the SDK code sample [`settings`](%gh-sdk-samples-master%/settings), this tutorial illustrates the steps to create custom Application-level Settings. Many IntelliJ Platform Settings implementations use fewer classes, but the `settings` code sample factors the functionality into three classes for clarity: * The [`AppSettingsConfigurable`](%gh-sdk-samples-master%/settings/src/main/java/org/intellij/sdk/settings/AppSettingsConfigurable.java) is analogous to a Controller in the MVC model - it interacts with the other two Settings classes and the IntelliJ Platform, -* The [`AppSettingsState`](%gh-sdk-samples-master%/settings/src/main/java/org/intellij/sdk/settings/AppSettingsState.java) is like a Model because it stores the Settings persistently, +* The [`AppSettings`](%gh-sdk-samples-master%/settings/src/main/java/org/intellij/sdk/settings/AppSettings.java) is like a Model because it stores the Settings persistently, * The [`AppSettingsComponent`](%gh-sdk-samples-master%/settings/src/main/java/org/intellij/sdk/settings/AppSettingsComponent.java) is similar to a View because it displays and captures edits to the values of the Settings. The structure of the implementation is the same for Project Settings, but there are minor differences in the [`Configurable` implementation](settings_guide.md#constructors) and [extension point (EP) declaration](settings_guide.md#declaring-project-settings). @@ -26,12 +26,12 @@ The structure of the implementation is the same for Project Settings, but there > {style="note"} -## The `AppSettingsState` Class +## The `AppSettings` Class -The `AppSettingsState` class persistently stores the custom Settings. +The `AppSettings` class persistently stores the custom Settings. It is based on the [IntelliJ Platform Persistence Model](persisting_state_of_components.md#using-persistentstatecomponent). -### Declaring `AppSettingsState` +### Declaring `AppSettings` Given a [Light Service](plugin_services.md#light-services) is not used, the persistent data class must be declared as a [Service](plugin_services.md#declaring-a-service) EP in the [plugin.xml](plugin_configuration_file.md) file. If these were Project Settings, the `com.intellij.projectService` EP would be used. @@ -40,40 +40,40 @@ However, because these are Application Settings, the `com.intellij.applicationSe ```xml + serviceImplementation="org.intellij.sdk.settings.AppSettings"/> ``` -### Creating the `AppSettingState` Implementation +### Creating the `AppSettings` Implementation -As discussed in [Implementing the PersistentStateComponent Interface](persisting_state_of_components.md#implementing-the-persistentstatecomponent-interface), `AppSettingsState` uses the pattern of implementing [`PersistentStateComponent`](%gh-ic%/platform/projectModel-api/src/com/intellij/openapi/components/PersistentStateComponent.java) itself: +As discussed in [Implementing the PersistentStateComponent Interface](persisting_state_of_components.md#implementing-the-persistentstatecomponent-interface), `AppSettings` uses the pattern of implementing [`PersistentStateComponent`](%gh-ic%/platform/projectModel-api/src/com/intellij/openapi/components/PersistentStateComponent.java) parameterized by a separate state class: ```java ``` -{src="settings/src/main/java/org/intellij/sdk/settings/AppSettingsState.java" include-symbol="AppSettingsState"} +{src="settings/src/main/java/org/intellij/sdk/settings/AppSettings.java" include-symbol="AppSettings"} #### `@Storage` Annotation The [`@State`](%gh-ic%/platform/projectModel-api/src/com/intellij/openapi/components/State.java) annotation, located just above the class declaration, [defines the data storage location](persisting_state_of_components.md#defining-the-storage-location). -For `AppSettingsState`, the data `name` parameter is the FQN of the class. +For `AppSettings`, the data `name` parameter is the FQN of the class. Using FQN is the best practice to follow, and is required if custom data gets stored in the standard project or workspace files. -The `storages` parameter utilizes the [`@Storage`](%gh-ic%/platform/projectModel-api/src/com/intellij/openapi/components/Storage.java) annotation to define a custom file name for the `AppSettingsState` data. +The `storages` parameter utilizes the [`@Storage`](%gh-ic%/platform/projectModel-api/src/com/intellij/openapi/components/Storage.java) annotation to define a custom file name for the `AppSettings` data. In this case, the file is located in the `options` directory of the [configuration directory](https://www.jetbrains.com/help/idea/tuning-the-ide.html#config-directory) for the IDE. -#### Persistent Data Fields +#### Persistent State Class -The `AppSettingState` implementation has two public fields: a `String` and a `boolean`. -Conceptually, these fields hold the name of a user, and whether that person is an IntelliJ IDEA user, respectively. +The `AppSettings` implementation contains an inner state class with two public fields: a `String` and a `boolean`. +Conceptually, these fields hold the name of a user and whether that person is an IntelliJ IDEA user, respectively. See [Implementing the State Class](persisting_state_of_components.md#implementing-the-state-class) for more information about how `PersistentStateComponent` serializes public fields. -#### `AppSettingState` Methods +#### `AppSettings` Methods The fields are so limited and straightforward for this class that encapsulation is not used for simplicity. All that's needed for functionality is to override the two methods called by the IntelliJ Platform when a new component state is loaded (`PersistentStateComponent.loadState()`), and when a state is saved (`PersistentStateComponent.getState()`). See [`PersistentStateComponent`](%gh-ic%/platform/projectModel-api/src/com/intellij/openapi/components/PersistentStateComponent.java) for more information about these methods. -One static convenience method has been added - `AppSettingState.getInstance()` - which allows `AppSettingsConfigurable` to easily acquire a reference to `AppSettingState`. +One static convenience method has been added - `AppSettings.getInstance()` - which allows `AppSettingsConfigurable` to easily acquire a reference to `AppSetting`. ## The `AppSettingsComponent` Class @@ -83,7 +83,7 @@ The `AppSettingsComponent` is instantiated by `AppSettingsConfigurable`. ### Creating the `AppSettingsComponent` Implementation -The `AppSettingsComponent` defines a `JPanel` containing a [`JBTextField`](%gh-ic%/platform/platform-api/src/com/intellij/ui/components/JBTextField.java) and a [`JBCheckBox`](%gh-ic%/platform/platform-api/src/com/intellij/ui/components/JBCheckBox.java) to hold and display the data that maps to the [data fields](#persistent-data-fields) of `AppSettingsState`: +The `AppSettingsComponent` defines a `JPanel` containing a [`JBTextField`](%gh-ic%/platform/platform-api/src/com/intellij/ui/components/JBTextField.java) and a [`JBCheckBox`](%gh-ic%/platform/platform-api/src/com/intellij/ui/components/JBCheckBox.java) to hold and display the data that maps to the [data fields of `AppSettings.State`](#persistent-state-class): ```java ``` @@ -96,7 +96,7 @@ The rest of the class are simple accessors and mutators to encapsulate the UI co ## The `AppSettingsConfigurable` Class -The methods of [`AppSettingsConfigurable`](%gh-sdk-samples-master%/settings/src/main/java/org/intellij/sdk/settings/AppSettingsConfigurable.java) are called by the IntelliJ Platform, and `AppSettingsConfigurable` in turn interacts with `AppSettingsComponent` and `AppSettingState`. +The methods of [`AppSettingsConfigurable`](%gh-sdk-samples-master%/settings/src/main/java/org/intellij/sdk/settings/AppSettingsConfigurable.java) are called by the IntelliJ Platform, and `AppSettingsConfigurable` in turn interacts with `AppSettingsComponent` and `AppSettings`. ### Declaring the `AppSettingsConfigurable` @@ -134,13 +134,11 @@ After performing the steps described above, compile and run the plugin in a Deve Open the IDE Settings by selecting Settings | Tools | SDK: Application Settings Example. The settings are preloaded with the default values: -!["Settings Defaults"](settings_defaults.png){width="600"} +!["Settings Defaults"](settings_defaults.png){width="700"} Now edit the settings values to "John Doe" and click the checkbox. Click on the OK button to close the Settings dialog and save the changes. Exit the Development Instance. -Open the file SdkSettingsPlugin.xml to see the Settings persistently stored. +Open the file code_samples/settings/build/idea-sandbox/config/options/SdkSettingsPlugin.xml to see the Settings persistently stored. In this demonstration the file resides in code_samples/settings/build/idea-sandbox/config/options/, but see [IDE Development Instances](ide_development_instance.md) for the general Development Instance case, or [Default IDE directories](https://www.jetbrains.com/help/idea/tuning-the-ide.html#default-dirs) if you are testing the `settings` plugin directly in an IDE. - -!["Persisted Settings"](settings_persisted.png){width="600"}