diff --git a/README.md b/README.md index 64cb06248..cc8e12184 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,5 @@ [![JetBrains incubator](https://img.shields.io/badge/JetBrains-incubator-yellow)](https://github.com/JetBrains#jetbrains-on-github) [![CI checks](https://img.shields.io/github/actions/workflow/status/JetBrains/jewel/build.yml?logo=github)](https://github.com/JetBrains/jewel/actions/workflows/build.yml) [![Licensed under Apache 2.0](https://img.shields.io/github/license/JetBrains/jewel)](https://github.com/JetBrains/jewel/blob/main/LICENSE) [![Latest release](https://img.shields.io/github/v/release/JetBrains/jewel?include_prereleases&label=Latest%20Release&logo=github)](https://github.com/JetBrains/jewel/releases/latest) - # Jewel: a Compose for Desktop theme Jewel logo @@ -10,11 +9,14 @@ desktop-optimized theme and set of components. > [!WARNING] > -> This project is in active development, and caution is advised when considering it for production uses. You _can_, -> but you should expect APIs to change often, things to move around and/or break, and all that jazz. Binary -> compatibility is not currently guaranteed across releases, but it is an eventual aim for 1.0, if it is possible. +> This project is in active development, and caution is advised when considering it for production uses. You _can_ use +> it, but you should expect APIs to change often, things to move around and/or break, and all that jazz. Binary +> compatibility is not guaranteed across releases, and APIs are still in flux and subject to change. +> +> Writing 3rd party IntelliJ Plugins in Compose for Desktop is currently **not officially supported** by the IntelliJ +> Platform. It should work, but your mileage may vary, and if things break you're on your own. > -> Use at your own risk! (but have fun if you do!) +> Use at your own risk! Jewel provides stand-alone implementations of the IntelliJ Platform themes that can be used in any Compose for Desktop application, and a Swing LaF Bridge that only works in the IntelliJ Platform (i.e., used to create IDE plugins), but @@ -26,15 +28,27 @@ The project is split in modules: 1. `buildSrc` contains the build logic, including: * The `jewel` and `jewel-publish` configuration plugins + * The `jewel-check-public-api` and `jewel-linting` configuration plugins * The Theme Palette generator plugin -2. `core` contains the foundational Jewel functionality, including the components and their styling primitives -3. `int-ui` implements the standalone version of the IntelliJ New UI, which implements the - ["Int UI" design system](https://www.figma.com/community/file/1227732692272811382/int-ui-kit), and can be used - anywhere -4. `ide-laf-bridge` contains the Swing LaF bridge to use in IntelliJ Platform plugins (see more below) -5. `samples` contains the example apps, which showcase the available components: - 1. `standalone` is a regular CfD app, using the predefined "base" theme definitions - 2. `ide-plugin` is an IntelliJ plugin, adding some UI to the IDE, and showcasing the use of the Swing Bridge + * The Studio Releases generator plugin +2. `foundation` contains the foundational Jewel functionality: + * Basic components without strong styling (e.g., `SelectableLazyColumn`, `BasicLazyTree`) + * The `JewelTheme` interface with a few basic composition locals + * The state management primitives + * The Jewel annotations + * A few other primitives +3. `ui` contains all the styled components and custom painters logic +4. `decorated-window` contains basic, unstyled functionality to have custom window decoration on the JetBrains Runtime +5. `int-ui` contains two modules: + * `int-ui-standalone` has a standalone version of the Int UI styling values that can be used in any Compose for + Desktop app + * `int-ui-decorated-window` has a standalone version of the Int UI styling values for the custom window decoration + that can be used in any Compose for Desktop app +5. `ide-laf-bridge` contains the Swing LaF bridge to use in IntelliJ Platform plugins (see more below) + * The `ide-laf-bridge-*` sub-modules contain code that is specific to a certain IntelliJ Platform version +6. `samples` contains the example apps, which showcase the available components: + * `standalone` is a regular CfD app, using the standalone theme definitions and custom window decoration + * `ide-plugin` is an IntelliJ plugin that showcases the use of the Swing Bridge ### Int UI Standalone theme @@ -46,31 +60,107 @@ to your heart's content. By default, it matches the official Int UI specs. > as it has extra features and patches for UI functionalities that aren't available in other JDKs. > We **do not support** running Jewel on any other JDK. +To use Jewel components in a non-IntelliJ Platform environment, you need to wrap your UI hierarchy in a `IntUiTheme` +composable: + +```kotlin +IntUiTheme(isDark = false) { + // ... +} +``` + +If you want more control over the theming, you can use other `IntUiTheme` overloads, like the standalone sample does. + +#### Custom window decoration +The JetBrains Runtime allows windows to have a custom decoration instead of the regular title bar. + +![A screenshot of the custom window decoration in the standalone sample](art/docs/custom-chrome.png) + +The standalone sample app shows how to easily get something that looks like a JetBrains IDE; if you want to go _very_ +custom, you only need to depend on the `decorated-window` module, which contains all the required primitives, but not +the Int UI styling. + +To get an IntelliJ-like custom title bar, you need to pass the window decoration styling to your theme call, and add the +`DecoratedWindow` composable at the top level of the theme: + +```kotlin +IntUiTheme( + themeDefinition, + componentStyling = { + themeDefinition.decoratedWindowComponentStyling( + titleBarStyle = TitleBarStyle.light() + ) + }, +) { + DecoratedWindow( + onCloseRequest = { exitApplication() }, + ) { + // ... + } +} +``` + ### The Swing Bridge -Jewel includes a crucial element for proper integration with the IDE: a bridge between the Swing components, theme -and LaF, and the Compose world. +Jewel includes a crucial element for proper integration with the IDE: a bridge between the Swing components — theme +and LaF — and the Compose world. This bridge ensures that we pick up the colours, typography, metrics, and images as defined in the current IntelliJ -theme, and apply them to the Compose components as well — at least for themes that use the -standard [IntelliJ theming](https://plugins.jetbrains.com/docs/intellij/themes-getting-started.html) mechanisms. +theme, and apply them to the Compose components as well. This means Jewel will automatically adapt to IntelliJ Platform +themes that use the [standard theming](https://plugins.jetbrains.com/docs/intellij/themes-getting-started.html) mechanisms. > [!NOTE] > IntelliJ themes that use non-standard mechanisms (such as providing custom UI implementations for Swing components) -> are not, and will never, be supported. +> are not, and can never, be supported. -If you're writing an IntelliJ Platform plugin, you should use the `SwingBridgeTheme` instead of a standalone theme. +If you're writing an IntelliJ Platform plugin, you should use the `SwingBridgeTheme` instead of the standalone theme: + +```kotlin +SwingBridgeTheme { + // ... +} +``` #### Accessing icons -When you want to draw an icon from the resources, you should use a `PainterProvider`. Reading an icon from the IDE is -as easy as using the `retrieveStatefulIcon()` and `retrieveStatelessIcon()`: +When you want to draw an icon from the resources, you can either use the `Icon` composable and pass it the resource path +and the corresponding class to look up the classpath from, or go one lever deeper and use the lower level, +`Painter`-based API. + +The `Icon` approach looks like this: ```kotlin -val svgLoader = service().svgLoader -val painterProvider = retrieveStatelessIcon("icons/bot-toolwindow.svg", svgLoader, iconData) +// Load the "close" icon from the IDE's AllIcons class +Icon( + "actions/close.svg", + iconClass = AllIcons::class.java, + contentDescription = "Close", +) ``` +To obtain a `Painter`, instead, you'd use: + +```kotlin +val painterProvider = rememberResourcePainterProvider( + path = "actions/close.svg", + iconClass = AllIcons::class.java +) +val painter by painterProvider.getPainter() +``` + +#### Icon runtime patching +Jewel emulates the under-the-hood machinations that happen in the IntelliJ Platform when loading icons. Specifically, +the resource will be subject to some transformations before being loaded. + +For example, in the IDE, if New UI is active, the icon path may be replaced with a different one. Some key colors in SVG +icons will also be replaced based on the current theme. See +[the docs](https://plugins.jetbrains.com/docs/intellij/work-with-icons-and-images.html#new-ui-icons). + +Beyond that, even in standalone, Jewel will pick up icons with the appropriate dark/light variant for the current theme, +and for bitmap icons it will try to pick the 2x variants based on the `LocalDensity`. + +If you want to learn more about this system, the entry point is the `PainterHint` class. + ### Swing interoperability As this is Compose for Desktop, you get a good degree of interoperability with Swing. To avoid glitches and z-order @@ -79,7 +169,7 @@ issues, you should enable the before you initialize Compose content. The `ToolWindow.addComposeTab()` extension function provided by the `ide-laf-bridge` module will take care of that for -you, but if you want to also enable it in other scenarios and in standalone applications, you can call the +you. However, if you want to also enable it in other scenarios and in standalone applications, you can call the `enableNewSwingCompositing()` function in your Compose entry points (that is, right before creating a `ComposePanel`). > [!NOTE] diff --git a/art/docs/custom-chrome.png b/art/docs/custom-chrome.png new file mode 100644 index 000000000..a8a331a18 Binary files /dev/null and b/art/docs/custom-chrome.png differ