m);
-
- /**
- * Windows & macOS properties:
- *
- * - {@code controls.visible} : {@link Boolean} - whether title bar controls
- * (minimize/maximize/close buttons) are visible, default = true.
- *
- * Windows properties:
- *
- * - {@code controls.width} : {@link Number} - width of block of buttons (not individual buttons).
- * Note that dialogs have only one button, while frames usually have 3 of them.
- * - {@code controls.dark} : {@link Boolean} - whether to use dark or light color theme
- * (light or dark icons respectively).
- * - {@code controls..} : {@link Color} - precise control over button colors,
- * where {@code } is one of:
- *
- {@code foreground}
- {@code background}
- * and {@code } is one of:
- *
- * - {@code normal}
- * - {@code hovered}
- * - {@code pressed}
- * - {@code disabled}
- * - {@code inactive}
- *
- *
- */
- void putProperty(String key, Object value);
-
- /**
- * @return space occupied by title bar controls on the left (px)
- */
- float getLeftInset();
- /**
- * @return space occupied by title bar controls on the right (px)
- */
- float getRightInset();
-
- /**
- * By default, any component which has no cursor or mouse event listeners set is considered transparent for
- * native title bar actions. That is, dragging simple JPanel in title bar area will drag the
- * window, but dragging a JButton will not. Adding mouse listener to a component will prevent any native actions
- * inside bounds of that component.
- *
- * This method gives you precise control of whether to allow native title bar actions or not.
- *
- * - {@code client=true} means that mouse is currently over a client area. Native title bar behavior is disabled.
- * - {@code client=false} means that mouse is currently over a non-client area. Native title bar behavior is enabled.
- *
- * Intended usage:
- *
- * - This method must be called in response to all {@linkplain java.awt.event.MouseEvent mouse events}
- * except {@link java.awt.event.MouseEvent#MOUSE_EXITED} and {@link java.awt.event.MouseEvent#MOUSE_WHEEL}.
- * - This method is called per-event, i.e. when component has multiple listeners, you only need to call it once.
- * - If this method hadn't been called, title bar behavior is reverted back to default upon processing the event.
- *
- * Note that hit test value is relevant only for title bar area, e.g. calling
- * {@code forceHitTest(false)} will not make window draggable via non-title bar area.
- *
- * Example:
- * Suppose you have a {@code JPanel} in the title bar area. You want it to respond to right-click for
- * some popup menu, but also retain native drag and double-click behavior.
- *
- * CustomTitleBar titlebar = ...;
- * JPanel panel = ...;
- * MouseAdapter adapter = new MouseAdapter() {
- * private void hit() { titlebar.forceHitTest(false); }
- * public void mouseClicked(MouseEvent e) {
- * hit();
- * if (e.getButton() == MouseEvent.BUTTON3) ...;
- * }
- * public void mousePressed(MouseEvent e) { hit(); }
- * public void mouseReleased(MouseEvent e) { hit(); }
- * public void mouseEntered(MouseEvent e) { hit(); }
- * public void mouseDragged(MouseEvent e) { hit(); }
- * public void mouseMoved(MouseEvent e) { hit(); }
- * };
- * panel.addMouseListener(adapter);
- * panel.addMouseMotionListener(adapter);
- *
- */
- void forceHitTest(boolean client);
-
- Window getContainingWindow();
- }
-}
diff --git a/decorated-window/src/main/java/com/jetbrains/WindowMove.java b/decorated-window/src/main/java/com/jetbrains/WindowMove.java
deleted file mode 100644
index 5f241303c0..0000000000
--- a/decorated-window/src/main/java/com/jetbrains/WindowMove.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright 2000-2023 JetBrains s.r.o.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package com.jetbrains;
-
-import java.awt.*;
-
-public interface WindowMove {
- /**
- * Starts moving the top-level parent window of the given window together with the mouse pointer.
- * The intended use is to facilitate the implementation of window management similar to the way
- * it is done natively on the platform.
- *
- * Preconditions for calling this method:
- *
- * - WM supports _NET_WM_MOVE_RESIZE (this is checked automatically when an implementation
- * of this interface is obtained).
- * - Mouse pointer is within this window's bounds.
- * - The mouse button specified by {@code mouseButton} is pressed.
- *
- *
- * Calling this method will make the window start moving together with the mouse pointer until
- * the specified mouse button is released or Esc is pressed. The conditions for cancelling
- * the move may differ between WMs.
- *
- * @param mouseButton indicates the mouse button that was pressed to start moving the window;
- * must be one of {@code MouseEvent.BUTTON1}, {@code MouseEvent.BUTTON2},
- * or {@code MouseEvent.BUTTON3}.
- */
- void startMovingTogetherWithMouse(Window window, int mouseButton);
-}
diff --git a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/DecoratedWindow.kt b/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/DecoratedWindow.kt
deleted file mode 100644
index 6abc043863..0000000000
--- a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/DecoratedWindow.kt
+++ /dev/null
@@ -1,289 +0,0 @@
-package org.jetbrains.jewel.window
-
-import androidx.compose.foundation.layout.padding
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.CompositionLocalProvider
-import androidx.compose.runtime.DisposableEffect
-import androidx.compose.runtime.Immutable
-import androidx.compose.runtime.ProvidableCompositionLocal
-import androidx.compose.runtime.Stable
-import androidx.compose.runtime.compositionLocalOf
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.awt.ComposeWindow
-import androidx.compose.ui.graphics.RectangleShape
-import androidx.compose.ui.graphics.painter.Painter
-import androidx.compose.ui.input.key.KeyEvent
-import androidx.compose.ui.layout.Layout
-import androidx.compose.ui.layout.Measurable
-import androidx.compose.ui.layout.MeasurePolicy
-import androidx.compose.ui.layout.MeasureResult
-import androidx.compose.ui.layout.MeasureScope
-import androidx.compose.ui.layout.Placeable
-import androidx.compose.ui.layout.layoutId
-import androidx.compose.ui.unit.Constraints
-import androidx.compose.ui.unit.offset
-import androidx.compose.ui.window.FrameWindowScope
-import androidx.compose.ui.window.Window
-import androidx.compose.ui.window.WindowPlacement
-import androidx.compose.ui.window.WindowState
-import androidx.compose.ui.window.rememberWindowState
-import com.jetbrains.JBR
-import org.jetbrains.jewel.foundation.Stroke
-import org.jetbrains.jewel.foundation.modifier.border
-import org.jetbrains.jewel.foundation.modifier.trackWindowActivation
-import org.jetbrains.jewel.foundation.theme.JewelTheme
-import org.jetbrains.jewel.window.styling.DecoratedWindowStyle
-import org.jetbrains.jewel.window.utils.DesktopPlatform
-import java.awt.event.ComponentEvent
-import java.awt.event.ComponentListener
-import java.awt.event.WindowAdapter
-import java.awt.event.WindowEvent
-
-@Composable
-public fun DecoratedWindow(
- onCloseRequest: () -> Unit,
- state: WindowState = rememberWindowState(),
- visible: Boolean = true,
- title: String = "",
- icon: Painter? = null,
- resizable: Boolean = true,
- enabled: Boolean = true,
- focusable: Boolean = true,
- alwaysOnTop: Boolean = false,
- onPreviewKeyEvent: (KeyEvent) -> Boolean = { false },
- onKeyEvent: (KeyEvent) -> Boolean = { false },
- style: DecoratedWindowStyle = JewelTheme.defaultDecoratedWindowStyle,
- content: @Composable DecoratedWindowScope.() -> Unit,
-) {
- remember {
- if (!JBR.isAvailable()) {
- error(
- "DecoratedWindow can only be used on JetBrainsRuntime(JBR) platform, " +
- "please check the document https://github.com/JetBrains/jewel#int-ui-standalone-theme",
- )
- }
- }
-
- // Using undecorated window for linux
- val undecorated = DesktopPlatform.Linux == DesktopPlatform.Current
-
- Window(
- onCloseRequest,
- state,
- visible,
- title,
- icon,
- undecorated,
- transparent = false,
- resizable,
- enabled,
- focusable,
- alwaysOnTop,
- onPreviewKeyEvent,
- onKeyEvent,
- ) {
- var decoratedWindowState by remember { mutableStateOf(DecoratedWindowState.of(window)) }
-
- DisposableEffect(window) {
- val adapter = object : WindowAdapter(), ComponentListener {
- override fun windowActivated(e: WindowEvent?) {
- decoratedWindowState = DecoratedWindowState.of(window)
- }
-
- override fun windowDeactivated(e: WindowEvent?) {
- decoratedWindowState = DecoratedWindowState.of(window)
- }
-
- override fun windowIconified(e: WindowEvent?) {
- decoratedWindowState = DecoratedWindowState.of(window)
- }
-
- override fun windowDeiconified(e: WindowEvent?) {
- decoratedWindowState = DecoratedWindowState.of(window)
- }
-
- override fun windowStateChanged(e: WindowEvent) {
- decoratedWindowState = DecoratedWindowState.of(window)
- }
-
- override fun componentResized(e: ComponentEvent?) {
- decoratedWindowState = DecoratedWindowState.of(window)
- }
-
- override fun componentMoved(e: ComponentEvent?) {
- // Empty
- }
-
- override fun componentShown(e: ComponentEvent?) {
- // Empty
- }
-
- override fun componentHidden(e: ComponentEvent?) {
- // Empty
- }
- }
-
- window.addWindowListener(adapter)
- window.addWindowStateListener(adapter)
- window.addComponentListener(adapter)
-
- onDispose {
- window.removeWindowListener(adapter)
- window.removeWindowStateListener(adapter)
- window.removeComponentListener(adapter)
- }
- }
-
- val undecoratedWindowBorder =
- if (undecorated && !decoratedWindowState.isMaximized) {
- Modifier.border(
- Stroke.Alignment.Inside,
- style.metrics.borderWidth,
- style.colors.borderFor(decoratedWindowState).value,
- RectangleShape,
- ).padding(style.metrics.borderWidth)
- } else {
- Modifier
- }
-
- CompositionLocalProvider(
- LocalTitleBarInfo provides TitleBarInfo(title, icon),
- ) {
- Layout(
- content = {
- val scope = object : DecoratedWindowScope {
- override val state: DecoratedWindowState
- get() = decoratedWindowState
-
- override val window: ComposeWindow
- get() = this@Window.window
- }
- scope.content()
- },
- modifier = undecoratedWindowBorder.trackWindowActivation(window),
- measurePolicy = DecoratedWindowMeasurePolicy,
- )
- }
- }
-}
-
-@Stable
-public interface DecoratedWindowScope : FrameWindowScope {
-
- override val window: ComposeWindow
-
- public val state: DecoratedWindowState
-}
-
-private object DecoratedWindowMeasurePolicy : MeasurePolicy {
-
- override fun MeasureScope.measure(measurables: List, constraints: Constraints): MeasureResult {
- if (measurables.isEmpty()) {
- return layout(width = constraints.minWidth, height = constraints.minHeight) {}
- }
-
- val titleBars = measurables.filter { it.layoutId == TITLE_BAR_LAYOUT_ID }
- if (titleBars.size > 1) {
- error("Window just can have only one title bar")
- }
- val titleBar = titleBars.firstOrNull()
- val titleBarBorder = measurables.firstOrNull { it.layoutId == TITLE_BAR_BORDER_LAYOUT_ID }
-
- val contentConstraints = constraints.copy(minWidth = 0, minHeight = 0)
-
- val titleBarPlaceable = titleBar?.measure(contentConstraints)
- val titleBarHeight = titleBarPlaceable?.height ?: 0
-
- val titleBarBorderPlaceable = titleBarBorder?.measure(contentConstraints)
- val titleBarBorderHeight = titleBarBorderPlaceable?.height ?: 0
-
- val measuredPlaceable = mutableListOf()
-
- for (it in measurables) {
- if (it.layoutId.toString().startsWith(TITLE_BAR_COMPONENT_LAYOUT_ID_PREFIX)) continue
- val offsetConstraints = contentConstraints.offset(vertical = -titleBarHeight - titleBarBorderHeight)
- val placeable = it.measure(offsetConstraints)
- measuredPlaceable += placeable
- }
-
- return layout(constraints.maxWidth, constraints.maxHeight) {
- titleBarPlaceable?.placeRelative(0, 0)
- titleBarBorderPlaceable?.placeRelative(0, titleBarHeight)
-
- measuredPlaceable.forEach { it.placeRelative(0, titleBarHeight + titleBarBorderHeight) }
- }
- }
-}
-
-@Immutable
-@JvmInline
-public value class DecoratedWindowState(public val state: ULong) {
-
- public val isActive: Boolean
- get() = state and Active != 0UL
-
- public val isFullscreen: Boolean
- get() = state and Fullscreen != 0UL
-
- public val isMinimized: Boolean
- get() = state and Minimize != 0UL
-
- public val isMaximized: Boolean
- get() = state and Maximize != 0UL
-
- public fun copy(
- fullscreen: Boolean = isFullscreen,
- minimized: Boolean = isMinimized,
- maximized: Boolean = isMaximized,
- active: Boolean = isActive,
- ): DecoratedWindowState =
- of(
- fullscreen = fullscreen,
- minimized = minimized,
- maximized = maximized,
- active = active,
- )
-
- override fun toString(): String =
- "${javaClass.simpleName}(isFullscreen=$isFullscreen, isActive=$isActive)"
-
- public companion object {
-
- public val Active: ULong = 1UL shl 0
- public val Fullscreen: ULong = 1UL shl 1
- public val Minimize: ULong = 1UL shl 2
- public val Maximize: ULong = 1UL shl 3
-
- public fun of(
- fullscreen: Boolean = false,
- minimized: Boolean = false,
- maximized: Boolean = false,
- active: Boolean = true,
- ): DecoratedWindowState =
- DecoratedWindowState(
- (if (fullscreen) Fullscreen else 0UL) or
- (if (minimized) Minimize else 0UL) or
- (if (maximized) Maximize else 0UL) or
- (if (active) Active else 0UL),
- )
-
- public fun of(window: ComposeWindow): DecoratedWindowState =
- of(
- fullscreen = window.placement == WindowPlacement.Fullscreen,
- minimized = window.isMinimized,
- maximized = window.placement == WindowPlacement.Maximized,
- active = window.isActive,
- )
- }
-}
-
-internal data class TitleBarInfo(val title: String, val icon: Painter?)
-
-internal val LocalTitleBarInfo: ProvidableCompositionLocal =
- compositionLocalOf {
- error("LocalTitleBarInfo not provided, TitleBar must be used in DecoratedWindow")
- }
diff --git a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/Theme.kt b/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/Theme.kt
deleted file mode 100644
index 93d672e515..0000000000
--- a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/Theme.kt
+++ /dev/null
@@ -1,17 +0,0 @@
-package org.jetbrains.jewel.window
-
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.ReadOnlyComposable
-import org.jetbrains.jewel.foundation.theme.JewelTheme
-import org.jetbrains.jewel.window.styling.DecoratedWindowStyle
-import org.jetbrains.jewel.window.styling.LocalDecoratedWindowStyle
-import org.jetbrains.jewel.window.styling.LocalTitleBarStyle
-import org.jetbrains.jewel.window.styling.TitleBarStyle
-
-public val JewelTheme.Companion.defaultTitleBarStyle: TitleBarStyle
- @Composable @ReadOnlyComposable
- get() = LocalTitleBarStyle.current
-
-public val JewelTheme.Companion.defaultDecoratedWindowStyle: DecoratedWindowStyle
- @Composable @ReadOnlyComposable
- get() = LocalDecoratedWindowStyle.current
diff --git a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/TitleBar.Linux.kt b/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/TitleBar.Linux.kt
deleted file mode 100644
index 04e50248a1..0000000000
--- a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/TitleBar.Linux.kt
+++ /dev/null
@@ -1,134 +0,0 @@
-package org.jetbrains.jewel.window
-
-import androidx.compose.foundation.focusable
-import androidx.compose.foundation.layout.PaddingValues
-import androidx.compose.foundation.layout.size
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.input.pointer.PointerButton
-import androidx.compose.ui.input.pointer.PointerEventPass
-import androidx.compose.ui.input.pointer.PointerEventType
-import androidx.compose.ui.input.pointer.onPointerEvent
-import androidx.compose.ui.platform.LocalViewConfiguration
-import androidx.compose.ui.unit.dp
-import com.jetbrains.JBR
-import org.jetbrains.jewel.foundation.theme.JewelTheme
-import org.jetbrains.jewel.ui.component.Icon
-import org.jetbrains.jewel.ui.component.IconButton
-import org.jetbrains.jewel.ui.component.styling.IconButtonStyle
-import org.jetbrains.jewel.ui.painter.PainterHint
-import org.jetbrains.jewel.ui.painter.PainterProvider
-import org.jetbrains.jewel.ui.painter.PainterProviderScope
-import org.jetbrains.jewel.ui.painter.PainterSuffixHint
-import org.jetbrains.jewel.window.styling.TitleBarStyle
-import java.awt.Frame
-import java.awt.event.MouseEvent
-import java.awt.event.WindowEvent
-
-@Composable
-internal fun DecoratedWindowScope.TitleBarOnLinux(
- modifier: Modifier = Modifier,
- gradientStartColor: Color = Color.Unspecified,
- style: TitleBarStyle = JewelTheme.defaultTitleBarStyle,
- content: @Composable TitleBarScope.(DecoratedWindowState) -> Unit,
-) {
- var lastPress = 0L
- val viewConfig = LocalViewConfiguration.current
- TitleBarImpl(
- modifier.onPointerEvent(PointerEventType.Press, PointerEventPass.Main) {
- if (this.currentEvent.button == PointerButton.Primary &&
- this.currentEvent.changes.any { changed -> !changed.isConsumed }
- ) {
- JBR.getWindowMove()?.startMovingTogetherWithMouse(window, MouseEvent.BUTTON1)
- if (System.currentTimeMillis() - lastPress in
- viewConfig.doubleTapMinTimeMillis..viewConfig.doubleTapTimeoutMillis
- ) {
- if (state.isMaximized) {
- window.extendedState = Frame.NORMAL
- } else {
- window.extendedState = Frame.MAXIMIZED_BOTH
- }
- }
- lastPress = System.currentTimeMillis()
- }
- },
- gradientStartColor,
- style,
- { _, _ -> PaddingValues(0.dp) },
- ) { state ->
- CloseButton(
- { window.dispatchEvent(WindowEvent(window, WindowEvent.WINDOW_CLOSING)) },
- state,
- style,
- )
-
- if (state.isMaximized) {
- ControlButton(
- { window.extendedState = Frame.NORMAL },
- state,
- style.icons.restoreButton,
- "Restore",
- )
- } else {
- ControlButton(
- { window.extendedState = Frame.MAXIMIZED_BOTH },
- state,
- style.icons.maximizeButton,
- "Maximize",
- )
- }
- ControlButton(
- { window.extendedState = Frame.ICONIFIED },
- state,
- style.icons.minimizeButton,
- "Minimize",
- )
- content(state)
- }
-}
-
-@Composable
-private fun TitleBarScope.CloseButton(
- onClick: () -> Unit,
- state: DecoratedWindowState,
- style: TitleBarStyle = JewelTheme.defaultTitleBarStyle,
-) {
- ControlButton(
- onClick,
- state,
- style.icons.closeButton,
- "Close",
- style,
- style.paneCloseButtonStyle,
- )
-}
-
-@Composable
-private fun TitleBarScope.ControlButton(
- onClick: () -> Unit,
- state: DecoratedWindowState,
- painterProvider: PainterProvider,
- description: String,
- style: TitleBarStyle = JewelTheme.defaultTitleBarStyle,
- iconButtonStyle: IconButtonStyle = style.paneButtonStyle,
-) {
- IconButton(
- onClick,
- Modifier.align(Alignment.End).focusable(false).size(style.metrics.titlePaneButtonSize),
- style = iconButtonStyle,
- ) {
- Icon(
- painterProvider.getPainter(if (state.isActive) PainterHint else Inactive).value,
- description,
- )
- }
-}
-
-private object Inactive : PainterSuffixHint() {
-
- override fun PainterProviderScope.suffix(): String = "Inactive"
-
- override fun toString(): String = "Inactive"
-}
diff --git a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/TitleBar.MacOS.kt b/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/TitleBar.MacOS.kt
deleted file mode 100644
index 63475e2d14..0000000000
--- a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/TitleBar.MacOS.kt
+++ /dev/null
@@ -1,103 +0,0 @@
-package org.jetbrains.jewel.window
-
-import androidx.compose.foundation.layout.PaddingValues
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.remember
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.toArgb
-import androidx.compose.ui.node.ModifierNodeElement
-import androidx.compose.ui.platform.InspectorInfo
-import androidx.compose.ui.platform.debugInspectorInfo
-import androidx.compose.ui.unit.dp
-import com.jetbrains.JBR
-import org.jetbrains.jewel.foundation.theme.JewelTheme
-import org.jetbrains.jewel.window.styling.TitleBarStyle
-import org.jetbrains.jewel.window.utils.macos.MacUtil
-
-public fun Modifier.newFullscreenControls(newControls: Boolean = true): Modifier =
- this then NewFullscreenControlsElement(
- newControls,
- debugInspectorInfo {
- name = "newFullscreenControls"
- value = newControls
- },
- )
-
-private class NewFullscreenControlsElement(
- val newControls: Boolean,
- val inspectorInfo: InspectorInfo.() -> Unit,
-) : ModifierNodeElement() {
-
- override fun create(): NewFullscreenControlsNode =
- NewFullscreenControlsNode(newControls)
-
- override fun equals(other: Any?): Boolean {
- if (this === other) return true
- val otherModifier = other as? NewFullscreenControlsElement
- ?: return false
- return newControls == otherModifier.newControls
- }
-
- override fun hashCode(): Int = newControls.hashCode()
-
- override fun InspectorInfo.inspectableProperties() {
- inspectorInfo()
- }
-
- override fun update(node: NewFullscreenControlsNode) {
- node.newControls = newControls
- }
-}
-
-private class NewFullscreenControlsNode(var newControls: Boolean) : Modifier.Node()
-
-@Composable
-internal fun DecoratedWindowScope.TitleBarOnMacOs(
- modifier: Modifier = Modifier,
- gradientStartColor: Color = Color.Unspecified,
- style: TitleBarStyle = JewelTheme.defaultTitleBarStyle,
- content: @Composable TitleBarScope.(DecoratedWindowState) -> Unit,
-) {
- val newFullscreenControls = modifier.foldOut(false) { e, r ->
- if (e is NewFullscreenControlsElement) {
- e.newControls
- } else {
- r
- }
- }
-
- if (newFullscreenControls) {
- System.setProperty("apple.awt.newFullScreeControls", true.toString())
- System.setProperty(
- "apple.awt.newFullScreeControls.background",
- "${style.colors.fullscreenControlButtonsBackground.toArgb()}",
- )
- MacUtil.updateColors(window)
- } else {
- System.clearProperty("apple.awt.newFullScreeControls")
- System.clearProperty("apple.awt.newFullScreeControls.background")
- }
-
- val titleBar = remember { JBR.getWindowDecorations().createCustomTitleBar() }
-
- TitleBarImpl(
- modifier = modifier.customTitleBarMouseEventHandler(titleBar),
- gradientStartColor = gradientStartColor,
- style = style,
- applyTitleBar = { height, state ->
- if (state.isFullscreen) {
- MacUtil.updateFullScreenButtons(window)
- }
- titleBar.height = height.value
- JBR.getWindowDecorations().setCustomTitleBar(window, titleBar)
-
- if (state.isFullscreen && newFullscreenControls) {
- PaddingValues(start = 80.dp)
- } else {
- PaddingValues(start = titleBar.leftInset.dp, end = titleBar.rightInset.dp)
- }
- },
- content = content,
- )
-}
diff --git a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/TitleBar.Windows.kt b/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/TitleBar.Windows.kt
deleted file mode 100644
index e342f36bae..0000000000
--- a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/TitleBar.Windows.kt
+++ /dev/null
@@ -1,65 +0,0 @@
-package org.jetbrains.jewel.window
-
-import androidx.compose.foundation.layout.PaddingValues
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.remember
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.input.pointer.PointerEventPass
-import androidx.compose.ui.input.pointer.PointerEventType
-import androidx.compose.ui.input.pointer.pointerInput
-import androidx.compose.ui.unit.dp
-import com.jetbrains.JBR
-import com.jetbrains.WindowDecorations.CustomTitleBar
-import kotlinx.coroutines.currentCoroutineContext
-import kotlinx.coroutines.isActive
-import org.jetbrains.jewel.foundation.theme.JewelTheme
-import org.jetbrains.jewel.ui.util.isDark
-import org.jetbrains.jewel.window.styling.TitleBarStyle
-
-@Composable
-internal fun DecoratedWindowScope.TitleBarOnWindows(
- modifier: Modifier = Modifier,
- gradientStartColor: Color = Color.Unspecified,
- style: TitleBarStyle = JewelTheme.defaultTitleBarStyle,
- content: @Composable TitleBarScope.(DecoratedWindowState) -> Unit,
-) {
- val titleBar = remember { JBR.getWindowDecorations().createCustomTitleBar() }
-
- TitleBarImpl(
- modifier = modifier.customTitleBarMouseEventHandler(titleBar),
- gradientStartColor = gradientStartColor,
- style = style,
- applyTitleBar = { height, _ ->
- titleBar.height = height.value
- titleBar.putProperty("controls.dark", style.colors.background.isDark())
- JBR.getWindowDecorations().setCustomTitleBar(window, titleBar)
- PaddingValues(start = titleBar.leftInset.dp, end = titleBar.rightInset.dp)
- },
- content = content,
- )
-}
-
-internal fun Modifier.customTitleBarMouseEventHandler(titleBar: CustomTitleBar): Modifier =
- pointerInput(Unit) {
- val currentContext = currentCoroutineContext()
- awaitPointerEventScope {
- var inUserControl = false
- while (currentContext.isActive) {
- val event = awaitPointerEvent(PointerEventPass.Main)
- event.changes.forEach {
- if (!it.isConsumed && !inUserControl) {
- titleBar.forceHitTest(false)
- } else {
- if (event.type == PointerEventType.Press) {
- inUserControl = true
- }
- if (event.type == PointerEventType.Release) {
- inUserControl = false
- }
- titleBar.forceHitTest(true)
- }
- }
- }
- }
- }
diff --git a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/TitleBar.kt b/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/TitleBar.kt
deleted file mode 100644
index acd1218ff1..0000000000
--- a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/TitleBar.kt
+++ /dev/null
@@ -1,294 +0,0 @@
-package org.jetbrains.jewel.window
-
-import androidx.compose.foundation.background
-import androidx.compose.foundation.layout.PaddingValues
-import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.CompositionLocalProvider
-import androidx.compose.runtime.Stable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.remember
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.focus.focusProperties
-import androidx.compose.ui.graphics.Brush
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.SolidColor
-import androidx.compose.ui.graphics.isUnspecified
-import androidx.compose.ui.graphics.painter.Painter
-import androidx.compose.ui.layout.Layout
-import androidx.compose.ui.layout.Measurable
-import androidx.compose.ui.layout.MeasurePolicy
-import androidx.compose.ui.layout.MeasureResult
-import androidx.compose.ui.layout.MeasureScope
-import androidx.compose.ui.layout.Placeable
-import androidx.compose.ui.layout.layoutId
-import androidx.compose.ui.layout.onSizeChanged
-import androidx.compose.ui.node.ModifierNodeElement
-import androidx.compose.ui.node.ParentDataModifierNode
-import androidx.compose.ui.platform.InspectableValue
-import androidx.compose.ui.platform.InspectorInfo
-import androidx.compose.ui.platform.LocalDensity
-import androidx.compose.ui.platform.NoInspectorInfo
-import androidx.compose.ui.platform.debugInspectorInfo
-import androidx.compose.ui.unit.Constraints
-import androidx.compose.ui.unit.Density
-import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.offset
-import org.jetbrains.jewel.foundation.theme.JewelTheme
-import org.jetbrains.jewel.foundation.theme.LocalContentColor
-import org.jetbrains.jewel.foundation.theme.OverrideDarkMode
-import org.jetbrains.jewel.ui.component.styling.LocalDefaultDropdownStyle
-import org.jetbrains.jewel.ui.component.styling.LocalIconButtonStyle
-import org.jetbrains.jewel.ui.util.isDark
-import org.jetbrains.jewel.window.styling.TitleBarStyle
-import org.jetbrains.jewel.window.utils.DesktopPlatform
-import org.jetbrains.jewel.window.utils.macos.MacUtil
-import java.awt.Window
-import kotlin.math.max
-
-internal const val TITLE_BAR_COMPONENT_LAYOUT_ID_PREFIX = "__TITLE_BAR_"
-
-internal const val TITLE_BAR_LAYOUT_ID = "__TITLE_BAR_CONTENT__"
-
-internal const val TITLE_BAR_BORDER_LAYOUT_ID = "__TITLE_BAR_BORDER__"
-
-@Composable
-public fun DecoratedWindowScope.TitleBar(
- modifier: Modifier = Modifier,
- gradientStartColor: Color = Color.Unspecified,
- style: TitleBarStyle = JewelTheme.defaultTitleBarStyle,
- content: @Composable TitleBarScope.(DecoratedWindowState) -> Unit,
-) {
- when (DesktopPlatform.Current) {
- DesktopPlatform.Linux -> TitleBarOnLinux(modifier, gradientStartColor, style, content)
- DesktopPlatform.Windows -> TitleBarOnWindows(modifier, gradientStartColor, style, content)
- DesktopPlatform.MacOS -> TitleBarOnMacOs(modifier, gradientStartColor, style, content)
- DesktopPlatform.Unknown -> error("TitleBar is not supported on this platform(${System.getProperty("os.name")})")
- }
-}
-
-@Composable
-internal fun DecoratedWindowScope.TitleBarImpl(
- modifier: Modifier = Modifier,
- gradientStartColor: Color = Color.Unspecified,
- style: TitleBarStyle = JewelTheme.defaultTitleBarStyle,
- applyTitleBar: (Dp, DecoratedWindowState) -> PaddingValues,
- content: @Composable TitleBarScope.(DecoratedWindowState) -> Unit,
-) {
- val titleBarInfo = LocalTitleBarInfo.current
-
- val background by style.colors.backgroundFor(state)
-
- val density = LocalDensity.current
-
- val backgroundBrush = remember(background, gradientStartColor) {
- if (gradientStartColor.isUnspecified) {
- SolidColor(background)
- } else {
- with(density) {
- Brush.horizontalGradient(
- 0.0f to background,
- 0.5f to gradientStartColor,
- 1.0f to background,
- startX = style.metrics.gradientStartX.toPx(),
- endX = style.metrics.gradientEndX.toPx(),
- )
- }
- }
- }
-
- Layout(
- content = {
- CompositionLocalProvider(
- LocalContentColor provides style.colors.content,
- LocalIconButtonStyle provides style.iconButtonStyle,
- LocalDefaultDropdownStyle provides style.dropdownStyle,
- ) {
- OverrideDarkMode(background.isDark()) {
- val scope = TitleBarScopeImpl(titleBarInfo.title, titleBarInfo.icon)
- scope.content(state)
- }
- }
- },
- modifier = modifier.background(backgroundBrush)
- .focusProperties { canFocus = false }
- .layoutId(TITLE_BAR_LAYOUT_ID)
- .height(style.metrics.height)
- .onSizeChanged { with(density) { applyTitleBar(it.height.toDp(), state) } }
- .fillMaxWidth(),
- measurePolicy = rememberTitleBarMeasurePolicy(
- window,
- state,
- applyTitleBar,
- ),
- )
-
- Spacer(
- Modifier.layoutId(TITLE_BAR_BORDER_LAYOUT_ID)
- .height(1.dp)
- .fillMaxWidth()
- .background(style.colors.border),
- )
-}
-
-internal class TitleBarMeasurePolicy(
- private val window: Window,
- private val state: DecoratedWindowState,
- private val applyTitleBar: (Dp, DecoratedWindowState) -> PaddingValues,
-) : MeasurePolicy {
-
- override fun MeasureScope.measure(measurables: List, constraints: Constraints): MeasureResult {
- if (measurables.isEmpty()) {
- return layout(width = constraints.minWidth, height = constraints.minHeight) {}
- }
-
- var occupiedSpaceHorizontally = 0
-
- var maxSpaceVertically = constraints.minHeight
- val contentConstraints = constraints.copy(minWidth = 0, minHeight = 0)
- val measuredPlaceable = mutableListOf>()
-
- for (it in measurables) {
- val placeable = it.measure(contentConstraints.offset(horizontal = -occupiedSpaceHorizontally))
- if (constraints.maxWidth < occupiedSpaceHorizontally + placeable.width) {
- break
- }
- occupiedSpaceHorizontally += placeable.width
- maxSpaceVertically = max(maxSpaceVertically, placeable.height)
- measuredPlaceable += it to placeable
- }
-
- val boxHeight = maxSpaceVertically
-
- val contentPadding = applyTitleBar(boxHeight.toDp(), state)
-
- val leftInset = contentPadding.calculateLeftPadding(layoutDirection).roundToPx()
- val rightInset = contentPadding.calculateRightPadding(layoutDirection).roundToPx()
-
- occupiedSpaceHorizontally += leftInset
- occupiedSpaceHorizontally += rightInset
-
- val boxWidth = maxOf(constraints.minWidth, occupiedSpaceHorizontally)
-
- return layout(boxWidth, boxHeight) {
- if (state.isFullscreen) {
- MacUtil.updateFullScreenButtons(window)
- }
- val placeableGroups =
- measuredPlaceable.groupBy { (measurable, _) ->
- (measurable.parentData as? TitleBarChildDataNode)?.horizontalAlignment
- ?: Alignment.CenterHorizontally
- }
-
- var headUsedSpace = leftInset
- var trailerUsedSpace = rightInset
-
- placeableGroups[Alignment.Start]?.forEach { (_, placeable) ->
- val x = headUsedSpace
- val y = Alignment.CenterVertically.align(placeable.height, boxHeight)
- placeable.placeRelative(x, y)
- headUsedSpace += placeable.width
- }
- placeableGroups[Alignment.End]?.forEach { (_, placeable) ->
- val x = boxWidth - placeable.width - trailerUsedSpace
- val y = Alignment.CenterVertically.align(placeable.height, boxHeight)
- placeable.placeRelative(x, y)
- trailerUsedSpace += placeable.width
- }
-
- val centerPlaceable = placeableGroups[Alignment.CenterHorizontally].orEmpty()
-
- val requiredCenterSpace = centerPlaceable.sumOf { it.second.width }
- val minX = headUsedSpace
- val maxX = boxWidth - trailerUsedSpace - requiredCenterSpace
- var centerX = (boxWidth - requiredCenterSpace) / 2
-
- if (minX <= maxX) {
- if (centerX > maxX) {
- centerX = maxX
- }
- if (centerX < minX) {
- centerX = minX
- }
-
- centerPlaceable.forEach { (_, placeable) ->
- val x = centerX
- val y = Alignment.CenterVertically.align(placeable.height, boxHeight)
- placeable.placeRelative(x, y)
- centerX += placeable.width
- }
- }
- }
- }
-}
-
-@Composable
-internal fun rememberTitleBarMeasurePolicy(
- window: Window,
- state: DecoratedWindowState,
- applyTitleBar: (Dp, DecoratedWindowState) -> PaddingValues,
-): MeasurePolicy =
- remember(window, state, applyTitleBar) {
- TitleBarMeasurePolicy(window, state, applyTitleBar)
- }
-
-public interface TitleBarScope {
-
- public val title: String
-
- public val icon: Painter?
-
- @Stable
- public fun Modifier.align(alignment: Alignment.Horizontal): Modifier
-}
-
-private class TitleBarScopeImpl(
- override val title: String,
- override val icon: Painter?,
-) : TitleBarScope {
-
- override fun Modifier.align(alignment: Alignment.Horizontal): Modifier =
- this then TitleBarChildDataElement(
- alignment,
- debugInspectorInfo {
- name = "align"
- value = alignment
- },
- )
-}
-
-private class TitleBarChildDataElement(
- val horizontalAlignment: Alignment.Horizontal,
- val inspectorInfo: InspectorInfo.() -> Unit = NoInspectorInfo,
-) : ModifierNodeElement(), InspectableValue {
-
- override fun create(): TitleBarChildDataNode = TitleBarChildDataNode(horizontalAlignment)
-
- override fun equals(other: Any?): Boolean {
- if (this === other) return true
- val otherModifier = other as? TitleBarChildDataElement ?: return false
- return horizontalAlignment == otherModifier.horizontalAlignment
- }
-
- override fun hashCode(): Int = horizontalAlignment.hashCode()
-
- override fun update(node: TitleBarChildDataNode) {
- node.horizontalAlignment = horizontalAlignment
- }
-
- override fun InspectorInfo.inspectableProperties() {
- inspectorInfo()
- }
-}
-
-private class TitleBarChildDataNode(
- var horizontalAlignment: Alignment.Horizontal,
-) : ParentDataModifierNode, Modifier.Node() {
-
- override fun Density.modifyParentData(parentData: Any?) =
- this@TitleBarChildDataNode
-}
diff --git a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/styling/DecoratedWindowStyling.kt b/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/styling/DecoratedWindowStyling.kt
deleted file mode 100644
index cc15f5ac62..0000000000
--- a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/styling/DecoratedWindowStyling.kt
+++ /dev/null
@@ -1,53 +0,0 @@
-package org.jetbrains.jewel.window.styling
-
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.Immutable
-import androidx.compose.runtime.ProvidableCompositionLocal
-import androidx.compose.runtime.State
-import androidx.compose.runtime.rememberUpdatedState
-import androidx.compose.runtime.staticCompositionLocalOf
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.unit.Dp
-import org.jetbrains.jewel.foundation.GenerateDataFunctions
-import org.jetbrains.jewel.window.DecoratedWindowState
-
-@Immutable
-@GenerateDataFunctions
-public class DecoratedWindowStyle(
- public val colors: DecoratedWindowColors,
- public val metrics: DecoratedWindowMetrics,
-) {
-
- public companion object
-}
-
-@Immutable
-@GenerateDataFunctions
-public class DecoratedWindowColors(
- public val border: Color,
- public val borderInactive: Color,
-) {
-
- @Composable
- public fun borderFor(state: DecoratedWindowState): State =
- rememberUpdatedState(
- when {
- !state.isActive -> borderInactive
- else -> border
- },
- )
-
- public companion object
-}
-
-@Immutable
-@GenerateDataFunctions
-public class DecoratedWindowMetrics(public val borderWidth: Dp) {
-
- public companion object
-}
-
-public val LocalDecoratedWindowStyle: ProvidableCompositionLocal =
- staticCompositionLocalOf {
- error("No DecoratedWindowStyle provided. Have you forgotten the theme?")
- }
diff --git a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/styling/TitleBarStyling.kt b/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/styling/TitleBarStyling.kt
deleted file mode 100644
index ca0d907f2c..0000000000
--- a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/styling/TitleBarStyling.kt
+++ /dev/null
@@ -1,102 +0,0 @@
-package org.jetbrains.jewel.window.styling
-
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.Immutable
-import androidx.compose.runtime.ProvidableCompositionLocal
-import androidx.compose.runtime.Stable
-import androidx.compose.runtime.State
-import androidx.compose.runtime.rememberUpdatedState
-import androidx.compose.runtime.staticCompositionLocalOf
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.DpSize
-import org.jetbrains.jewel.foundation.GenerateDataFunctions
-import org.jetbrains.jewel.ui.component.styling.DropdownStyle
-import org.jetbrains.jewel.ui.component.styling.IconButtonStyle
-import org.jetbrains.jewel.ui.painter.PainterProvider
-import org.jetbrains.jewel.window.DecoratedWindowState
-
-@Stable
-@GenerateDataFunctions
-public class TitleBarStyle(
- public val colors: TitleBarColors,
- public val metrics: TitleBarMetrics,
- public val icons: TitleBarIcons,
- public val dropdownStyle: DropdownStyle,
- public val iconButtonStyle: IconButtonStyle,
- public val paneButtonStyle: IconButtonStyle,
- public val paneCloseButtonStyle: IconButtonStyle,
-) {
-
- public companion object
-}
-
-@Immutable
-@GenerateDataFunctions
-public class TitleBarColors(
- public val background: Color,
- public val inactiveBackground: Color,
- public val content: Color,
- public val border: Color,
-
- // The background color for newControlButtons(three circles in left top corner) in MacOS
- // fullscreen mode
- public val fullscreenControlButtonsBackground: Color,
-
- // The hover and press background color for window control buttons(minimize, maximize) in Linux
- public val titlePaneButtonHoveredBackground: Color,
- public val titlePaneButtonPressedBackground: Color,
-
- // The hover and press background color for window close button in Linux
- public val titlePaneCloseButtonHoveredBackground: Color,
- public val titlePaneCloseButtonPressedBackground: Color,
-
- // The hover and press background color for IconButtons in title bar content
- public val iconButtonHoveredBackground: Color,
- public val iconButtonPressedBackground: Color,
-
- // The hover and press background color for Dropdown in title bar content
- public val dropdownPressedBackground: Color,
- public val dropdownHoveredBackground: Color,
-) {
-
- @Composable
- public fun backgroundFor(state: DecoratedWindowState): State =
- rememberUpdatedState(
- when {
- !state.isActive -> inactiveBackground
- else -> background
- },
- )
-
- public companion object
-}
-
-@Immutable
-@GenerateDataFunctions
-public class TitleBarMetrics(
- public val height: Dp,
- public val gradientStartX: Dp,
- public val gradientEndX: Dp,
- public val titlePaneButtonSize: DpSize,
-) {
-
- public companion object
-}
-
-@Immutable
-@GenerateDataFunctions
-public class TitleBarIcons(
- public val minimizeButton: PainterProvider,
- public val maximizeButton: PainterProvider,
- public val restoreButton: PainterProvider,
- public val closeButton: PainterProvider,
-) {
-
- public companion object
-}
-
-public val LocalTitleBarStyle: ProvidableCompositionLocal =
- staticCompositionLocalOf {
- error("No TitleBarStyle provided. Have you forgotten the theme?")
- }
diff --git a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/utils/DesktopPlatform.kt b/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/utils/DesktopPlatform.kt
deleted file mode 100644
index 51872dc474..0000000000
--- a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/utils/DesktopPlatform.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-package org.jetbrains.jewel.window.utils
-
-public enum class DesktopPlatform {
- Linux,
- Windows,
- MacOS,
- Unknown,
- ;
-
- public companion object {
-
- public val Current: DesktopPlatform by lazy {
- val name = System.getProperty("os.name")
- when {
- name?.startsWith("Linux") == true -> Linux
- name?.startsWith("Win") == true -> Windows
- name == "Mac OS X" -> MacOS
- else -> Unknown
- }
- }
- }
-}
diff --git a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/utils/JnaLoader.kt b/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/utils/JnaLoader.kt
deleted file mode 100644
index 7e136b0f65..0000000000
--- a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/utils/JnaLoader.kt
+++ /dev/null
@@ -1,43 +0,0 @@
-package org.jetbrains.jewel.window.utils
-
-import com.sun.jna.Native
-import java.util.logging.Level
-import java.util.logging.Logger
-import kotlin.system.measureTimeMillis
-
-internal object JnaLoader {
-
- private var loaded: Boolean? = null
- private val logger = Logger.getLogger(JnaLoader::class.java.simpleName)
-
- @Synchronized
- fun load() {
- if (loaded == null) {
- loaded = false
- try {
- val time = measureTimeMillis { Native.POINTER_SIZE }
- logger.info("JNA library (${Native.POINTER_SIZE shl 3}-bit) loaded in $time ms")
- loaded = true
- } catch (@Suppress("TooGenericExceptionCaught") t: Throwable) {
- logger.log(
- Level.WARNING,
- "Unable to load JNA library(os=${
- System.getProperty("os.name")
- } ${System.getProperty("os.version")}, jna.boot.library.path=${
- System.getProperty("jna.boot.library.path")
- })",
- t,
- )
- }
- }
- }
-
- @get:Synchronized
- val isLoaded: Boolean
- get() {
- if (loaded == null) {
- load()
- }
- return loaded ?: false
- }
-}
diff --git a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/utils/UnsafeAccessing.kt b/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/utils/UnsafeAccessing.kt
deleted file mode 100644
index ed0e7920a2..0000000000
--- a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/utils/UnsafeAccessing.kt
+++ /dev/null
@@ -1,73 +0,0 @@
-package org.jetbrains.jewel.window.utils
-
-import sun.misc.Unsafe
-import java.lang.reflect.AccessibleObject
-import java.util.logging.Level
-import java.util.logging.Logger
-
-internal object UnsafeAccessing {
-
- private val logger = Logger.getLogger(UnsafeAccessing::class.java.simpleName)
-
- private val unsafe: Any? by lazy {
- try {
- val theUnsafe = Unsafe::class.java.getDeclaredField("theUnsafe")
- theUnsafe.isAccessible = true
- theUnsafe.get(null) as Unsafe
- } catch (@Suppress("TooGenericExceptionCaught") error: Throwable) {
- logger.log(Level.WARNING, "Unsafe accessing initializing failed.", error)
- null
- }
- }
-
- val desktopModule by lazy { ModuleLayer.boot().findModule("java.desktop").get() }
-
- val ownerModule: Module by lazy { this.javaClass.module }
-
- private val isAccessibleFieldOffset: Long? by lazy {
- try {
- (unsafe as? Unsafe)?.objectFieldOffset(Parent::class.java.getDeclaredField("first"))
- } catch (_: Throwable) {
- null
- }
- }
-
- private val implAddOpens by lazy {
- try {
- Module::class.java
- .getDeclaredMethod("implAddOpens", String::class.java, Module::class.java)
- .accessible()
- } catch (_: Throwable) {
- null
- }
- }
-
- fun assignAccessibility(obj: AccessibleObject) {
- try {
- val theUnsafe = unsafe as? Unsafe ?: return
- val offset = isAccessibleFieldOffset ?: return
- theUnsafe.putBooleanVolatile(obj, offset, true)
- } catch (_: Throwable) {
- // ignore
- }
- }
-
- fun assignAccessibility(module: Module, packages: List) {
- try {
- packages.forEach { implAddOpens?.invoke(module, it, ownerModule) }
- } catch (_: Throwable) {
- // ignore
- }
- }
-
- private class Parent {
-
- var first = false
-
- @Volatile
- var second: Any? = null
- }
-}
-
-internal fun T.accessible(): T =
- apply { UnsafeAccessing.assignAccessibility(this) }
diff --git a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/utils/macos/Foundation.kt b/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/utils/macos/Foundation.kt
deleted file mode 100644
index 193b29c7eb..0000000000
--- a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/utils/macos/Foundation.kt
+++ /dev/null
@@ -1,98 +0,0 @@
-package org.jetbrains.jewel.window.utils.macos
-
-import com.sun.jna.Function
-import com.sun.jna.Library
-import com.sun.jna.Native
-import com.sun.jna.Pointer
-import org.jetbrains.jewel.window.utils.JnaLoader
-import java.lang.reflect.Proxy
-import java.util.Arrays
-import java.util.Collections
-import java.util.logging.Level
-import java.util.logging.Logger
-
-internal object Foundation {
-
- private val logger = Logger.getLogger(Foundation::class.java.simpleName)
-
- init {
- if (!JnaLoader.isLoaded) {
- logger.log(Level.WARNING, "JNA is not loaded")
- }
- }
-
- private val myFoundationLibrary: FoundationLibrary? by lazy {
- try {
- Native.load(
- "Foundation",
- FoundationLibrary::class.java,
- Collections.singletonMap("jna.encoding", "UTF8"),
- )
- } catch (_: Throwable) {
- null
- }
- }
-
- private val myObjcMsgSend: Function? by lazy {
- try {
- (Proxy.getInvocationHandler(myFoundationLibrary) as Library.Handler).nativeLibrary.getFunction("objc_msgSend")
- } catch (_: Throwable) {
- null
- }
- }
-
- /**
- * Get the ID of the NSClass with className
- */
- fun getObjcClass(className: String?): ID? = myFoundationLibrary?.objc_getClass(className)
-
- fun getProtocol(name: String?): ID? = myFoundationLibrary?.objc_getProtocol(name)
-
- fun createSelector(s: String?): Pointer? = myFoundationLibrary?.sel_registerName(s)
-
- private fun prepInvoke(id: ID?, selector: Pointer?, args: Array): Array {
- val invokArgs = arrayOfNulls(args.size + 2)
- invokArgs[0] = id
- invokArgs[1] = selector
- System.arraycopy(args, 0, invokArgs, 2, args.size)
- return invokArgs
- }
-
- // objc_msgSend is called with the calling convention of the target method
- // on x86_64 this does not make a difference, but arm64 uses a different calling convention for varargs
- // it is therefore important to not call objc_msgSend as a vararg function
- operator fun invoke(id: ID?, selector: Pointer?, vararg args: Any?): ID =
- ID(myObjcMsgSend?.invokeLong(prepInvoke(id, selector, args)) ?: 0)
-
- /**
- * Invokes the given vararg selector.
- * Expects `NSArray arrayWithObjects:(id), ...` like signature, i.e. exactly one fixed argument, followed by varargs.
- */
- fun invokeVarArg(id: ID?, selector: Pointer?, vararg args: Any?): ID {
- // c functions and objc methods have at least 1 fixed argument, we therefore need to separate out the first argument
- return myFoundationLibrary?.objc_msgSend(
- id,
- selector,
- args[0],
- *Arrays.copyOfRange(args, 1, args.size),
- ) ?: ID.NIL
- }
-
- operator fun invoke(cls: String?, selector: String?, vararg args: Any?): ID =
- invoke(getObjcClass(cls), createSelector(selector), *args)
-
- fun invokeVarArg(cls: String?, selector: String?, vararg args: Any?): ID =
- invokeVarArg(getObjcClass(cls), createSelector(selector), *args)
-
- fun safeInvoke(stringCls: String?, stringSelector: String?, vararg args: Any?): ID {
- val cls = getObjcClass(stringCls)
- val selector = createSelector(stringSelector)
- if (!invoke(cls, "respondsToSelector:", selector).booleanValue()) {
- error("Missing selector $stringSelector for $stringCls")
- }
- return invoke(cls, selector, *args)
- }
-
- operator fun invoke(id: ID?, selector: String?, vararg args: Any?): ID =
- invoke(id, createSelector(selector), *args)
-}
diff --git a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/utils/macos/FoundationLibrary.kt b/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/utils/macos/FoundationLibrary.kt
deleted file mode 100644
index a4c93807e2..0000000000
--- a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/utils/macos/FoundationLibrary.kt
+++ /dev/null
@@ -1,89 +0,0 @@
-package org.jetbrains.jewel.window.utils.macos
-
-import com.sun.jna.Callback
-import com.sun.jna.Library
-import com.sun.jna.Pointer
-
-internal interface FoundationLibrary : Library {
- fun NSLog(pString: Pointer?, thing: Any?)
- fun NSFullUserName(): ID?
- fun objc_allocateClassPair(supercls: ID?, name: String?, extraBytes: Int): ID?
- fun objc_registerClassPair(cls: ID?)
- fun CFStringCreateWithBytes(
- allocator: Pointer?,
- bytes: ByteArray?,
- byteCount: Int,
- encoding: Int,
- isExternalRepresentation: Byte,
- ): ID?
-
- fun CFStringGetCString(theString: ID?, buffer: ByteArray?, bufferSize: Int, encoding: Int): Byte
- fun CFStringGetLength(theString: ID?): Int
- fun CFStringConvertNSStringEncodingToEncoding(nsEncoding: Long): Long
- fun CFStringConvertEncodingToIANACharSetName(cfEncoding: Long): ID?
- fun CFStringConvertIANACharSetNameToEncoding(encodingName: ID?): Long
- fun CFStringConvertEncodingToNSStringEncoding(cfEncoding: Long): Long
- fun CFRetain(cfTypeRef: ID?)
- fun CFRelease(cfTypeRef: ID?)
- fun CFGetRetainCount(cfTypeRef: Pointer?): Int
- fun objc_getClass(className: String?): ID?
- fun objc_getProtocol(name: String?): ID?
- fun class_createInstance(pClass: ID?, extraBytes: Int): ID?
- fun sel_registerName(selectorName: String?): Pointer?
- fun class_replaceMethod(cls: ID?, selName: Pointer?, impl: Callback?, types: String?): ID?
- fun objc_getMetaClass(name: String?): ID?
-
- /**
- * Note: Vararg version. Should only be used only for selectors with a single fixed argument followed by varargs.
- */
- fun objc_msgSend(receiver: ID?, selector: Pointer?, firstArg: Any?, vararg args: Any?): ID?
- fun class_respondsToSelector(cls: ID?, selName: Pointer?): Boolean
- fun class_addMethod(cls: ID?, selName: Pointer?, imp: Callback?, types: String?): Boolean
- fun class_addMethod(cls: ID?, selName: Pointer?, imp: ID?, types: String?): Boolean
- fun class_addProtocol(aClass: ID?, protocol: ID?): Boolean
- fun class_isMetaClass(cls: ID?): Boolean
- fun NSStringFromSelector(selector: Pointer?): ID?
- fun NSStringFromClass(aClass: ID?): ID?
- fun objc_getClass(clazz: Pointer?): Pointer?
-
- companion object {
- const val kCFStringEncodingMacRoman = 0
- const val kCFStringEncodingWindowsLatin1 = 0x0500
- const val kCFStringEncodingISOLatin1 = 0x0201
- const val kCFStringEncodingNextStepLatin = 0x0B01
- const val kCFStringEncodingASCII = 0x0600
- const val kCFStringEncodingUnicode = 0x0100
- const val kCFStringEncodingUTF8 = 0x08000100
- const val kCFStringEncodingNonLossyASCII = 0x0BFF
- const val kCFStringEncodingUTF16 = 0x0100
- const val kCFStringEncodingUTF16BE = 0x10000100
- const val kCFStringEncodingUTF16LE = 0x14000100
- const val kCFStringEncodingUTF32 = 0x0c000100
- const val kCFStringEncodingUTF32BE = 0x18000100
- const val kCFStringEncodingUTF32LE = 0x1c000100
-
- // https://developer.apple.com/library/mac/documentation/Carbon/Reference/CGWindow_Reference/Constants/Constants.html#//apple_ref/doc/constant_group/Window_List_Option_Constants
- const val kCGWindowListOptionAll = 0
- const val kCGWindowListOptionOnScreenOnly = 1
- const val kCGWindowListOptionOnScreenAboveWindow = 2
- const val kCGWindowListOptionOnScreenBelowWindow = 4
- const val kCGWindowListOptionIncludingWindow = 8
- const val kCGWindowListExcludeDesktopElements = 16
-
- // https://developer.apple.com/library/mac/documentation/Carbon/Reference/CGWindow_Reference/Constants/Constants.html#//apple_ref/doc/constant_group/Window_Image_Types
- const val kCGWindowImageDefault = 0
- const val kCGWindowImageBoundsIgnoreFraming = 1
- const val kCGWindowImageShouldBeOpaque = 2
- const val kCGWindowImageOnlyShadows = 4
- const val kCGWindowImageBestResolution = 8
- const val kCGWindowImageNominalResolution = 16
-
- // see enum NSBitmapImageFileType
- const val NSBitmapImageFileTypeTIFF = 0
- const val NSBitmapImageFileTypeBMP = 1
- const val NSBitmapImageFileTypeGIF = 2
- const val NSBitmapImageFileTypeJPEG = 3
- const val NSBitmapImageFileTypePNG = 4
- const val NSBitmapImageFileTypeJPEG2000 = 5
- }
-}
diff --git a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/utils/macos/ID.kt b/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/utils/macos/ID.kt
deleted file mode 100644
index df74e25e7f..0000000000
--- a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/utils/macos/ID.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-package org.jetbrains.jewel.window.utils.macos
-
-import com.sun.jna.NativeLong
-
-/**
- * Could be an address in memory (if pointer to a class or method) or a
- * value (like 0 or 1)
- */
-internal class ID : NativeLong {
-
- constructor()
- constructor(peer: Long) : super(peer)
-
- fun booleanValue(): Boolean = toInt() != 0
-
- override fun toByte(): Byte = toInt().toByte()
-
- override fun toChar(): Char = toInt().toChar()
-
- override fun toShort(): Short = toInt().toShort()
-
- @Suppress("RedundantOverride") // Without this, we get a SOE
- override fun toInt(): Int = super.toInt()
-
- companion object {
-
- @JvmField
- val NIL = ID(0L)
- }
-}
diff --git a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/utils/macos/MacUtil.kt b/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/utils/macos/MacUtil.kt
deleted file mode 100644
index 248cb170a1..0000000000
--- a/decorated-window/src/main/kotlin/org/jetbrains/jewel/window/utils/macos/MacUtil.kt
+++ /dev/null
@@ -1,95 +0,0 @@
-package org.jetbrains.jewel.window.utils.macos
-
-import org.jetbrains.jewel.window.utils.UnsafeAccessing
-import org.jetbrains.jewel.window.utils.accessible
-import java.awt.Component
-import java.awt.Window
-import java.lang.reflect.InvocationTargetException
-import java.util.logging.Level
-import java.util.logging.Logger
-import javax.swing.SwingUtilities
-
-internal object MacUtil {
-
- private val logger = Logger.getLogger(MacUtil::class.java.simpleName)
-
- init {
- try {
- UnsafeAccessing.assignAccessibility(
- UnsafeAccessing.desktopModule,
- listOf("sun.awt", "sun.lwawt", "sun.lwawt.macosx"),
- )
- } catch (@Suppress("TooGenericExceptionCaught") e: Exception) {
- logger.log(Level.WARNING, "Assign access for jdk.desktop failed.", e)
- }
- }
-
- fun getWindowFromJavaWindow(w: Window?): ID {
- if (w == null) {
- return ID.NIL
- }
- try {
- val cPlatformWindow = getPlatformWindow(w)
- if (cPlatformWindow != null) {
- val ptr = cPlatformWindow.javaClass.superclass.getDeclaredField("ptr")
- ptr.setAccessible(true)
- return ID(ptr.getLong(cPlatformWindow))
- }
- } catch (e: IllegalAccessException) {
- logger.log(Level.WARNING, "Fail to get cPlatformWindow from awt window.", e)
- } catch (e: NoSuchFieldException) {
- logger.log(Level.WARNING, "Fail to get cPlatformWindow from awt window.", e)
- }
- return ID.NIL
- }
-
- fun getPlatformWindow(w: Window): Any? {
- try {
- val awtAccessor = Class.forName("sun.awt.AWTAccessor")
- val componentAccessor = awtAccessor.getMethod("getComponentAccessor").invoke(null)
- val getPeer = componentAccessor.javaClass.getMethod("getPeer", Component::class.java).accessible()
- val peer = getPeer.invoke(componentAccessor, w)
- if (peer != null) {
- val cWindowPeerClass: Class<*> = peer.javaClass
- val getPlatformWindowMethod = cWindowPeerClass.getDeclaredMethod("getPlatformWindow")
- val cPlatformWindow = getPlatformWindowMethod.invoke(peer)
- if (cPlatformWindow != null) {
- return cPlatformWindow
- }
- }
- } catch (e: NoSuchMethodException) {
- logger.log(Level.WARNING, "Fail to get cPlatformWindow from awt window.", e)
- } catch (e: IllegalAccessException) {
- logger.log(Level.WARNING, "Fail to get cPlatformWindow from awt window.", e)
- } catch (e: InvocationTargetException) {
- logger.log(Level.WARNING, "Fail to get cPlatformWindow from awt window.", e)
- } catch (e: ClassNotFoundException) {
- logger.log(Level.WARNING, "Fail to get cPlatformWindow from awt window.", e)
- }
- return null
- }
-
- fun updateColors(w: Window) {
- SwingUtilities.invokeLater {
- val window = getWindowFromJavaWindow(w)
- val delegate = Foundation.invoke(window, "delegate")
- if (Foundation.invoke(delegate, "respondsToSelector:", Foundation.createSelector("updateColors"))
- .booleanValue()
- ) {
- Foundation.invoke(delegate, "updateColors")
- }
- }
- }
-
- fun updateFullScreenButtons(w: Window) {
- SwingUtilities.invokeLater {
- val selector = Foundation.createSelector("updateFullScreenButtons")
- val window = getWindowFromJavaWindow(w)
- val delegate = Foundation.invoke(window, "delegate")
-
- if (Foundation.invoke(delegate, "respondsToSelector:", selector).booleanValue()) {
- Foundation.invoke(delegate, "updateFullScreenButtons")
- }
- }
- }
-}
diff --git a/gradle.properties b/gradle.properties
index 2ce2eecdc4..1208c254b6 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -6,4 +6,4 @@ kotlin.stdlib.default.dependency=false
# See https://jb.gg/intellij-platform-kotlin-oom
kotlin.incremental.useClasspathSnapshot=false
-bridge.ijp.target=241
+bridge.ijp.target=232
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index aa0097a6a7..70c8909d16 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -2,7 +2,7 @@
composeDesktop = "1.6.0-dev1397"
detekt = "1.23.4"
dokka = "1.8.20"
-idea = "241.9959.31-EAP-SNAPSHOT"
+idea = "232.10227.8"
ideaGradlePlugin = "1.17.0"
jna = "5.14.0"
kotlin = "1.8.21"
diff --git a/ide-laf-bridge/api/ide-laf-bridge.api b/ide-laf-bridge/api/ide-laf-bridge.api
index b0d51fad0a..e029402075 100644
--- a/ide-laf-bridge/api/ide-laf-bridge.api
+++ b/ide-laf-bridge/api/ide-laf-bridge.api
@@ -78,6 +78,10 @@ public final class org/jetbrains/jewel/bridge/TypographyKt {
public static final fun small (Lorg/jetbrains/jewel/ui/component/Typography;Landroidx/compose/runtime/Composer;I)Landroidx/compose/ui/text/TextStyle;
}
+public final class org/jetbrains/jewel/bridge/UiThemeExtensionsKt {
+ public static final fun getIcons (Lcom/intellij/ide/ui/UITheme;)Ljava/util/Map;
+}
+
public final class org/jetbrains/jewel/bridge/actionSystem/ProvideDataKt {
public static final fun ComponentDataProviderBridge (Ljavax/swing/JComponent;Landroidx/compose/ui/Modifier;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;II)V
public static final fun provideData (Landroidx/compose/ui/Modifier;Lkotlin/jvm/functions/Function1;)Landroidx/compose/ui/Modifier;
diff --git a/ide-laf-bridge/src/main/kotlin/org/jetbrains/jewel/bridge/BridgeIconData.kt b/ide-laf-bridge/src/main/kotlin/org/jetbrains/jewel/bridge/BridgeIconData.kt
index 69d1b2c759..fb6627088b 100644
--- a/ide-laf-bridge/src/main/kotlin/org/jetbrains/jewel/bridge/BridgeIconData.kt
+++ b/ide-laf-bridge/src/main/kotlin/org/jetbrains/jewel/bridge/BridgeIconData.kt
@@ -1,14 +1,16 @@
package org.jetbrains.jewel.bridge
import com.intellij.ide.ui.UITheme
+import com.intellij.ui.ColorUtil
+import org.jetbrains.jewel.foundation.InternalJewelApi
import org.jetbrains.jewel.foundation.theme.ThemeIconData
-@Suppress("UnstableApiUsage")
+@OptIn(InternalJewelApi::class)
public fun ThemeIconData.Companion.readFromLaF(): ThemeIconData {
val uiTheme = currentUiThemeOrNull()
- val bean = uiTheme?.describe()
- val iconMap = bean?.icons.orEmpty()
- val selectedIconColorPalette = bean?.iconColorsOnSelection.orEmpty()
+ val iconMap = uiTheme?.icons.orEmpty()
+ val selectedIconColorPalette = uiTheme?.selectedIconColorPalette.orEmpty()
+ .mapValues { ColorUtil.fromHex(it.value).rgb }
val colorPalette = UITheme.getColorPalette()
return ThemeIconData(iconMap, colorPalette, selectedIconColorPalette)
diff --git a/ide-laf-bridge/src/main/kotlin/org/jetbrains/jewel/bridge/BridgeOverride.kt b/ide-laf-bridge/src/main/kotlin/org/jetbrains/jewel/bridge/BridgeOverride.kt
index e6a9af4231..3651942891 100644
--- a/ide-laf-bridge/src/main/kotlin/org/jetbrains/jewel/bridge/BridgeOverride.kt
+++ b/ide-laf-bridge/src/main/kotlin/org/jetbrains/jewel/bridge/BridgeOverride.kt
@@ -1,7 +1,5 @@
package org.jetbrains.jewel.bridge
-import com.intellij.openapi.diagnostic.Logger
-import com.intellij.ui.icons.patchIconPath
import com.intellij.util.ui.DirProvider
import org.jetbrains.jewel.ui.painter.PainterPathHint
import org.jetbrains.jewel.ui.painter.PainterProviderScope
@@ -11,7 +9,14 @@ internal object BridgeOverride : PainterPathHint {
private val dirProvider = DirProvider()
- @Suppress("UnstableApiUsage") // patchIconPath() is explicitly open to us
+ private val patchIconPath by lazy {
+ val clazz = Class.forName("com.intellij.ui.icons.CachedImageIconKt")
+ val patchIconPath =
+ clazz.getMethod("patchIconPath", String::class.java, ClassLoader::class.java)
+ patchIconPath.isAccessible = true
+ patchIconPath
+ }
+
override fun PainterProviderScope.patch(): String {
if (this !is ResourcePainterProviderScope) return path
@@ -20,49 +25,17 @@ internal object BridgeOverride : PainterPathHint {
// removed (the classloader is set up differently in prod IDEs and when running
// from Gradle, and the icon could be in either place depending on the environment)
val fallbackPath = path.removePrefix(dirProvider.dir())
-
- for (classLoader in classLoaders) {
- val patchedPath = patchIconPath(path.removePrefix("/"), classLoader)?.first
- ?: patchIconPath(fallbackPath, classLoader)?.first
-
- // 233 EAP 4 broke path patching horribly; now it can return a
- // "reflective path", which is a FQN to an ExpUIIcons entry.
- // As a (hopefully) temporary solution, we undo this transformation
- // back into the original path.
- if (patchedPath?.startsWith("com.intellij.icons.ExpUiIcons") == true) {
- return inferActualPathFromReflectivePath(patchedPath)
- }
-
- if (patchedPath != null) {
- return patchedPath
- }
- }
- return path
- }
-
- private fun inferActualPathFromReflectivePath(patchedPath: String): String {
- val iconPath = patchedPath.removePrefix("com.intellij.icons.ExpUiIcons.")
-
- return buildString {
- append("expui/")
- iconPath.split('.')
- .map { it.trim() }
- .filter { it.isNotEmpty() }
- .forEach {
- append(it.first().lowercaseChar())
- append(it.drop(1))
- append('/')
+ val patchedPath =
+ classLoaders
+ .firstNotNullOfOrNull { classLoader ->
+ val patchedPathAndClassLoader =
+ patchIconPath.invoke(null, path.removePrefix("/"), classLoader)
+ ?: patchIconPath.invoke(null, fallbackPath, classLoader)
+ patchedPathAndClassLoader as? Pair<*, *>
}
- replace(length - 1, length, "") // Drop last '/'
- if (iconPath.contains("_dark")) append("_dark")
- append(".svg")
+ ?.first as? String
- Logger.getInstance("IconsPathPatching")
- .warn(
- "IntelliJ returned a reflective path: $patchedPath for $iconPath." +
- " We reverted that to a plausible-looking resource path: ${toString()}",
- )
- }
+ return patchedPath ?: path
}
override fun toString(): String = "BridgeOverride"
diff --git a/ide-laf-bridge/src/main/kotlin/org/jetbrains/jewel/bridge/BridgePainterHintsProvider.kt b/ide-laf-bridge/src/main/kotlin/org/jetbrains/jewel/bridge/BridgePainterHintsProvider.kt
index 839a666573..6f600491c7 100644
--- a/ide-laf-bridge/src/main/kotlin/org/jetbrains/jewel/bridge/BridgePainterHintsProvider.kt
+++ b/ide-laf-bridge/src/main/kotlin/org/jetbrains/jewel/bridge/BridgePainterHintsProvider.kt
@@ -10,11 +10,12 @@ import org.jetbrains.jewel.ui.painter.BasePainterHintsProvider
import org.jetbrains.jewel.ui.painter.PainterHint
import org.jetbrains.jewel.ui.painter.hints.Dark
import org.jetbrains.jewel.ui.painter.hints.HiDpi
+import org.jetbrains.jewel.ui.util.fromRGBAHexStringOrNull
@InternalJewelApi
public class BridgePainterHintsProvider private constructor(
isDark: Boolean,
- intellijIconPalette: Map = emptyMap(),
+ intellijIconPalette: Map = emptyMap(),
themeIconPalette: Map = emptyMap(),
themeColorPalette: Map = emptyMap(),
) : BasePainterHintsProvider(isDark, intellijIconPalette, themeIconPalette, themeColorPalette) {
@@ -31,21 +32,20 @@ public class BridgePainterHintsProvider private constructor(
private val logger = thisLogger()
- @Suppress("UnstableApiUsage") // We need to call @Internal APIs
public operator fun invoke(isDark: Boolean): BasePainterHintsProvider {
val uiTheme = currentUiThemeOrNull() ?: return BridgePainterHintsProvider(isDark)
logger.info("Parsing theme info from theme ${uiTheme.name} (id: ${uiTheme.id}, isDark: ${uiTheme.isDark})")
- val bean = uiTheme.describe()
- val iconColorPalette =
- (bean.colorPalette as Map).mapValues {
- when (val value = it.value) {
- is String -> value
+ val iconColorPalette = uiTheme.iconColorPalette
+ val keyPalette = UITheme.getColorPalette()
+ val themeColors = uiTheme.colors.orEmpty()
+ .mapValues { (_, v) ->
+ when (v) {
+ is Int -> Color(v)
+ is String -> Color.fromRGBAHexStringOrNull(v)
else -> null
}
}
- val keyPalette = UITheme.getColorPalette()
- val themeColors = bean.colors.mapValues { (_, v) -> Color(v) }
return BridgePainterHintsProvider(isDark, keyPalette, iconColorPalette, themeColors)
}
diff --git a/ide-laf-bridge/src/main/kotlin/org/jetbrains/jewel/bridge/UiThemeExtensions.kt b/ide-laf-bridge/src/main/kotlin/org/jetbrains/jewel/bridge/UiThemeExtensions.kt
index 81a6cd6da6..c4a75d91af 100644
--- a/ide-laf-bridge/src/main/kotlin/org/jetbrains/jewel/bridge/UiThemeExtensions.kt
+++ b/ide-laf-bridge/src/main/kotlin/org/jetbrains/jewel/bridge/UiThemeExtensions.kt
@@ -1,8 +1,45 @@
package org.jetbrains.jewel.bridge
import com.intellij.ide.ui.LafManager
-import com.intellij.ide.ui.laf.UIThemeLookAndFeelInfo
+import com.intellij.ide.ui.UITheme
+import com.intellij.ide.ui.laf.UIThemeBasedLookAndFeelInfo
+import com.intellij.openapi.diagnostic.Logger
+import org.jetbrains.jewel.foundation.InternalJewelApi
+import java.lang.reflect.Field
-@Suppress("UnstableApiUsage")
-internal fun currentUiThemeOrNull(): UIThemeLookAndFeelInfo? =
- LafManager.getInstance().currentUIThemeLookAndFeel?.takeIf { it.isInitialized }
+private val logger = Logger.getInstance("UiThemeExtensions")
+
+private val classUITheme
+ get() = UITheme::class.java
+
+@InternalJewelApi
+internal fun currentUiThemeOrNull() =
+ (LafManager.getInstance().currentLookAndFeel as? UIThemeBasedLookAndFeelInfo)?.theme
+
+@InternalJewelApi
+public val UITheme.icons: Map
+ get() = readMapField(classUITheme.getDeclaredField("icons"))
+ .filterKeys { it != "ColorPalette" }
+
+internal val UITheme.iconColorPalette: Map
+ get() = readMapField