1
0
mirror of https://github.com/chylex/IntelliJ-IdeaVim.git synced 2025-02-26 05:46:00 +01:00

Better widget

This commit is contained in:
Filipp Vakhitov 2024-01-13 23:01:01 +02:00
parent c12082affc
commit 52cf10cb2e
6 changed files with 119 additions and 85 deletions

View File

@ -94,10 +94,10 @@ import com.maddyhome.idea.vim.newapi.vim
import com.maddyhome.idea.vim.state.mode.inSelectMode
import com.maddyhome.idea.vim.state.mode.mode
import com.maddyhome.idea.vim.state.mode.selectionType
import com.maddyhome.idea.vim.ui.widgets.macro.MacroWidgetListener
import com.maddyhome.idea.vim.ui.ShowCmdOptionChangeListener
import com.maddyhome.idea.vim.ui.widgets.mode.ModeWidgetListener
import com.maddyhome.idea.vim.ui.ex.ExEntryPanel
import com.maddyhome.idea.vim.ui.widgets.macro.macroWidgetOptionListener
import com.maddyhome.idea.vim.ui.widgets.mode.modeWidgetOptionListener
import com.maddyhome.idea.vim.vimDisposable
import java.awt.event.MouseAdapter
import java.awt.event.MouseEvent
@ -160,10 +160,10 @@ internal object VimListenerManager {
optionGroup.addGlobalOptionChangeListener(Options.showcmd, ShowCmdOptionChangeListener)
// This code is executed after ideavimrc execution, so we trigger onGlobalOptionChanged just in case
optionGroup.addGlobalOptionChangeListener(IjOptions.showmodewidget, ModeWidgetListener)
optionGroup.addGlobalOptionChangeListener(IjOptions.showmodewidget, MacroWidgetListener)
ModeWidgetListener.onGlobalOptionChanged()
MacroWidgetListener.onGlobalOptionChanged()
optionGroup.addGlobalOptionChangeListener(IjOptions.showmodewidget, modeWidgetOptionListener)
optionGroup.addGlobalOptionChangeListener(IjOptions.showmodewidget, macroWidgetOptionListener)
modeWidgetOptionListener.onGlobalOptionChanged()
macroWidgetOptionListener.onGlobalOptionChanged()
optionGroup.addEffectiveOptionValueChangeListener(Options.guicursor, GuicursorChangeListener)
@ -184,8 +184,8 @@ internal object VimListenerManager {
optionGroup.removeEffectiveOptionValueChangeListener(Options.relativenumber, EditorGroup.NumberChangeListener.INSTANCE)
optionGroup.removeEffectiveOptionValueChangeListener(Options.scrolloff, ScrollGroup.ScrollOptionsChangeListener)
optionGroup.removeGlobalOptionChangeListener(Options.showcmd, ShowCmdOptionChangeListener)
optionGroup.removeGlobalOptionChangeListener(IjOptions.showmodewidget, ModeWidgetListener)
optionGroup.removeGlobalOptionChangeListener(IjOptions.showmodewidget, MacroWidgetListener)
optionGroup.removeGlobalOptionChangeListener(IjOptions.showmodewidget, modeWidgetOptionListener)
optionGroup.removeGlobalOptionChangeListener(IjOptions.showmodewidget, macroWidgetOptionListener)
optionGroup.removeEffectiveOptionValueChangeListener(Options.guicursor, GuicursorChangeListener)
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright 2003-2024 The IdeaVim authors
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE.txt file or at
* https://opensource.org/licenses/MIT.
*/
package com.maddyhome.idea.vim.ui.widgets
import com.intellij.openapi.components.service
import com.intellij.openapi.project.ProjectManager
import com.intellij.openapi.wm.StatusBarWidgetFactory
import com.intellij.openapi.wm.impl.status.widget.StatusBarWidgetsManager
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.common.VimPluginListener
import com.maddyhome.idea.vim.options.GlobalOptionChangeListener
public class VimWidgetListener(private val widgetFactory: Class<out StatusBarWidgetFactory>) : GlobalOptionChangeListener, VimPluginListener {
init {
injector.listenersNotifier.vimPluginListeners.add(this)
}
override fun onGlobalOptionChanged() {
updateWidget()
}
override fun turnedOn() {
updateWidget()
}
override fun turnedOff() {
updateWidget()
}
private fun updateWidget() {
val factory = StatusBarWidgetFactory.EP_NAME.findExtension(widgetFactory) ?: return
for (project in ProjectManager.getInstance().openProjects) {
val statusBarWidgetsManager = project.service<StatusBarWidgetsManager>()
statusBarWidgetsManager.updateWidget(factory)
}
}
}

View File

@ -8,36 +8,33 @@
package com.maddyhome.idea.vim.ui.widgets.macro
import com.intellij.openapi.components.service
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.ProjectManager
import com.intellij.openapi.wm.StatusBarWidget
import com.intellij.openapi.wm.StatusBarWidgetFactory
import com.intellij.openapi.wm.WindowManager
import com.intellij.openapi.wm.impl.status.widget.StatusBarWidgetsManager
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.common.MacroRecordingListener
import com.maddyhome.idea.vim.common.VimPluginListener
import com.maddyhome.idea.vim.newapi.globalIjOptions
import com.maddyhome.idea.vim.options.GlobalOptionChangeListener
import com.maddyhome.idea.vim.newapi.ij
import com.maddyhome.idea.vim.ui.widgets.VimWidgetListener
import com.maddyhome.idea.vim.ui.widgets.mode.VimStatusBarWidget
import java.awt.Component
private const val ID = "IdeaVim::Macro"
internal class MacroWidgetFactory : StatusBarWidgetFactory {
internal class MacroWidgetFactory : StatusBarWidgetFactory, VimStatusBarWidget {
private var content: String = ""
private val macroRecordingListener = object : MacroRecordingListener {
override fun recordingStarted(editor: VimEditor, register: Char) {
content = "recording @$register"
updateWidget()
updateWidgetInStatusBar(ID, editor.ij.project)
}
override fun recordingFinished(editor: VimEditor, register: Char) {
content = ""
updateWidget()
updateWidgetInStatusBar(ID, editor.ij.project)
}
}
@ -58,14 +55,6 @@ internal class MacroWidgetFactory : StatusBarWidgetFactory {
return VimPlugin.isEnabled() && injector.globalIjOptions().showmodewidget
}
private fun updateWidget() {
val windowManager = WindowManager.getInstance()
ProjectManager.getInstance().openProjects.forEach {
val statusBar = windowManager.getStatusBar(it)
statusBar.updateWidget(ID)
}
}
private inner class VimMacroWidget : StatusBarWidget {
override fun ID(): String {
return ID
@ -91,28 +80,4 @@ internal class MacroWidgetFactory : StatusBarWidgetFactory {
}
}
internal object MacroWidgetListener : GlobalOptionChangeListener, VimPluginListener {
init {
injector.listenersNotifier.vimPluginListeners.add(this)
}
override fun onGlobalOptionChanged() {
updateWidget()
}
override fun turnedOn() {
updateWidget()
}
override fun turnedOff() {
updateWidget()
}
private fun updateWidget() {
val factory = StatusBarWidgetFactory.EP_NAME.findExtension(MacroWidgetFactory::class.java) ?: return
for (project in ProjectManager.getInstance().openProjects) {
val statusBarWidgetsManager = project.service<StatusBarWidgetsManager>()
statusBarWidgetsManager.updateWidget(factory)
}
}
}
public val macroWidgetOptionListener: VimWidgetListener = VimWidgetListener(MacroWidgetFactory::class.java)

View File

@ -8,17 +8,13 @@
package com.maddyhome.idea.vim.ui.widgets.mode
import com.intellij.openapi.components.service
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.ProjectManager
import com.intellij.openapi.wm.StatusBarWidget
import com.intellij.openapi.wm.StatusBarWidgetFactory
import com.intellij.openapi.wm.impl.status.widget.StatusBarWidgetsManager
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.common.VimPluginListener
import com.maddyhome.idea.vim.newapi.globalIjOptions
import com.maddyhome.idea.vim.options.GlobalOptionChangeListener
import com.maddyhome.idea.vim.ui.widgets.VimWidgetListener
public class ModeWidgetFactory : StatusBarWidgetFactory {
public companion object {
@ -42,28 +38,4 @@ public class ModeWidgetFactory : StatusBarWidgetFactory {
}
}
internal object ModeWidgetListener : GlobalOptionChangeListener, VimPluginListener {
init {
injector.listenersNotifier.vimPluginListeners.add(this)
}
override fun onGlobalOptionChanged() {
updateWidget()
}
override fun turnedOn() {
updateWidget()
}
override fun turnedOff() {
updateWidget()
}
private fun updateWidget() {
val factory = StatusBarWidgetFactory.EP_NAME.findExtension(ModeWidgetFactory::class.java) ?: return
for (project in ProjectManager.getInstance().openProjects) {
val statusBarWidgetsManager = project.service<StatusBarWidgetsManager>()
statusBarWidgetsManager.updateWidget(factory)
}
}
}
public val modeWidgetOptionListener: VimWidgetListener = VimWidgetListener(ModeWidgetFactory::class.java)

View File

@ -12,7 +12,6 @@ import com.intellij.openapi.editor.Editor
import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.wm.CustomStatusBarWidget
import com.intellij.openapi.wm.WindowManager
import com.intellij.ui.components.JBLabel
import com.intellij.ui.util.width
import com.intellij.util.ui.UIUtil
@ -29,7 +28,7 @@ import java.awt.Dimension
import javax.swing.JComponent
import kotlin.math.max
public class VimModeWidget(public val project: Project) : CustomStatusBarWidget {
public class VimModeWidget(public val project: Project) : CustomStatusBarWidget, VimStatusBarWidget {
private companion object {
private const val INSERT = "INSERT"
private const val NORMAL = "NORMAL"
@ -72,9 +71,7 @@ public class VimModeWidget(public val project: Project) : CustomStatusBarWidget
public fun updateWidget(mode: Mode?) {
updateLabel(mode)
val windowManager = WindowManager.getInstance()
val statusBar = windowManager.getStatusBar(project)
statusBar.updateWidget(ModeWidgetFactory.ID)
updateWidgetInStatusBar(ModeWidgetFactory.ID, project)
}
private fun updateLabel(mode: Mode?) {

View File

@ -0,0 +1,57 @@
/*
* Copyright 2003-2024 The IdeaVim authors
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE.txt file or at
* https://opensource.org/licenses/MIT.
*/
package com.maddyhome.idea.vim.ui.widgets.mode
import com.intellij.openapi.project.Project
import com.intellij.openapi.wm.WindowManager
import java.util.*
public interface VimStatusBarWidget {
public fun updateWidgetInStatusBar(widgetID: String, project: Project?) {
if (project == null) return
val updateWidgetTask = TimerWithRetriesTask(500L, 50) {
val windowManager = WindowManager.getInstance()
val statusBar = windowManager.getStatusBar(project) ?: return@TimerWithRetriesTask
statusBar.updateWidget(widgetID)
}
updateWidgetTask.execute()
}
}
/**
* A task that may be used to address issues with initialization in the Platform, executing code with a reasonable number of retries and a reasonable period.
* Clearly, this is a workaround and its use should be avoided when possible.
*
* Why is it needed for widgets?
* In a single project environment, it is not necessary since the status bar is initialized before the editor opens.
* However, in multi-project setups, the editor window is opened before the status bar initialization.
* And this tasks tries to loops until status bar creation in order to notify it about opened editor.
*/
private class TimerWithRetriesTask(
private val period: Long,
private val retriesLimit: Int,
private val block: () -> Unit
) {
private val timer = Timer()
fun execute() {
timer.schedule(object : TimerTask() {
private var counter = 0
override fun run() {
if (counter >= retriesLimit) {
timer.cancel()
} else {
this@TimerWithRetriesTask.block()
counter++
}
}
}, 0L, period)
}
}