diff --git a/platform/testFramework/extensions/src/com/intellij/keymap/KeymapsTestCase.java b/platform/testFramework/extensions/src/com/intellij/keymap/KeymapsTestCase.java index cccd81daf1d2..eb13f43ca47a 100644 --- a/platform/testFramework/extensions/src/com/intellij/keymap/KeymapsTestCase.java +++ b/platform/testFramework/extensions/src/com/intellij/keymap/KeymapsTestCase.java @@ -55,7 +55,7 @@ public abstract class KeymapsTestCase extends KeymapsTestCaseBase { {"control 1", "FileChooser.GotoHome", "GotoBookmark1", "DuplicatesForm.SendToLeft"}, {"control 2", "FileChooser.GotoProject", "GotoBookmark2", "DuplicatesForm.SendToRight"}, {"control 3", "GotoBookmark3", "FileChooser.GotoModule"}, - {"control ADD", "ExpandAll", "ExpandExpandableComponent", "ExpandRegion"}, + {"control ADD", "ExpandAll", "ExpandExpandableComponent", "ExpandRegion", "Markdown.Preview.IncreaseFontSize"}, {"control A", "$SelectAll", "Terminal.MoveCaretToLineStart"}, {"control B", "GotoDeclaration", "org.intellij.plugins.markdown.ui.actions.styling.ToggleBoldAction"}, {"control C", "$Copy", "Terminal.ClearPrompt", "Terminal.CopySelectedText", "org.jetbrains.r.console.RConsoleView.RInterruptAction", @@ -69,19 +69,19 @@ public abstract class KeymapsTestCase extends KeymapsTestCaseBase { {"control ENTER", "Console.Execute.Multiline", "DirDiffMenu.SynchronizeDiff.All", "EditorSplitLine", "NotebookRunCellAction", "PyExecuteCellAction", "SplitChooser.Duplicate", "Terminal.SmartCommandExecution.Run", "ViewSource", "org.jetbrains.r.actions.RunSelection", "Docker.RemoteServers.StartComposeService", "GraphQLExecuteEditor"}, - {"control EQUALS", "ExpandAll", "ExpandExpandableComponent", "ExpandRegion"}, + {"control EQUALS", "ExpandAll", "ExpandExpandableComponent", "ExpandRegion", "Markdown.Preview.IncreaseFontSize"}, {"control F5", "Refresh", "Rerun"}, {"control I", "ImplementMethods", "org.intellij.plugins.markdown.ui.actions.styling.ToggleItalicAction"}, {"control INSERT", "$Copy", "Terminal.CopySelectedText"}, {"control M", "EditorScrollToCenter", "Vcs.ShowMessageHistory"}, - {"control MINUS", "CollapseAll", "CollapseExpandableComponent", "CollapseRegion"}, + {"control MINUS", "CollapseAll", "CollapseExpandableComponent", "CollapseRegion", "Markdown.Preview.DecreaseFontSize"}, {"control N", "FileChooser.NewFolder", "GotoClass"}, {"control P", "FileChooser.TogglePathBar", "ParameterInfo"}, {"control PERIOD", "EditorChooseLookupItemDot", "CollapseSelection"}, {"control R", "Replace", "Terminal.SearchInCommandHistory", "org.jetbrains.plugins.ruby.rails.console.ReloadSources"}, {"control SLASH", "CommentByLineComment", "Graph.ActualSize"}, {"control SPACE", "CodeCompletion", "ChangesView.SetDefault"}, - {"control SUBTRACT", "CollapseAll", "CollapseExpandableComponent", "CollapseRegion"}, + {"control SUBTRACT", "CollapseAll", "CollapseExpandableComponent", "CollapseRegion", "Markdown.Preview.DecreaseFontSize"}, {"control U", "GotoSuperMethod", "CommanderSwapPanels", "org.intellij.plugins.markdown.ui.actions.styling.InsertImageAction"}, {"control UP", "EditorScrollUp", "EditorLookupUp", "MethodOverloadSwitchUp", "NotebookSelectCellAboveAction", "Terminal.SelectLastBlock", "Terminal.SelectBlockAbove"}, @@ -290,13 +290,13 @@ public abstract class KeymapsTestCase extends KeymapsTestCaseBase { {"ctrl H", "Replace", "Vcs.ShowMessageHistory"}, {"ctrl I", "IncrementalSearch", "org.intellij.plugins.markdown.ui.actions.styling.ToggleItalicAction"}, {"ctrl L", "EditorDeleteLine", "Terminal.ClearBuffer"}, - {"ctrl MINUS", "Back", "CollapseAll", "CollapseExpandableComponent"}, + {"ctrl MINUS", "Back", "CollapseAll", "CollapseExpandableComponent", "Markdown.Preview.DecreaseFontSize"}, {"ctrl N", "FileChooser.NewFolder", "NewElement"}, {"ctrl P", "FileChooser.TogglePathBar", "Print"}, {"ctrl PERIOD", "EditorChooseLookupItemDot", "ShowIntentionActions"}, {"ctrl R", SECOND_STROKE, "Terminal.SearchInCommandHistory", "org.jetbrains.plugins.ruby.rails.console.ReloadSources"}, {"ctrl R,R", "ChangesView.Rename", "Git.Rename.Local.Branch", "Git.Reword.Commit", "RenameElement", "ShelvedChanges.Rename"}, - {"ctrl SUBTRACT", "Back", "CollapseAll", "CollapseExpandableComponent"}, + {"ctrl SUBTRACT", "Back", "CollapseAll", "CollapseExpandableComponent", "Markdown.Preview.DecreaseFontSize"}, {"ctrl alt B", "ViewBreakpoints", "org.jetbrains.r.rendering.chunk.RunChunksAboveAction"}, {"ctrl alt ENTER", "ReformatCode", "org.jetbrains.r.actions.DebugSelection"}, {"ctrl alt F", "ActivateStructureToolWindow", "ShowFilterPopup"}, @@ -333,8 +333,8 @@ public abstract class KeymapsTestCase extends KeymapsTestCaseBase { {"ctrl alt LEFT", "GotoSuperMethod", "ResizeToolWindowLeft"}, {"meta Y", "$Redo", "QuickImplementations"}, {"meta alt B", "ViewBreakpoints", "org.jetbrains.r.rendering.chunk.RunChunksAboveAction"}, - {"meta SUBTRACT", "Back", "CollapseAll", "CollapseExpandableComponent"}, - {"meta MINUS", "Back", "CollapseAll", "CollapseExpandableComponent"}, + {"meta SUBTRACT", "Back", "CollapseAll", "CollapseExpandableComponent", "Markdown.Preview.DecreaseFontSize"}, + {"meta MINUS", "Back", "CollapseAll", "CollapseExpandableComponent", "Markdown.Preview.DecreaseFontSize"}, {"shift F5", "Graph.ApplyCurrentLayout", "Stop"}, {"shift meta alt R", "ChooseRunConfiguration", "ForceRefresh"}, {"ctrl alt F", "ActivateStructureToolWindow", "EditorNextWord", "ShowFilterPopup"}, @@ -423,6 +423,8 @@ public abstract class KeymapsTestCase extends KeymapsTestCaseBase { {"shift ctrl alt LEFT", "NextEditorTab", "Diff.NextChange"}, {"shift ctrl alt RIGHT", "PreviousEditorTab", "Diff.PrevChange"}, {"shift ctrl DIVIDE", "CollapseAll", "CollapseAllRegions"}, + {"ctrl MINUS", "EditorDecreaseFontSizeGlobal", "Markdown.Preview.DecreaseFontSize"}, + {"ctrl EQUALS", "EditorIncreaseFontSizeGlobal", "Markdown.Preview.IncreaseFontSize"}, }), Map.entry("NetBeans 6.5", new String[][]{ {"F4", "RunToCursor", "EditSource"}, @@ -495,6 +497,8 @@ public abstract class KeymapsTestCase extends KeymapsTestCaseBase { {"shift meta U", "FindUsagesInFile", "ShelveChanges.UnshelveWithDialog", "Markdown.Styling.CreateLink"}, {"shift meta X", "EditorToggleCase", "com.jetbrains.php.framework.FrameworkRunConsoleAction"}, {"shift meta DIVIDE", "CollapseAll", "CollapseAllRegions"}, + {"meta MINUS", "EditorDecreaseFontSizeGlobal", "Markdown.Preview.DecreaseFontSize"}, + {"meta EQUALS", "EditorIncreaseFontSizeGlobal", "Markdown.Preview.IncreaseFontSize"}, }), Map.entry("Sublime Text", new String[][]{ {"F2", "ChangesView.Rename", "Console.TableResult.EditValue", "Git.Reword.Commit", "Git.Rename.Local.Branch", @@ -502,22 +506,22 @@ public abstract class KeymapsTestCase extends KeymapsTestCaseBase { "XDebugger.SetValue", "Table-startEditing", "Tree-startEditing", "Console.TableResult.GotoReferencedResult"}, {"F12", "GotoDeclaration", "WebInspector.Browser.Selection.Toggle"}, {"alt MINUS", "Back", "RInsertAssignmentAction"}, - {"ctrl ADD", "EditorIncreaseFontSize", "ExpandAll", "ExpandExpandableComponent"}, + {"ctrl ADD", "EditorIncreaseFontSize", "ExpandAll", "ExpandExpandableComponent", "Markdown.Preview.IncreaseFontSize"}, {"ctrl B", "Compile", "org.intellij.plugins.markdown.ui.actions.styling.ToggleBoldAction"}, {"ctrl D", "CompareTwoFiles", "Diff.ShowDiff", "FileChooser.GotoDesktop", "SelectNextOccurrence", "SendEOF", "Terminal.CloseSession", "org.jetbrains.r.console.RConsoleView.REofAction"}, {"ctrl ENTER", "Console.Execute.Multiline", "DirDiffMenu.SynchronizeDiff.All", "EditorStartNewLine", "NotebookRunCellAction", "PyExecuteCellAction", "ViewSource", "org.jetbrains.r.actions.RunSelection", "Terminal.SmartCommandExecution.Run", "SplitChooser.Duplicate", "Docker.RemoteServers.StartComposeService", "GraphQLExecuteEditor"}, - {"ctrl EQUALS", "EditorIncreaseFontSize", "ExpandAll", "ExpandExpandableComponent"}, + {"ctrl EQUALS", "EditorIncreaseFontSize", "ExpandAll", "ExpandExpandableComponent", "Markdown.Preview.IncreaseFontSize"}, {"ctrl I", "IncrementalSearch", "org.intellij.plugins.markdown.ui.actions.styling.ToggleItalicAction"}, {"ctrl L", "EditorSelectLine", "Terminal.ClearBuffer"}, {"ctrl M", "EditorMatchBrace", "Vcs.ShowMessageHistory"}, - {"ctrl MINUS", "CollapseAll", "CollapseExpandableComponent", "EditorDecreaseFontSize"}, + {"ctrl MINUS", "CollapseAll", "CollapseExpandableComponent", "EditorDecreaseFontSize", "Markdown.Preview.DecreaseFontSize"}, {"ctrl N", "FileChooser.NewFolder", "NewElement"}, {"ctrl P", "FileChooser.TogglePathBar", "GotoFile"}, {"ctrl R", "FileStructurePopup", "Terminal.SearchInCommandHistory", "org.jetbrains.plugins.ruby.rails.console.ReloadSources"}, - {"ctrl SUBTRACT", "CollapseAll", "CollapseExpandableComponent", "EditorDecreaseFontSize"}, + {"ctrl SUBTRACT", "CollapseAll", "CollapseExpandableComponent", "EditorDecreaseFontSize", "Markdown.Preview.DecreaseFontSize"}, {"ctrl V", "EditorPasteSimple", "Terminal.Paste", "JupyterNotebookPasteCellCommandModeAction"}, {"ctrl W", "CloseContent", "Terminal.DeletePreviousWord"}, {"ctrl alt DOWN", "Console.TableResult.NextPage", "EditorCloneCaretBelow"}, @@ -539,7 +543,7 @@ public abstract class KeymapsTestCase extends KeymapsTestCaseBase { "XDebugger.SetValue", "Table-startEditing", "Tree-startEditing", "Console.TableResult.GotoReferencedResult"}, {"F12", "GotoDeclaration", "WebInspector.Browser.Selection.Toggle"}, {"ctrl R", "GotoNextBookmark", "Terminal.SearchInCommandHistory"}, - {"meta ADD", "EditorIncreaseFontSize", "ExpandAll", "ExpandExpandableComponent"}, + {"meta ADD", "EditorIncreaseFontSize", "ExpandAll", "ExpandExpandableComponent", "Markdown.Preview.IncreaseFontSize"}, {"meta B", "Compile", "org.intellij.plugins.markdown.ui.actions.styling.ToggleBoldAction"}, {"meta BACK_SPACE", "EditorDeleteToLineStart", "$Delete"}, {"meta D", "CompareTwoFiles", "Diff.ShowDiff", "FileChooser.GotoDesktop", "SelectNextOccurrence", "SendEOF"}, @@ -547,14 +551,14 @@ public abstract class KeymapsTestCase extends KeymapsTestCaseBase { {"meta ENTER", "Console.Execute.Multiline", "DirDiffMenu.SynchronizeDiff.All", "EditorStartNewLine", "ViewSource", "org.jetbrains.r.actions.RunSelection", "Terminal.SmartCommandExecution.Run", "SplitChooser.Duplicate", "Docker.RemoteServers.StartComposeService", "GraphQLExecuteEditor"}, - {"meta EQUALS", "EditorIncreaseFontSize", "ExpandAll", "ExpandExpandableComponent"}, + {"meta EQUALS", "EditorIncreaseFontSize", "ExpandAll", "ExpandExpandableComponent", "Markdown.Preview.IncreaseFontSize"}, {"meta I", "DatabaseView.PropertiesAction", "IncrementalSearch", "org.intellij.plugins.markdown.ui.actions.styling.ToggleItalicAction"}, {"meta K", SECOND_STROKE, "Terminal.ClearBuffer"}, {"meta L", "EditorSelectLine", "Terminal.ClearBuffer"}, - {"meta MINUS", "CollapseAll", "CollapseExpandableComponent", "EditorDecreaseFontSize"}, + {"meta MINUS", "CollapseAll", "CollapseExpandableComponent", "EditorDecreaseFontSize", "Markdown.Preview.DecreaseFontSize"}, {"meta P", "FileChooser.TogglePathBar", "GotoFile"}, {"meta R", "FileStructurePopup", "Refresh", "Rerun", "org.jetbrains.plugins.ruby.rails.console.ReloadSources"}, - {"meta SUBTRACT", "CollapseAll", "CollapseExpandableComponent", "EditorDecreaseFontSize"}, + {"meta SUBTRACT", "CollapseAll", "CollapseExpandableComponent", "EditorDecreaseFontSize", "Markdown.Preview.DecreaseFontSize"}, {"meta T", "GotoFile", "Terminal.NewTab"}, {"meta UP", "EditorTextStart", "FileChooser.GoToParent", "Terminal.SelectLastBlock", "Terminal.SelectBlockAbove"}, {"meta V", "EditorPasteSimple", "Terminal.Paste", "JupyterNotebookPasteCellCommandModeAction"}, diff --git a/plugins/markdown/core/resources/META-INF/plugin.xml b/plugins/markdown/core/resources/META-INF/plugin.xml index ab4a9646cbbb..488641927597 100644 --- a/plugins/markdown/core/resources/META-INF/plugin.xml +++ b/plugins/markdown/core/resources/META-INF/plugin.xml @@ -595,13 +595,25 @@ class="org.intellij.plugins.markdown.extensions.CleanupExtensionsExternalFilesAction" icon="AllIcons.Actions.GC"/> - + + class="org.intellij.plugins.markdown.ui.actions.ChangeFontSizeAction$Increase"> + + + - + class="org.intellij.plugins.markdown.ui.actions.ChangeFontSizeAction$Decrease"> + + + + + + + + + diff --git a/plugins/markdown/core/resources/messages/MarkdownBundle.properties b/plugins/markdown/core/resources/messages/MarkdownBundle.properties index f65b628b78fe..6a38c23e94b8 100644 --- a/plugins/markdown/core/resources/messages/MarkdownBundle.properties +++ b/plugins/markdown/core/resources/messages/MarkdownBundle.properties @@ -367,3 +367,6 @@ group.Markdown.Preview.FontSize.text=Preview Font Size action.Markdown.Preview.IncreaseFontSize.text=Increase Preview Font Size action.Markdown.Preview.DecreaseFontSize.text=Decrease Preview Font Size action.Markdown.Preview.ResetFontSize.text=Reset Preview Font Size + +action.Markdown.Preview.AdjustFontSize.text=Adjust Font Size\u2026 +action.Markdown.Preview.FontSize.label.text=Font size: \ No newline at end of file diff --git a/plugins/markdown/core/src/org/intellij/plugins/markdown/settings/MarkdownSettingsConfigurable.kt b/plugins/markdown/core/src/org/intellij/plugins/markdown/settings/MarkdownSettingsConfigurable.kt index 0c45ec780016..2a89347fc41c 100644 --- a/plugins/markdown/core/src/org/intellij/plugins/markdown/settings/MarkdownSettingsConfigurable.kt +++ b/plugins/markdown/core/src/org/intellij/plugins/markdown/settings/MarkdownSettingsConfigurable.kt @@ -19,6 +19,7 @@ import com.intellij.openapi.fileTypes.UnknownFileType import com.intellij.openapi.options.BoundSearchableConfigurable import com.intellij.openapi.options.ex.Settings import com.intellij.openapi.project.Project +import com.intellij.openapi.ui.ComboBox import com.intellij.openapi.ui.DialogPanel import com.intellij.openapi.ui.TextFieldWithBrowseButton import com.intellij.openapi.ui.ValidationInfo @@ -27,7 +28,6 @@ import com.intellij.ui.EnumComboBoxModel import com.intellij.ui.SimpleListCellRenderer import com.intellij.ui.components.ActionLink import com.intellij.ui.components.JBCheckBox -import com.intellij.ui.components.JBTextField import com.intellij.ui.dsl.builder.* import com.intellij.ui.layout.ValidationInfoBuilder import com.intellij.util.application @@ -159,15 +159,19 @@ internal class MarkdownSettingsConfigurable(private val project: Project): Bound } } - private fun Row.previewFontSizeField(): Cell { - return intTextField(range = 0..300).bindIntText( + private fun Row.previewFontSizeField(): Cell> { + return comboBox(fontSizeOptions).bindItem( getter = { service().state.fontSize }, setter = { value -> service().update { settings -> - settings.state.fontSize = value + if (value != null) { + settings.state.fontSize = value + } } } - ) + ).applyToComponent { + isEditable = true + } } private fun validateCustomStylesheetPath(builder: ValidationInfoBuilder, textField: TextFieldWithBrowseButton): ValidationInfo? { @@ -374,5 +378,7 @@ internal class MarkdownSettingsConfigurable(private val project: Project): Bound else -> "" } } + + val fontSizeOptions = listOf(8, 9, 10, 11, 12, 14, 16, 18, 20, 22, 24, 26, 28, 36, 48, 72) } } \ No newline at end of file diff --git a/plugins/markdown/core/src/org/intellij/plugins/markdown/ui/actions/AdjustFontSizeAction.kt b/plugins/markdown/core/src/org/intellij/plugins/markdown/ui/actions/AdjustFontSizeAction.kt new file mode 100644 index 000000000000..300c016eb287 --- /dev/null +++ b/plugins/markdown/core/src/org/intellij/plugins/markdown/ui/actions/AdjustFontSizeAction.kt @@ -0,0 +1,94 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package org.intellij.plugins.markdown.ui.actions + +import com.intellij.icons.AllIcons +import com.intellij.openapi.actionSystem.ActionUpdateThread +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.components.service +import com.intellij.openapi.project.DumbAwareAction +import com.intellij.openapi.ui.popup.JBPopupFactory +import com.intellij.openapi.util.IconLoader +import com.intellij.ui.components.JBLabel +import com.intellij.util.ui.JBUI +import org.intellij.plugins.markdown.MarkdownBundle +import org.intellij.plugins.markdown.settings.MarkdownPreviewSettings +import org.intellij.plugins.markdown.settings.MarkdownSettingsConfigurable.Companion.fontSizeOptions +import org.intellij.plugins.markdown.ui.preview.MarkdownPreviewFileEditor +import org.intellij.plugins.markdown.ui.preview.MarkdownPreviewFileEditor.Companion.PREVIEW_POPUP_POINT +import org.intellij.plugins.markdown.ui.preview.jcef.MarkdownJCEFHtmlPanel +import java.awt.FlowLayout +import javax.swing.BorderFactory +import javax.swing.JButton +import javax.swing.JPanel +import javax.swing.SwingConstants + +class AdjustFontSizeAction: DumbAwareAction() { + private val previewSettings + get() = service() + + override fun actionPerformed(event: AnActionEvent) { + val editor = MarkdownActionUtil.findMarkdownPreviewEditor(event) + checkNotNull(editor) { "Preview editor should be obtainable from the action event" } + val preview = editor.getUserData(MarkdownPreviewFileEditor.PREVIEW_BROWSER)?.get() ?: return + if (preview !is MarkdownJCEFHtmlPanel) { + return + } + + val fontSizeLabel = JBLabel(previewSettings.state.fontSize.toString(), SwingConstants.CENTER).apply { + preferredSize = JBUI.size(22, 22) + } + val decreaseButton = JButton(AllIcons.General.Remove).apply { + border = BorderFactory.createEmptyBorder() + isContentAreaFilled = false + preferredSize = JBUI.size(22, 22) + + setDisabledIcon(IconLoader.getDisabledIcon(AllIcons.General.Remove)) + isEnabled = previewSettings.state.fontSize != fontSizeOptions.first() + } + val increaseButton = JButton(AllIcons.General.Add).apply { + border = BorderFactory.createEmptyBorder() + isContentAreaFilled = false + preferredSize = JBUI.size(22, 22) + + setDisabledIcon(IconLoader.getDisabledIcon(AllIcons.General.Add)) + isEnabled = previewSettings.state.fontSize != fontSizeOptions.last() + } + + fun updateFontSize(transform: (Int) -> Int?) { + val currentSize = preview.getCurrentFontSize() + val newSize = transform(currentSize) ?: currentSize + previewSettings.state.fontSize = newSize + preview.changeFontSize(newSize) + + fontSizeLabel.text = newSize.toString() + decreaseButton.isEnabled = newSize != fontSizeOptions.first() + increaseButton.isEnabled = newSize != fontSizeOptions.last() + } + + val hintComponent = JPanel(FlowLayout(FlowLayout.LEFT, 10, 5)) + hintComponent.add(JBLabel(MarkdownBundle.message("action.Markdown.Preview.FontSize.label.text"))) + hintComponent.add(decreaseButton.apply { addActionListener { updateFontSize { fontSizeOptions.findLast { step -> step < it } } } }) + hintComponent.add(fontSizeLabel.apply { text = preview.getCurrentFontSize().toString() }) + hintComponent.add(increaseButton.apply { addActionListener { updateFontSize { fontSizeOptions.find { step -> step > it } } } }) + + val popup = JBPopupFactory.getInstance().createComponentPopupBuilder(hintComponent, hintComponent).setRequestFocus(true).createPopup() + val point = event.dataContext.getData(PREVIEW_POPUP_POINT) + if (point != null) { + popup.show(point) + } else { + popup.showInFocusCenter() + } + } + + private fun MarkdownJCEFHtmlPanel.getCurrentFontSize() = getTemporaryFontSize() ?: previewSettings.state.fontSize + + override fun update(event: AnActionEvent) { + val editor = MarkdownActionUtil.findMarkdownPreviewEditor(event) + event.presentation.isEnabledAndVisible = editor != null + } + + override fun getActionUpdateThread(): ActionUpdateThread { + return ActionUpdateThread.EDT + } +} + diff --git a/plugins/markdown/core/src/org/intellij/plugins/markdown/ui/actions/ChangeFontSizeAction.kt b/plugins/markdown/core/src/org/intellij/plugins/markdown/ui/actions/ChangeFontSizeAction.kt index 92c7b7f27993..7dd9c2df5b24 100644 --- a/plugins/markdown/core/src/org/intellij/plugins/markdown/ui/actions/ChangeFontSizeAction.kt +++ b/plugins/markdown/core/src/org/intellij/plugins/markdown/ui/actions/ChangeFontSizeAction.kt @@ -2,23 +2,20 @@ package org.intellij.plugins.markdown.ui.actions import com.intellij.openapi.actionSystem.ActionUpdateThread import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.components.service import com.intellij.openapi.project.DumbAwareAction -import com.intellij.openapi.util.Key -import com.intellij.ui.jcef.JBCefApp +import com.intellij.openapi.ui.popup.Balloon +import com.intellij.ui.awt.RelativePoint import org.intellij.plugins.markdown.ui.preview.MarkdownPreviewFileEditor import org.intellij.plugins.markdown.ui.preview.PreviewLAFThemeStyles import org.intellij.plugins.markdown.ui.preview.jcef.MarkdownJCEFHtmlPanel -import org.intellij.plugins.markdown.ui.preview.jcef.impl.executeJavaScript - -private val FontSize = Key.create("Markdown.Preview.FontSize") +import org.intellij.plugins.markdown.ui.preview.jcef.zoomIndicator.PreviewZoomIndicatorManager internal sealed class ChangeFontSizeAction(private val transform: (Int) -> Int): DumbAwareAction() { class Increase: ChangeFontSizeAction(transform = { it + 1 }) class Decrease: ChangeFontSizeAction(transform = { (it - 1).coerceAtLeast(1) }) - class Reset: ChangeFontSizeAction(transform = { PreviewLAFThemeStyles.defaultFontSize }) - override fun actionPerformed(event: AnActionEvent) { val editor = MarkdownActionUtil.findMarkdownPreviewEditor(event) checkNotNull(editor) { "Preview editor should be obtainable from the action event" } @@ -26,10 +23,12 @@ internal sealed class ChangeFontSizeAction(private val transform: (Int) -> Int): if (preview !is MarkdownJCEFHtmlPanel) { return } - val currentSize = preview.getUserData(FontSize) ?: PreviewLAFThemeStyles.defaultFontSize + val currentSize = preview.getTemporaryFontSize() ?: PreviewLAFThemeStyles.defaultFontSize val newSize = transform(currentSize) - preview.putUserData(FontSize, newSize) - preview.changeFontSize(newSize) + preview.changeFontSize(newSize, temporary = true) + val project = event.project + val balloon = project?.service()?.createOrGetBalloon(preview) + balloon?.show(RelativePoint.getSouthOf(preview.component), Balloon.Position.below) } override fun update(event: AnActionEvent) { @@ -41,18 +40,3 @@ internal sealed class ChangeFontSizeAction(private val transform: (Int) -> Int): return ActionUpdateThread.EDT } } - -/** - * @param size Unscaled font size. - */ -internal fun MarkdownJCEFHtmlPanel.changeFontSize(size: Int) { - val scaled = JBCefApp.normalizeScaledSize(size) - // language=JavaScript - val code = """ - |(function() { - | const styles = document.querySelector(":root").style; - | styles.setProperty("${PreviewLAFThemeStyles.Variables.FontSize}", "${scaled}px"); - |})(); - """.trimMargin() - executeJavaScript(code) -} diff --git a/plugins/markdown/core/src/org/intellij/plugins/markdown/ui/actions/ResetFontSizeAction.kt b/plugins/markdown/core/src/org/intellij/plugins/markdown/ui/actions/ResetFontSizeAction.kt new file mode 100644 index 000000000000..315c383fb4e2 --- /dev/null +++ b/plugins/markdown/core/src/org/intellij/plugins/markdown/ui/actions/ResetFontSizeAction.kt @@ -0,0 +1,32 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package org.intellij.plugins.markdown.ui.actions + +import com.intellij.ide.IdeBundle +import com.intellij.openapi.actionSystem.ActionPlaces +import com.intellij.openapi.actionSystem.ActionUpdateThread +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.project.DumbAwareAction +import org.intellij.plugins.markdown.ui.preview.MarkdownPreviewFileEditor.Companion.PREVIEW_JCEF_PANEL +import org.intellij.plugins.markdown.ui.preview.PreviewLAFThemeStyles + +internal class ResetFontSizeAction: DumbAwareAction() { + override fun actionPerformed(event: AnActionEvent) { + val preview = event.getRequiredData(PREVIEW_JCEF_PANEL).get() ?: return + preview.changeFontSize(PreviewLAFThemeStyles.defaultFontSize, temporary = true) + } + + override fun update(event: AnActionEvent) { + if (event.place == ActionPlaces.POPUP) { + event.presentation.text = IdeBundle.message("action.reset.font.size", PreviewLAFThemeStyles.defaultFontSize) + val toReset = PreviewLAFThemeStyles.defaultFontSize + val preview = event.dataContext.getData(PREVIEW_JCEF_PANEL)?.get() ?: return + val currentSize = preview.getTemporaryFontSize() + event.presentation.setEnabled(currentSize != toReset) + } + } + + override fun getActionUpdateThread(): ActionUpdateThread { + return ActionUpdateThread.EDT + } +} + diff --git a/plugins/markdown/core/src/org/intellij/plugins/markdown/ui/preview/MarkdownPreviewFileEditor.kt b/plugins/markdown/core/src/org/intellij/plugins/markdown/ui/preview/MarkdownPreviewFileEditor.kt index ce6750ab600d..bb418c7ae845 100644 --- a/plugins/markdown/core/src/org/intellij/plugins/markdown/ui/preview/MarkdownPreviewFileEditor.kt +++ b/plugins/markdown/core/src/org/intellij/plugins/markdown/ui/preview/MarkdownPreviewFileEditor.kt @@ -2,6 +2,10 @@ package org.intellij.plugins.markdown.ui.preview import com.intellij.CommonBundle +import com.intellij.ide.DataManager +import com.intellij.openapi.actionSystem.DataKey +import com.intellij.openapi.actionSystem.ex.ActionUtil +import com.intellij.openapi.actionSystem.impl.SimpleDataContext import com.intellij.openapi.application.EDT import com.intellij.openapi.application.readAction import com.intellij.openapi.editor.Document @@ -14,13 +18,16 @@ import com.intellij.openapi.fileEditor.FileEditorState import com.intellij.openapi.fileEditor.TextEditorWithPreview import com.intellij.openapi.project.Project import com.intellij.openapi.ui.Messages +import com.intellij.openapi.ui.popup.JBPopupFactory import com.intellij.openapi.util.Disposer import com.intellij.openapi.util.Key import com.intellij.openapi.util.UserDataHolder import com.intellij.openapi.util.UserDataHolderBase import com.intellij.openapi.util.registry.Registry import com.intellij.openapi.vfs.VirtualFile +import com.intellij.ui.awt.RelativePoint import com.intellij.util.concurrency.annotations.RequiresEdt +import com.intellij.util.ui.StartupUiUtil import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.MutableStateFlow @@ -30,16 +37,21 @@ import org.intellij.plugins.markdown.MarkdownBundle import org.intellij.plugins.markdown.settings.MarkdownExtensionsSettings import org.intellij.plugins.markdown.settings.MarkdownSettings import org.intellij.plugins.markdown.ui.preview.html.MarkdownUtil.generateMarkdownHtml +import org.intellij.plugins.markdown.ui.preview.jcef.MarkdownJCEFHtmlPanel import org.intellij.plugins.markdown.util.MarkdownPluginScope import org.jetbrains.annotations.ApiStatus.Internal +import java.awt.AWTEvent import java.awt.BorderLayout +import java.awt.Point import java.awt.event.ComponentAdapter import java.awt.event.ComponentEvent +import java.awt.event.MouseEvent import java.beans.PropertyChangeListener import java.lang.ref.WeakReference import javax.swing.JComponent import javax.swing.JPanel + @Internal class MarkdownPreviewFileEditor( private val project: Project, @@ -78,6 +90,33 @@ class MarkdownPreviewFileEditor( } } }) + + StartupUiUtil.addAwtListener(AWTEvent.MOUSE_EVENT_MASK, this) { event -> + if (event is MouseEvent && event.id == MouseEvent.MOUSE_CLICKED && event.button == MouseEvent.BUTTON3 + && event.component.isShowing() && htmlPanelWrapper.isShowing() + && component.containsScreenLocation(event.locationOnScreen) + ) { + val context = SimpleDataContext.builder() + .setParent(DataManager.getInstance().getDataContext(event.component)) + .add(PREVIEW_POPUP_POINT, RelativePoint.fromScreen(event.locationOnScreen)) + .build() + val group = requireNotNull(ActionUtil.getActionGroup("Markdown.PreviewGroup")) + val popup = JBPopupFactory.getInstance().createActionGroupPopup( + null, + group, + context, + JBPopupFactory.ActionSelectionAid.MNEMONICS, + true + ) + popup.showInScreenCoordinates(event.component, event.locationOnScreen) + } + } + } + + private fun JComponent.containsScreenLocation(screenLocation: Point): Boolean { + val relativeX = screenLocation.x - locationOnScreen.x + val relativeY = screenLocation.y - locationOnScreen.y + return (relativeX >= 0 && relativeX < size.width && relativeY >= 0 && relativeY < size.height) } private suspend fun setupScrollHelper() { @@ -238,5 +277,8 @@ class MarkdownPreviewFileEditor( companion object { val PREVIEW_BROWSER: Key> = Key.create("PREVIEW_BROWSER") + + internal val PREVIEW_POPUP_POINT: DataKey = DataKey.create("PREVIEW_POPUP_POINT") + internal val PREVIEW_JCEF_PANEL: DataKey> = DataKey.create("PREVIEW_JCEF_PANEL") } } diff --git a/plugins/markdown/core/src/org/intellij/plugins/markdown/ui/preview/PreviewLAFThemeStyles.kt b/plugins/markdown/core/src/org/intellij/plugins/markdown/ui/preview/PreviewLAFThemeStyles.kt index c7692c0031e6..929d58eade59 100644 --- a/plugins/markdown/core/src/org/intellij/plugins/markdown/ui/preview/PreviewLAFThemeStyles.kt +++ b/plugins/markdown/core/src/org/intellij/plugins/markdown/ui/preview/PreviewLAFThemeStyles.kt @@ -1,6 +1,7 @@ // Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.intellij.plugins.markdown.ui.preview +import com.intellij.ide.ui.UISettings import com.intellij.openapi.components.service import com.intellij.openapi.editor.colors.EditorColorsManager import com.intellij.openapi.editor.colors.EditorColorsScheme @@ -19,6 +20,7 @@ internal object PreviewLAFThemeStyles { @Suppress("ConstPropertyName", "CssInvalidHtmlTagReference") object Variables { const val FontSize = "--default-font-size" + const val Scale = "--scale" } val defaultFontSize: Int @@ -43,15 +45,19 @@ internal object PreviewLAFThemeStyles { val markdownFenceBackground = JBColor(Color(212, 222, 231, 255 / 4), Color(212, 222, 231, 25)) val fontSize = JBCefApp.normalizeScaledSize(defaultFontSize) val backgroundColor = scheme.defaultBackground.webRgba() + val scale = service().currentIdeScale // language=CSS return """ :root { ${Variables.FontSize}: ${fontSize}px; + ${Variables.Scale}: ${scale}; } body { background-color: ${backgroundColor}; font-size: var(${Variables.FontSize}) !important; + transform: scale(var(${Variables.Scale})) !important; + transform-origin: 0 0; } body, p, blockquote, ul, ol, dl, table, pre, code, tr { diff --git a/plugins/markdown/core/src/org/intellij/plugins/markdown/ui/preview/jcef/MarkdownJCEFHtmlPanel.kt b/plugins/markdown/core/src/org/intellij/plugins/markdown/ui/preview/jcef/MarkdownJCEFHtmlPanel.kt index 6eb4880bdde0..3c3575f3b350 100644 --- a/plugins/markdown/core/src/org/intellij/plugins/markdown/ui/preview/jcef/MarkdownJCEFHtmlPanel.kt +++ b/plugins/markdown/core/src/org/intellij/plugins/markdown/ui/preview/jcef/MarkdownJCEFHtmlPanel.kt @@ -1,16 +1,23 @@ // Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.intellij.plugins.markdown.ui.preview.jcef +import com.intellij.ide.ui.UISettingsListener import com.intellij.openapi.application.readAction +import com.intellij.openapi.components.service import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.project.BaseProjectDirectories import com.intellij.openapi.project.Project +import com.intellij.openapi.ui.popup.Balloon import com.intellij.openapi.util.Disposer +import com.intellij.openapi.util.Key import com.intellij.openapi.util.UserDataHolder import com.intellij.openapi.util.UserDataHolderBase import com.intellij.openapi.util.registry.Registry import com.intellij.openapi.vfs.VirtualFile +import com.intellij.ui.awt.RelativePoint import com.intellij.ui.components.JBLoadingPanel +import com.intellij.ui.components.JBViewport +import com.intellij.ui.components.Magnificator import com.intellij.ui.jcef.JBCefApp import com.intellij.ui.jcef.JBCefClient import com.intellij.ui.jcef.JCEFHtmlPanel @@ -32,16 +39,19 @@ import org.intellij.markdown.html.HtmlGenerator import org.intellij.plugins.markdown.extensions.MarkdownBrowserPreviewExtension import org.intellij.plugins.markdown.extensions.MarkdownConfigurableExtension import org.intellij.plugins.markdown.settings.MarkdownPreviewSettings -import org.intellij.plugins.markdown.ui.actions.changeFontSize +import org.intellij.plugins.markdown.settings.MarkdownSettingsConfigurable.Companion.fontSizeOptions import org.intellij.plugins.markdown.ui.preview.* import org.intellij.plugins.markdown.ui.preview.jcef.impl.* +import org.intellij.plugins.markdown.ui.preview.jcef.zoomIndicator.PreviewZoomIndicatorManager import org.intellij.plugins.markdown.util.MarkdownApplicationScope import org.intellij.plugins.markdown.util.MarkdownPluginScope import org.jetbrains.annotations.ApiStatus import org.jetbrains.annotations.TestOnly import java.awt.BorderLayout +import java.awt.Point import java.net.URL import javax.swing.JComponent +import kotlin.math.round import kotlin.time.Duration.Companion.milliseconds class MarkdownJCEFHtmlPanel( @@ -120,7 +130,7 @@ class MarkdownJCEFHtmlPanel( data class Update( val content: String, val initialScrollOffset: Int, - val document: VirtualFile? + val document: VirtualFile?, ) : PreviewRequest data class ReloadWithOffset(val offset: Int) : PreviewRequest @@ -159,6 +169,17 @@ class MarkdownJCEFHtmlPanel( connection.subscribe(MarkdownPreviewSettings.ChangeListener.TOPIC, MarkdownPreviewSettings.ChangeListener { settings -> changeFontSize(settings.state.fontSize) }) + connection.subscribe(UISettingsListener.TOPIC, UISettingsListener { settings -> + val scale = settings.currentIdeScale + // language=JavaScript + val code = """ + |(function() { + | const styles = document.querySelector(":root").style; + | styles.setProperty("${PreviewLAFThemeStyles.Variables.Scale}", "${scale}"); + |})(); + """.trimMargin() + executeJavaScript(code) + }) coroutineScope.launch { val projectRoot = projectRoot.await() @@ -272,20 +293,68 @@ class MarkdownJCEFHtmlPanel( executeJavaScript("window.scrollController?.scrollBy($horizontal, $vertical)") } + private var previewInnerComponent: JComponent? = null + private val TEMPORARY_FONT_SIZE = Key.create("Markdown.Preview.FontSize") + private fun createComponent(): JComponent { - val component = super.getComponent() - if (project == null || virtualFile == null) return component + previewInnerComponent = super.getComponent() + if (project == null || virtualFile == null) return previewInnerComponent!! val panel = JBLoadingPanel(BorderLayout(), this) + + previewInnerComponent!!.putClientProperty(Magnificator.CLIENT_PROPERTY_KEY, object: Magnificator { + override fun magnify(scale: Double, at: Point): Point { + val currentSize = this@MarkdownJCEFHtmlPanel.getTemporaryFontSize() ?: PreviewLAFThemeStyles.defaultFontSize + var fontSize = round(currentSize * scale).toInt() + fontSize = maxOf(fontSize, fontSizeOptions.first()) + fontSize = minOf(fontSize, fontSizeOptions.last()) + changeFontSize(fontSize, temporary = true) + + return at + } + }) + + previewInnerComponent!!.addPropertyChangeListener(TEMPORARY_FONT_SIZE.toString()) { + val balloon = project.service().createOrGetBalloon(this@MarkdownJCEFHtmlPanel) + balloon?.show(RelativePoint.getSouthOf(previewInnerComponent!!), Balloon.Position.below) + } + coroutineScope.async(context = Dispatchers.Default, start = CoroutineStart.UNDISPATCHED) { panel.startLoading() - panel.add(component) + val viewPort = JBViewport() + viewPort.add(previewInnerComponent) + panel.add(viewPort) projectRoot.await() panel.stopLoading() } return panel } + fun getTemporaryFontSize() = getUserData(TEMPORARY_FONT_SIZE) + + /** + * @param size Unscaled font size. + */ + internal fun changeFontSize(size: Int, temporary: Boolean = false) { + if (temporary) { + val previousSize = getUserData(TEMPORARY_FONT_SIZE) ?: PreviewLAFThemeStyles.defaultFontSize + putUserData(TEMPORARY_FONT_SIZE, size) + previewInnerComponent?.firePropertyChange(TEMPORARY_FONT_SIZE.toString(), previousSize, size) + } else { + putUserData(TEMPORARY_FONT_SIZE, null) + } + + val scaled = JBCefApp.normalizeScaledSize(size) + // language=JavaScript + val code = """ + |(function() { + | const styles = document.querySelector(":root").style; + | styles.setProperty("${PreviewLAFThemeStyles.Variables.FontSize}", "${scaled}px"); + |})(); + """.trimMargin() + executeJavaScript(code) + } + private fun createFileSchemeResourcesProcessor(projectRoot: VirtualFile?): ResourceProvider? { if (projectRoot == null) return null diff --git a/plugins/markdown/core/src/org/intellij/plugins/markdown/ui/preview/jcef/zoomIndicator/PreviewZoomIndicatorManager.kt b/plugins/markdown/core/src/org/intellij/plugins/markdown/ui/preview/jcef/zoomIndicator/PreviewZoomIndicatorManager.kt new file mode 100644 index 000000000000..64dcaecb1a60 --- /dev/null +++ b/plugins/markdown/core/src/org/intellij/plugins/markdown/ui/preview/jcef/zoomIndicator/PreviewZoomIndicatorManager.kt @@ -0,0 +1,92 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package org.intellij.plugins.markdown.ui.preview.jcef.zoomIndicator + +import com.intellij.openapi.actionSystem.ActionPlaces +import com.intellij.openapi.actionSystem.AnAction +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.actionSystem.AnActionResult +import com.intellij.openapi.actionSystem.ex.AnActionListener +import com.intellij.openapi.application.EDT +import com.intellij.openapi.components.Service +import com.intellij.openapi.project.Project +import com.intellij.openapi.ui.popup.Balloon +import com.intellij.openapi.ui.popup.JBPopupFactory +import com.intellij.ui.BalloonImpl +import com.intellij.ui.ExperimentalUI +import com.intellij.ui.JBColor +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.channels.BufferOverflow +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.launch +import org.intellij.plugins.markdown.ui.preview.jcef.MarkdownJCEFHtmlPanel +import org.intellij.plugins.markdown.util.MarkdownPluginScope +import java.lang.ref.WeakReference +import kotlin.time.Duration.Companion.seconds + +@Service(Service.Level.PROJECT) +class PreviewZoomIndicatorManager(project: Project) { + private val coroutineScope = MarkdownPluginScope.createChildScope(project) + + private val cancelBalloonRequests = MutableSharedFlow(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST) + + private var balloon: Balloon? = null + private var preview: WeakReference = WeakReference(null) + + init { + project.messageBus.connect().subscribe(AnActionListener.TOPIC, object : AnActionListener { + override fun afterActionPerformed(action: AnAction, event: AnActionEvent, result: AnActionResult) { + if (event.place == ActionPlaces.POPUP && action is PreviewZoomIndicatorView.SettingsAction) { + cancelCurrentPopup() + } + } + }) + + coroutineScope.launch(context = Dispatchers.EDT) { + cancelBalloonRequests.collectLatest { + val b = balloon ?: return@collectLatest + b.getView().isHovered().collectLatest { isHovered -> + if (!isHovered) { + delay(5.seconds) + cancelCurrentPopup() + } else { + check(cancelBalloonRequests.tryEmit(Unit)) + } + } + } + } + } + + fun createOrGetBalloon(preview: MarkdownJCEFHtmlPanel): Balloon? { + val view = PreviewZoomIndicatorView(preview) + val b = balloon + if (this.preview.refersTo(preview) && (b as? BalloonImpl)?.isVisible == true) { + b.getView().updateFontSize() + return null + } + cancelCurrentPopup() + val newUI = ExperimentalUI.isNewUI() + val b2 = JBPopupFactory.getInstance().createBalloonBuilder(view) + .setRequestFocus(false) + .setShadow(true) + .setFillColor(if (newUI) JBColor.namedColor("Toolbar.Floating.background", JBColor(0xEDEDED, 0x454A4D)) else view.background) + .setBorderColor(if (newUI) JBColor.namedColor("Toolbar.Floating.borderColor", JBColor(0xEBECF0, 0x43454A)) else JBColor.border()) + .setShowCallout(false) + .setFadeoutTime(0) + .setHideOnKeyOutside(false) + .createBalloon().apply { setAnimationEnabled(false) } + balloon = b2 + check(cancelBalloonRequests.tryEmit(Unit)) + this.preview = WeakReference(preview) + return b2 + } + + fun cancelCurrentPopup() { + balloon?.hide() + balloon = null + preview.clear() + } + + private fun Balloon.getView() = ((this as BalloonImpl).content as PreviewZoomIndicatorView) +} \ No newline at end of file diff --git a/plugins/markdown/core/src/org/intellij/plugins/markdown/ui/preview/jcef/zoomIndicator/PreviewZoomIndicatorView.kt b/plugins/markdown/core/src/org/intellij/plugins/markdown/ui/preview/jcef/zoomIndicator/PreviewZoomIndicatorView.kt new file mode 100644 index 000000000000..75844382978c --- /dev/null +++ b/plugins/markdown/core/src/org/intellij/plugins/markdown/ui/preview/jcef/zoomIndicator/PreviewZoomIndicatorView.kt @@ -0,0 +1,109 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package org.intellij.plugins.markdown.ui.preview.jcef.zoomIndicator + +import com.intellij.icons.AllIcons +import com.intellij.ide.IdeBundle +import com.intellij.ide.actions.ShowSettingsUtilImpl +import com.intellij.openapi.actionSystem.* +import com.intellij.openapi.actionSystem.ex.ActionUtil +import com.intellij.openapi.actionSystem.impl.ActionButton +import com.intellij.openapi.actionSystem.impl.SimpleDataContext +import com.intellij.openapi.project.DumbAwareAction +import com.intellij.ui.components.AnActionLink +import com.intellij.util.ui.JBUI +import kotlinx.coroutines.channels.BufferOverflow +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableSharedFlow +import net.miginfocom.swing.MigLayout +import org.intellij.plugins.markdown.MarkdownBundle +import org.intellij.plugins.markdown.ui.preview.MarkdownPreviewFileEditor.Companion.PREVIEW_JCEF_PANEL +import org.intellij.plugins.markdown.ui.preview.jcef.MarkdownJCEFHtmlPanel +import java.awt.event.MouseAdapter +import java.awt.event.MouseEvent +import java.lang.ref.WeakReference +import javax.swing.JLabel +import javax.swing.JPanel + +class PreviewZoomIndicatorView(private val preview: MarkdownJCEFHtmlPanel) : JPanel(MigLayout("novisualpadding, ins 0")) { + private val isHoveredFlow = MutableSharedFlow(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST) + + private val fontSizeLabel = JLabel(IdeBundle.message("action.reset.font.size.info", "000")) + + private val settingsButton = SettingsButton(SettingsAction()) + + internal class SettingsAction: DumbAwareAction(IdeBundle.message("action.open.editor.settings.text"), "", AllIcons.General.Settings) { + override fun actionPerformed(e: AnActionEvent) { + val prj = e.project ?: return + val searchName = MarkdownBundle.message("markdown.settings.preview.font.size") + ShowSettingsUtilImpl.showSettingsDialog(prj, "Settings.Markdown", searchName) + } + } + + internal class SettingsButton(settingsAction: SettingsAction) : ActionButton(settingsAction, settingsAction.templatePresentation.clone(), ActionPlaces.POPUP, JBUI.size(22, 22)) { + override fun performAction(e: MouseEvent?) { + val event = AnActionEvent.createFromInputEvent(e, myPlace, myPresentation, dataContext, false, true) + ActionUtil.performDumbAwareWithCallbacks(myAction, event) { actionPerformed(event) } + } + override fun isShowing() = true + } + + private inner class PatchedActionLink(action: AnAction, event: AnActionEvent) : AnActionLink(action, ActionPlaces.POPUP) { + init { + text = event.presentation.text + autoHideOnDisable = false + isEnabled = event.presentation.isEnabled + event.presentation.addPropertyChangeListener { + if (it.propertyName == Presentation.PROP_ENABLED) { + isEnabled = it.newValue as Boolean + } + } + } + + override fun uiDataSnapshot(sink: DataSink) { + super.uiDataSnapshot(sink) + sink[PREVIEW_JCEF_PANEL] = WeakReference(preview) + } + override fun isShowing() = true + } + + private val resetLink = ActionManager.getInstance().getAction("Markdown.Preview.ResetFontSize").run { + val dataContext = SimpleDataContext.builder().add(PREVIEW_JCEF_PANEL, WeakReference(preview)).build() + val event = AnActionEvent.createFromInputEvent(null, ActionPlaces.POPUP, + null, dataContext, + false, true) + update(event) + fontSizeLabel.addPropertyChangeListener { + if (it.propertyName == "text") { + update(event) + } + } + + PatchedActionLink(this, event) + } + + init { + check(isHoveredFlow.tryEmit(false)) + + val mouseListener = object : MouseAdapter() { + override fun mouseEntered(e: MouseEvent?) { check(isHoveredFlow.tryEmit(true)) } + override fun mouseExited(e: MouseEvent?) { check(isHoveredFlow.tryEmit(false)) } + } + addMouseListener(mouseListener) + resetLink.addMouseListener(mouseListener) + settingsButton.addMouseListener(mouseListener) + + updateFontSize() + + add(fontSizeLabel, "wmin 100, gapbottom 1, gapleft 3") + add(resetLink, "gapbottom 1") + add(settingsButton) + } + + fun updateFontSize() { + fontSizeLabel.text = IdeBundle.message("action.reset.font.size.info", preview.getTemporaryFontSize()) + } + + fun isHovered(): Flow { + return isHoveredFlow + } +} \ No newline at end of file diff --git a/resources-en/resources/search/searchableOptions.xml b/resources-en/resources/search/searchableOptions.xml index 7f062eea3d96..ffc67c222144 100644 --- a/resources-en/resources/search/searchableOptions.xml +++ b/resources-en/resources/search/searchableOptions.xml @@ -35614,6 +35614,12 @@