Skip to content

Instantly share code, notes, and snippets.

@mallowigi
Created March 25, 2025 10:32
Show Gist options
  • Save mallowigi/d89857c5e500030c5b8d665fa6fde40d to your computer and use it in GitHub Desktop.
Save mallowigi/d89857c5e500030c5b8d665fa6fde40d to your computer and use it in GitHub Desktop.
/**
* ****************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2015-2024 Elior "Mallowigi" Boukhobza
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
* ****************************************************************************
*/
package com.mallowigi.idea.projectframe.components
import com.intellij.openapi.actionSystem.impl.ActionButton
import com.intellij.openapi.project.Project
import com.intellij.util.ui.UIUtil
import com.mallowigi.idea.config.application.newconfig.main.MTThemeConfigState
import com.mallowigi.idea.projectframe.MTTitleFrame
import com.mallowigi.idea.utils.MTUtils
import java.awt.Color
import java.awt.Component
import java.awt.Container
import java.awt.event.ContainerEvent
import java.awt.event.ContainerListener
import java.beans.PropertyChangeEvent
abstract class MTProjectFrameComponent(open val project: Project) {
/** List of components whose color is to be locked. */
private val lockedComponentMap: MutableMap<Component, Boolean> = mutableMapOf()
private val projectColor: Color
get() = MTUtils.getProjectColor(project)
private val textColor: Color
get() = MTThemeConfigState.instance.selectedTheme.theme.selectionForegroundColor
open val fallbackBackground: Color
get() = UIUtil.getPanelBackground()
open val fallbackForeground: Color
get() = UIUtil.getLabelForeground()
/** Is colorization enabled. */
abstract val isEnabled: Boolean
/** Component path. */
abstract val componentPath: List<String>
/** Apply color. */
open fun apply() {
lockedComponentMap.clear()
findComponent()?.let {
lockComponentBackgroundColor(it)
if (isEnabled) {
it.background = projectColor
it.foreground = textColor
} else {
it.background = fallbackBackground
it.foreground = fallbackForeground
}
}
}
/** Finds the component of the frame. */
private fun findComponent(): Container? {
val path = componentPath
val root = MTUtils.getRoot(project) ?: return null
return MTUtils.findPath(root as Container, path)
}
/**
* Disable any unintended changes to given component of a project from
* external sources.
*
* @note registers a PropertyChangeListener for given property name, which
* `aggressively` reverts the color back to the one set by this plugin.
*/
private fun lockComponentBackgroundColor(component: Component) {
if (!lockedComponentMap.containsKey(component)) {
lockedComponentMap[component] = false
}
if (lockedComponentMap[component] == false) {
addBackgroundChangeListener(component)
addContainerListener(component as Container)
lockedComponentMap[component] = true
}
}
/**
* Add background property change listener to replace the color
* automatically.
*/
private fun addBackgroundChangeListener(component: Component) {
val listener: (evt: PropertyChangeEvent) -> Unit = { setColor(component as Container) }
component.removePropertyChangeListener(MTTitleFrame.BACKGROUND, listener)
component.removePropertyChangeListener(MTTitleFrame.FOREGROUND, listener)
component.addPropertyChangeListener(MTTitleFrame.BACKGROUND, listener)
component.addPropertyChangeListener(MTTitleFrame.FOREGROUND, listener)
}
private fun setColor(component: Container) {
component.background = if (isEnabled) projectColor else fallbackBackground
component.foreground = if (isEnabled) textColor else fallbackForeground
for (comp in component.components) {
if (comp !is ActionButton) setColor(comp as Container)
}
}
private fun addContainerListener(component: Container) {
val listener = object : ContainerListener {
override fun componentAdded(e: ContainerEvent) = setColor(e.component as Container)
override fun componentRemoved(e: ContainerEvent) = setColor(e.component as Container)
}
component.removeContainerListener(listener)
component.addContainerListener(listener)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment