From a25b97614f8033e8429bf404ff67892546c9b005 Mon Sep 17 00:00:00 2001 From: hstyi Date: Fri, 14 Feb 2025 20:38:46 +0800 Subject: [PATCH] feat: Floating Toolbar (#231) --- src/main/kotlin/app/termora/Database.kt | 8 +- src/main/kotlin/app/termora/Icons.kt | 2 + .../app/termora/MultipleTerminalListener.kt | 17 +- .../kotlin/app/termora/PropertyTerminalTab.kt | 8 + .../kotlin/app/termora/SettingsOptionsPane.kt | 20 ++- .../app/termora/TerminalPanelFactory.kt | 6 + .../terminal/panel/FloatingToolbarPanel.kt | 156 ++++++++++++++++++ .../termora/terminal/panel/TerminalPanel.kt | 32 +++- ...erminalPanelMouseFloatingToolBarAdapter.kt | 49 ++++++ src/main/resources/i18n/messages.properties | 1 + .../resources/i18n/messages_zh_CN.properties | 1 + .../resources/i18n/messages_zh_TW.properties | 1 + src/main/resources/icons/closeSmall.svg | 6 + .../resources/icons/closeSmallHovered.svg | 7 + .../icons/closeSmallHovered_dark.svg | 7 + src/main/resources/icons/closeSmall_dark.svg | 6 + 16 files changed, 319 insertions(+), 8 deletions(-) create mode 100644 src/main/kotlin/app/termora/terminal/panel/FloatingToolbarPanel.kt create mode 100644 src/main/kotlin/app/termora/terminal/panel/TerminalPanelMouseFloatingToolBarAdapter.kt create mode 100644 src/main/resources/icons/closeSmall.svg create mode 100644 src/main/resources/icons/closeSmallHovered.svg create mode 100644 src/main/resources/icons/closeSmallHovered_dark.svg create mode 100644 src/main/resources/icons/closeSmall_dark.svg diff --git a/src/main/kotlin/app/termora/Database.kt b/src/main/kotlin/app/termora/Database.kt index f1a1229..7d377fa 100644 --- a/src/main/kotlin/app/termora/Database.kt +++ b/src/main/kotlin/app/termora/Database.kt @@ -18,9 +18,6 @@ import org.apache.commons.lang3.StringUtils import org.slf4j.LoggerFactory import java.io.File import java.util.* -import kotlin.collections.component1 -import kotlin.collections.component2 -import kotlin.collections.set import kotlin.properties.ReadWriteProperty import kotlin.reflect.KProperty import kotlin.time.Duration.Companion.minutes @@ -475,6 +472,11 @@ class Database private constructor(private val env: Environment) : Disposable { * 终端断开连接时自动关闭Tab */ var autoCloseTabWhenDisconnected by BooleanPropertyDelegate(false) + + /** + * 是否显示悬浮工具栏 + */ + var floatingToolbar by BooleanPropertyDelegate(true) } /** diff --git a/src/main/kotlin/app/termora/Icons.kt b/src/main/kotlin/app/termora/Icons.kt index d1cc91e..901c7d8 100644 --- a/src/main/kotlin/app/termora/Icons.kt +++ b/src/main/kotlin/app/termora/Icons.kt @@ -3,6 +3,8 @@ package app.termora object Icons { val bulletList by lazy { DynamicIcon("icons/bulletList.svg", "icons/bulletList_dark.svg") } val up by lazy { DynamicIcon("icons/up.svg", "icons/up_dark.svg") } + val closeSmall by lazy { DynamicIcon("icons/closeSmall.svg", "icons/closeSmall_dark.svg") } + val closeSmallHovered by lazy { DynamicIcon("icons/closeSmallHovered.svg", "icons/closeSmallHovered_dark.svg") } val plugin by lazy { DynamicIcon("icons/plugin.svg", "icons/plugin_dark.svg") } val moveUp by lazy { DynamicIcon("icons/moveUp.svg", "icons/moveUp_dark.svg") } val down by lazy { DynamicIcon("icons/down.svg", "icons/down_dark.svg") } diff --git a/src/main/kotlin/app/termora/MultipleTerminalListener.kt b/src/main/kotlin/app/termora/MultipleTerminalListener.kt index 5f36892..987f713 100644 --- a/src/main/kotlin/app/termora/MultipleTerminalListener.kt +++ b/src/main/kotlin/app/termora/MultipleTerminalListener.kt @@ -5,6 +5,7 @@ import app.termora.actions.ActionManager import app.termora.terminal.Terminal import app.termora.terminal.TerminalColor import app.termora.terminal.TextStyle +import app.termora.terminal.panel.FloatingToolbarPanel import app.termora.terminal.panel.TerminalDisplay import app.termora.terminal.panel.TerminalPaintListener import app.termora.terminal.panel.TerminalPanel @@ -32,13 +33,25 @@ class MultipleTerminalListener : TerminalPaintListener { // 正在搜索那么需要下移 val finding = terminal.getTerminalModel().getData(TerminalPanel.Finding, false) + // 如果悬浮窗正在显示,那么需要下移 + val floatingToolBar = terminalPanel.getData(FloatingToolbarPanel.FloatingToolbar)?.isVisible == true + + var y = g.fontMetrics.ascent + if (finding) { + y += g.fontMetrics.height + g.fontMetrics.ascent / 2 + } + + if (floatingToolBar) { + y += g.fontMetrics.height + g.fontMetrics.ascent / 2 + } + + g.font = font g.color = Color(colorPalette.getColor(TerminalColor.Normal.RED)) g.drawString( text, terminalDisplay.width - width - terminalPanel.getAverageCharWidth() / 2, - g.fontMetrics.ascent + if (finding) - g.fontMetrics.height + g.fontMetrics.ascent / 2 else 0 + y ) g.font = oldFont } diff --git a/src/main/kotlin/app/termora/PropertyTerminalTab.kt b/src/main/kotlin/app/termora/PropertyTerminalTab.kt index f6615fe..1a4f078 100644 --- a/src/main/kotlin/app/termora/PropertyTerminalTab.kt +++ b/src/main/kotlin/app/termora/PropertyTerminalTab.kt @@ -1,7 +1,11 @@ package app.termora +import app.termora.actions.AnActionEvent +import app.termora.terminal.panel.FloatingToolbarPanel +import org.apache.commons.lang3.StringUtils import java.beans.PropertyChangeEvent import java.beans.PropertyChangeListener +import java.util.* abstract class PropertyTerminalTab : TerminalTab { protected val listeners = mutableListOf() @@ -26,6 +30,10 @@ abstract class PropertyTerminalTab : TerminalTab { override fun onLostFocus() { hasFocus = false + + // 切换标签时,尝试隐藏悬浮工具栏 + val evt = AnActionEvent(getJComponent(), StringUtils.EMPTY, EventObject(getJComponent())) + evt.getData(FloatingToolbarPanel.FloatingToolbar)?.triggerHide() } diff --git a/src/main/kotlin/app/termora/SettingsOptionsPane.kt b/src/main/kotlin/app/termora/SettingsOptionsPane.kt index ff48dc7..fcb2a20 100644 --- a/src/main/kotlin/app/termora/SettingsOptionsPane.kt +++ b/src/main/kotlin/app/termora/SettingsOptionsPane.kt @@ -20,6 +20,7 @@ import app.termora.sync.SyncType import app.termora.sync.SyncerProvider import app.termora.terminal.CursorStyle import app.termora.terminal.DataKey +import app.termora.terminal.panel.FloatingToolbarPanel import app.termora.terminal.panel.TerminalPanel import cash.z.ecc.android.bip39.Mnemonics import com.formdev.flatlaf.FlatClientProperties @@ -310,6 +311,7 @@ class SettingsOptionsPane : OptionsPane() { private val terminalSetting get() = Database.getDatabase().terminal private val selectCopyComboBox = YesOrNoComboBox() private val autoCloseTabComboBox = YesOrNoComboBox() + private val floatingToolbarComboBox = YesOrNoComboBox() init { initView() @@ -332,6 +334,19 @@ class SettingsOptionsPane : OptionsPane() { } autoCloseTabComboBox.toolTipText = I18n.getString("termora.settings.terminal.auto-close-tab-description") + floatingToolbarComboBox.addItemListener { e -> + if (e.stateChange == ItemEvent.SELECTED) { + terminalSetting.floatingToolbar = floatingToolbarComboBox.selectedItem as Boolean + TerminalPanelFactory.getAllTerminalPanel().forEach { tp -> + if (terminalSetting.floatingToolbar && FloatingToolbarPanel.isPined) { + tp.getData(FloatingToolbarPanel.FloatingToolbar)?.triggerShow() + } else { + tp.getData(FloatingToolbarPanel.FloatingToolbar)?.triggerHide() + } + } + } + } + selectCopyComboBox.addItemListener { e -> if (e.stateChange == ItemEvent.SELECTED) { terminalSetting.selectCopy = selectCopyComboBox.selectedItem as Boolean @@ -477,6 +492,7 @@ class SettingsOptionsPane : OptionsPane() { cursorStyleComboBox.selectedItem = terminalSetting.cursor selectCopyComboBox.selectedItem = terminalSetting.selectCopy autoCloseTabComboBox.selectedItem = terminalSetting.autoCloseTabWhenDisconnected + floatingToolbarComboBox.selectedItem = terminalSetting.floatingToolbar } override fun getIcon(isSelected: Boolean): Icon { @@ -494,7 +510,7 @@ class SettingsOptionsPane : OptionsPane() { private fun getCenterComponent(): JComponent { val layout = FormLayout( "left:pref, $formMargin, default:grow, $formMargin, left:pref, $formMargin, pref, default:grow", - "pref, $formMargin, pref, $formMargin, pref, $formMargin, pref, $formMargin, pref, $formMargin, pref, $formMargin, pref, $formMargin, pref" + "pref, $formMargin, pref, $formMargin, pref, $formMargin, pref, $formMargin, pref, $formMargin, pref, $formMargin, pref, $formMargin, pref, $formMargin, pref" ) val beepBtn = JButton(Icons.run) @@ -521,6 +537,8 @@ class SettingsOptionsPane : OptionsPane() { .add(selectCopyComboBox).xy(3, rows).apply { rows += step } .add("${I18n.getString("termora.settings.terminal.cursor-style")}:").xy(1, rows) .add(cursorStyleComboBox).xy(3, rows).apply { rows += step } + .add("${I18n.getString("termora.settings.terminal.floating-toolbar")}:").xy(1, rows) + .add(floatingToolbarComboBox).xy(3, rows).apply { rows += step } .add("${I18n.getString("termora.settings.terminal.auto-close-tab")}:").xy(1, rows) .add(autoCloseTabComboBox).xy(3, rows).apply { rows += step } .add("${I18n.getString("termora.settings.terminal.local-shell")}:").xy(1, rows) diff --git a/src/main/kotlin/app/termora/TerminalPanelFactory.kt b/src/main/kotlin/app/termora/TerminalPanelFactory.kt index c9dd6e5..50a8862 100644 --- a/src/main/kotlin/app/termora/TerminalPanelFactory.kt +++ b/src/main/kotlin/app/termora/TerminalPanelFactory.kt @@ -16,6 +16,12 @@ class TerminalPanelFactory { fun getInstance(scope: Scope): TerminalPanelFactory { return scope.getOrCreate(TerminalPanelFactory::class) { TerminalPanelFactory() } } + + fun getAllTerminalPanel(): List { + return ApplicationScope.forApplicationScope().windowScopes() + .map { getInstance(it) } + .flatMap { it.getTerminalPanels() } + } } fun createTerminalPanel(terminal: Terminal, ptyConnector: PtyConnector): TerminalPanel { diff --git a/src/main/kotlin/app/termora/terminal/panel/FloatingToolbarPanel.kt b/src/main/kotlin/app/termora/terminal/panel/FloatingToolbarPanel.kt new file mode 100644 index 0000000..3e38293 --- /dev/null +++ b/src/main/kotlin/app/termora/terminal/panel/FloatingToolbarPanel.kt @@ -0,0 +1,156 @@ +package app.termora.terminal.panel + +import app.termora.* +import app.termora.actions.AnAction +import app.termora.actions.AnActionEvent +import app.termora.actions.DataProviders +import app.termora.terminal.DataKey +import com.formdev.flatlaf.extras.components.FlatToolBar +import com.formdev.flatlaf.ui.FlatRoundBorder +import org.apache.commons.lang3.StringUtils +import java.awt.event.ActionListener +import javax.swing.JButton + +class FloatingToolbarPanel : FlatToolBar(), Disposable { + private val floatingToolbarEnable get() = Database.getDatabase().terminal.floatingToolbar + private var closed = false + + companion object { + + val FloatingToolbar = DataKey(FloatingToolbarPanel::class) + val isPined get() = pinAction.isSelected + + private val pinAction by lazy { + object : AnAction() { + private val properties get() = Database.getDatabase().properties + private val key = "FloatingToolbar.pined" + + init { + setStateAction() + isSelected = properties.getString(key, StringUtils.EMPTY).toBoolean() + } + + override fun actionPerformed(evt: AnActionEvent) { + isSelected = !isSelected + properties.putString(key, isSelected.toString()) + actionListeners.forEach { it.actionPerformed(evt) } + + if (isSelected) { + TerminalPanelFactory.getAllTerminalPanel().forEach { + it.getData(FloatingToolbar)?.triggerShow() + } + } else { + // 触发者的不隐藏 + val c = evt.getData(FloatingToolbar) + TerminalPanelFactory.getAllTerminalPanel().forEach { + val e = it.getData(FloatingToolbar) + if (c != e) { + e?.triggerHide() + } + } + } + } + } + } + } + + init { + border = FlatRoundBorder() + isOpaque = false + isFocusable = false + isFloatable = false + isVisible = false + + if (floatingToolbarEnable) { + if (pinAction.isSelected) { + isVisible = true + } + } + + initActions() + } + + fun triggerShow() { + if (!floatingToolbarEnable || closed) { + return + } + + if (isVisible == false) { + isVisible = true + firePropertyChange("visible", false, true) + } + } + + fun triggerHide() { + if (floatingToolbarEnable && !closed) { + if (pinAction.isSelected) { + return + } + } + + if (isVisible == true) { + isVisible = false + firePropertyChange("visible", true, false) + } + } + + private fun initActions() { + // Pin + add(initPinActionButton()) + + // 重连 + add(initReconnectActionButton()) + + // 关闭 + add(initCloseActionButton()) + } + + private fun initPinActionButton(): JButton { + val btn = JButton(Icons.pin) + btn.isSelected = pinAction.isSelected + + val actionListener = ActionListener { btn.isSelected = pinAction.isSelected } + pinAction.addActionListener(actionListener) + btn.addActionListener(pinAction) + + Disposer.register(this, object : Disposable { + override fun dispose() { + btn.removeActionListener(pinAction) + pinAction.removeActionListener(actionListener) + } + }) + + return btn + } + + private fun initCloseActionButton(): JButton { + val btn = JButton(Icons.closeSmall) + btn.pressedIcon = Icons.closeSmallHovered + btn.rolloverIcon = Icons.closeSmallHovered + btn.addActionListener { + closed = true + triggerHide() + } + return btn + } + + private fun initReconnectActionButton(): JButton { + val btn = JButton(Icons.refresh) + btn.toolTipText = I18n.getString("termora.tabbed.contextmenu.reconnect") + + btn.addActionListener(object : AnAction() { + override fun actionPerformed(evt: AnActionEvent) { + val tab = evt.getData(DataProviders.TerminalTab) ?: return + if (tab.canReconnect()) { + tab.reconnect() + } + } + }) + return btn + } + + override fun dispose() { + + } + +} \ No newline at end of file diff --git a/src/main/kotlin/app/termora/terminal/panel/TerminalPanel.kt b/src/main/kotlin/app/termora/terminal/panel/TerminalPanel.kt index 684ac75..eaede2b 100644 --- a/src/main/kotlin/app/termora/terminal/panel/TerminalPanel.kt +++ b/src/main/kotlin/app/termora/terminal/panel/TerminalPanel.kt @@ -1,6 +1,7 @@ package app.termora.terminal.panel import app.termora.Disposable +import app.termora.Disposer import app.termora.actions.DataProvider import app.termora.actions.DataProviderSupport import app.termora.actions.DataProviders @@ -40,10 +41,12 @@ class TerminalPanel(val terminal: Terminal, private val ptyConnector: PtyConnect } private val terminalFindPanel = TerminalFindPanel(this, terminal) + private val floatingToolbar = FloatingToolbarPanel() private val terminalDisplay = TerminalDisplay(this, terminal) - val scrollBar = TerminalScrollBar(this@TerminalPanel, terminalFindPanel, terminal) private val dataProviderSupport = DataProviderSupport() + val scrollBar = TerminalScrollBar(this@TerminalPanel, terminalFindPanel, terminal) + /** * 键盘事件 @@ -117,6 +120,7 @@ class TerminalPanel(val terminal: Terminal, private val ptyConnector: PtyConnect val layeredPane = TerminalLayeredPane() layeredPane.add(terminalDisplay, JLayeredPane.DEFAULT_LAYER as Any) layeredPane.add(terminalFindPanel, JLayeredPane.POPUP_LAYER as Any) + layeredPane.add(floatingToolbar, JLayeredPane.POPUP_LAYER as Any) add(layeredPane, BorderLayout.CENTER) add(scrollBar, BorderLayout.EAST) @@ -127,6 +131,7 @@ class TerminalPanel(val terminal: Terminal, private val ptyConnector: PtyConnect dataProviderSupport.addData(DataProviders.TerminalPanel, this) dataProviderSupport.addData(DataProviders.Terminal, terminal) dataProviderSupport.addData(DataProviders.PtyConnector, ptyConnector) + dataProviderSupport.addData(FloatingToolbarPanel.FloatingToolbar, floatingToolbar) } private fun initEvents() { @@ -158,6 +163,11 @@ class TerminalPanel(val terminal: Terminal, private val ptyConnector: PtyConnect this.addMouseListener(trackingAdapter) this.addMouseWheelListener(trackingAdapter) + // 悬浮工具栏 + val floatingToolBarAdapter = TerminalPanelMouseFloatingToolBarAdapter(this, terminalDisplay) + this.addMouseMotionListener(floatingToolBarAdapter) + this.addMouseListener(floatingToolBarAdapter) + // 滚动相关 this.addMouseWheelListener(object : MouseWheelListener { override fun mouseWheelMoved(e: MouseWheelEvent) { @@ -197,6 +207,8 @@ class TerminalPanel(val terminal: Terminal, private val ptyConnector: PtyConnect // 开启拖拽 enableDropTarget() + // 监听悬浮工具栏变化,然后重新渲染 + floatingToolbar.addPropertyChangeListener { repaintImmediate() } } @@ -373,6 +385,9 @@ class TerminalPanel(val terminal: Terminal, private val ptyConnector: PtyConnect } + override fun dispose() { + Disposer.dispose(floatingToolbar) + } fun getAverageCharWidth(): Int { return terminalDisplay.getAverageCharWidth() @@ -450,6 +465,7 @@ class TerminalPanel(val terminal: Terminal, private val ptyConnector: PtyConnect synchronized(treeLock) { val w = width val h = height + val findPanelHeight = max(terminalFindPanel.preferredSize.height, terminalFindPanel.height) for (c in components) { when (c) { terminalDisplay -> { @@ -467,7 +483,19 @@ class TerminalPanel(val terminal: Terminal, private val ptyConnector: PtyConnect w - width, 0, width, - max(terminalFindPanel.preferredSize.height, terminalFindPanel.height) + findPanelHeight + ) + } + + floatingToolbar -> { + val width = floatingToolbar.preferredSize.width + val height = floatingToolbar.preferredSize.height + val y = 4 + c.setBounds( + w - width, + if (terminalFindPanel.isVisible) findPanelHeight + y else y, + width, + height ) } } diff --git a/src/main/kotlin/app/termora/terminal/panel/TerminalPanelMouseFloatingToolBarAdapter.kt b/src/main/kotlin/app/termora/terminal/panel/TerminalPanelMouseFloatingToolBarAdapter.kt new file mode 100644 index 0000000..50d8ae2 --- /dev/null +++ b/src/main/kotlin/app/termora/terminal/panel/TerminalPanelMouseFloatingToolBarAdapter.kt @@ -0,0 +1,49 @@ +package app.termora.terminal.panel + +import app.termora.Database +import java.awt.Rectangle +import java.awt.event.MouseAdapter +import java.awt.event.MouseEvent + +class TerminalPanelMouseFloatingToolBarAdapter( + private val terminalPanel: TerminalPanel, + private val terminalDisplay: TerminalDisplay +) : MouseAdapter() { + + private val floatingToolbarEnable get() = Database.getDatabase().terminal.floatingToolbar + + override fun mouseMoved(e: MouseEvent) { + if (!floatingToolbarEnable) { + return + } + + val floatingToolbar = terminalPanel.getData(FloatingToolbarPanel.FloatingToolbar) ?: return + val width = terminalPanel.width + val height = terminalPanel.height + val widthDiff = (width * 0.25).toInt() + val heightDiff = (height * 0.25).toInt() + + if (e.x in width - widthDiff..width && e.y in 0..heightDiff) { + floatingToolbar.triggerShow() + } else { + floatingToolbar.triggerHide() + } + } + + override fun mouseExited(e: MouseEvent) { + val floatingToolbar = terminalPanel.getData(FloatingToolbarPanel.FloatingToolbar) ?: return + + if (terminalDisplay.isShowing) { + val rectangle = Rectangle(terminalDisplay.locationOnScreen, terminalDisplay.size) + // 如果鼠标指针还在 terminalDisplay 中,那么就不需要隐藏 + if (rectangle.contains(e.locationOnScreen)) { + return + } + } + + + floatingToolbar.triggerHide() + } + + +} \ No newline at end of file diff --git a/src/main/resources/i18n/messages.properties b/src/main/resources/i18n/messages.properties index 3a4dc85..35616e7 100644 --- a/src/main/resources/i18n/messages.properties +++ b/src/main/resources/i18n/messages.properties @@ -70,6 +70,7 @@ termora.settings.terminal.beep=Beep termora.settings.terminal.select-copy=Select copy termora.settings.terminal.cursor-style=Cursor type termora.settings.terminal.local-shell=Local shell +termora.settings.terminal.floating-toolbar=Floating Toolbar termora.settings.terminal.auto-close-tab=Auto Close Tab termora.settings.terminal.auto-close-tab-description=Automatically close the tab when the terminal is disconnected normally diff --git a/src/main/resources/i18n/messages_zh_CN.properties b/src/main/resources/i18n/messages_zh_CN.properties index 0792710..6eab2bc 100644 --- a/src/main/resources/i18n/messages_zh_CN.properties +++ b/src/main/resources/i18n/messages_zh_CN.properties @@ -77,6 +77,7 @@ termora.settings.terminal.beep=蜂鸣声 termora.settings.terminal.select-copy=选中复制 termora.settings.terminal.cursor-style=光标样式 termora.settings.terminal.local-shell=本地终端 +termora.settings.terminal.floating-toolbar=悬浮工具栏 termora.settings.terminal.auto-close-tab=自动关闭标签 termora.settings.terminal.auto-close-tab-description=当终端正常断开连接时自动关闭标签页 diff --git a/src/main/resources/i18n/messages_zh_TW.properties b/src/main/resources/i18n/messages_zh_TW.properties index f5e10c0..69b90e8 100644 --- a/src/main/resources/i18n/messages_zh_TW.properties +++ b/src/main/resources/i18n/messages_zh_TW.properties @@ -86,6 +86,7 @@ termora.settings.terminal.beep=蜂鳴聲 termora.settings.terminal.select-copy=選取複製 termora.settings.terminal.cursor-style=遊標風格 termora.settings.terminal.local-shell=本地端 +termora.settings.terminal.floating-toolbar=懸浮工具列 termora.settings.terminal.auto-close-tab=自動關閉標籤 termora.settings.terminal.auto-close-tab-description=當終端正常斷開連線時自動關閉標籤頁 diff --git a/src/main/resources/icons/closeSmall.svg b/src/main/resources/icons/closeSmall.svg new file mode 100644 index 0000000..75b624b --- /dev/null +++ b/src/main/resources/icons/closeSmall.svg @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/icons/closeSmallHovered.svg b/src/main/resources/icons/closeSmallHovered.svg new file mode 100644 index 0000000..a84bc61 --- /dev/null +++ b/src/main/resources/icons/closeSmallHovered.svg @@ -0,0 +1,7 @@ + + + + + diff --git a/src/main/resources/icons/closeSmallHovered_dark.svg b/src/main/resources/icons/closeSmallHovered_dark.svg new file mode 100644 index 0000000..bac58a6 --- /dev/null +++ b/src/main/resources/icons/closeSmallHovered_dark.svg @@ -0,0 +1,7 @@ + + + + + diff --git a/src/main/resources/icons/closeSmall_dark.svg b/src/main/resources/icons/closeSmall_dark.svg new file mode 100644 index 0000000..d609e39 --- /dev/null +++ b/src/main/resources/icons/closeSmall_dark.svg @@ -0,0 +1,6 @@ + + + +