Borderless: ellipsis in window title when moving between displays with different DPI (#IDEA-219715)

GitOrigin-RevId: c094363457cfaaed9066ebb0352e8cb92f2b54fc
This commit is contained in:
graann
2019-08-12 17:03:47 +03:00
committed by intellij-monorepo-bot
parent a0b20ad307
commit 172ef146dc
6 changed files with 98 additions and 278 deletions
@@ -2,6 +2,7 @@
package com.intellij.openapi.wm.impl.customFrameDecorations.header.titleLabel
import sun.swing.SwingUtilities2
import java.awt.FontMetrics
import java.io.File
import javax.swing.JComponent
@@ -43,27 +44,23 @@ class ClippingTitle(prefix: String = " - ", suffix: String = "") : DefaultPartTi
if (value == longText) return
super.longText = value
val shtt = if(value.isEmpty()) "" else value.substringAfterLast(fileSeparatorChar)
if(shtt != longText && !shtt.isEmpty()) shortText = "$ellipsisSymbol$fileSeparatorChar$shtt"
if(shtt != longText && shtt.isNotEmpty()) shortText = "$ellipsisSymbol$fileSeparatorChar$shtt"
}
override fun shrink(maxWidth: Int): Int {
val fm = label.getFontMetrics(label.font)
override fun shrink(label: JComponent, fm: FontMetrics, maxWidth: Int): String {
val prefixWidth = SwingUtilities2.stringWidth(label, fm, prefix)
val suffixWidth = SwingUtilities2.stringWidth(label, fm, suffix)
return when {
maxWidth > longWidth -> {
showLong()
longWidth
getLong()
}
longWidth > maxWidth - prefixWidth - suffixWidth -> {
val clipString = clipString(label, longText, maxWidth - prefixWidth - suffixWidth, fileSeparatorChar)
label.text = if (clipString.isEmpty()) "" else "$prefix$clipString$suffix"
SwingUtilities2.stringWidth(label, fm, longText)
return if (clipString.isEmpty()) "" else "$prefix$clipString$suffix"
}
else -> {
showShort()
return shortWidth
return getShort()
}
}
}
@@ -18,13 +18,11 @@ class CustomDecorationPath(val frame: JFrame, onBoundsChanged: () -> Unit) : Sel
fun setActive(value: Boolean) {
val color = if (value) CustomFrameDecorations.titlePaneInfoForeground() else CustomFrameDecorations.titlePaneInactiveInfoForeground()
components.forEach {
it.component.foreground = color
}
getView().foreground = color
}
fun getListenerBounds(): List<RelativeRectangle> {
return if (!isClipped()) {
return if (!isClipped) {
emptyList()
}
else {
@@ -2,27 +2,16 @@
package com.intellij.openapi.wm.impl.customFrameDecorations.header.titleLabel
import sun.swing.SwingUtilities2
import java.awt.Insets
import java.awt.FontMetrics
import javax.swing.JComponent
import javax.swing.JLabel
open class DefaultPartTitle(open var prefix: String = " ", open var suffix: String = ""): BaseTitlePart {
protected open val label = TitleLabel()
private var shortTextWidth: Int = 0
private var longTextWidth: Int = 0
protected var state = TitlePart.State.LONG
override var active: Boolean = true
set(value) {
field = value
if(!value) {
label.text = ""
}
}
override var longText: String = ""
set(value) {
@@ -33,35 +22,14 @@ open class DefaultPartTitle(open var prefix: String = " ", open var suffix: Stri
override var shortText: String = ""
override val component: JComponent
get() = label
override fun hide() {
if(!active) return
state = TitlePart.State.HIDE
label.text = ""
override fun getLong(): String {
return if (!active || longText.isEmpty()) "" else "$prefix$longText$suffix"
}
override val isClipped: Boolean
get() = state != TitlePart.State.LONG || !active
override fun showLong() {
if(!active) return
label.text = if (longText.isEmpty()) "" else "$prefix$longText$suffix"
state = TitlePart.State.LONG
override fun getShort(): String {
return if (!active || shortText.isEmpty()) "" else "$prefix$shortText$suffix"
}
override fun showShort() {
if(!active) return
label.text = if (shortText.isEmpty()) "" else "$prefix$shortText$suffix"
state = TitlePart.State.SHORT
}
fun currentState(): TitlePart.State = if(label.text.isEmpty()) TitlePart.State.HIDE else state
override val longWidth: Int get() = longTextWidth
override val shortWidth: Int get() = shortTextWidth
@@ -69,19 +37,8 @@ open class DefaultPartTitle(open var prefix: String = " ", open var suffix: Stri
override val toolTipPart: String
get() = if (longText.isEmpty()) "" else "$prefix$longText$suffix"
override fun setToolTip(value: String?) {
label.toolTipText = value
}
override fun refresh() {
val fm = label.getFontMetrics(label.font)
override fun refresh(label: JComponent, fm: FontMetrics) {
longTextWidth = if (longText.isEmpty() || !active) 0 else SwingUtilities2.stringWidth(label, fm, "$prefix$longText$suffix")
shortTextWidth = if (shortText.isEmpty() || !active) 0 else SwingUtilities2.stringWidth(label, fm, "$prefix$shortText$suffix")
}
open class TitleLabel : JLabel() {
override fun getInsets(): Insets {
return Insets(0, 0, 0, 0)
}
}
}
@@ -1,22 +1,16 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.openapi.wm.impl.customFrameDecorations.header.titleLabel
import net.miginfocom.swing.MigLayout
import sun.swing.SwingUtilities2
import java.awt.Color
import java.awt.Font
import java.awt.FontMetrics
import java.io.File
import javax.swing.JComponent
import javax.swing.JPanel
import javax.swing.plaf.FontUIResource
class ProjectTitlePane : ShrinkingTitlePart {
private val unparsed = object : DefaultPartTitle() {
override val label: TitleLabel = BoldTitleLabel()
}
private val unparsed = DefaultPartTitle()
private val projectTitle = ProjectTitle()
var parsed = false
override var active: Boolean
get() = projectTitle.active
@@ -24,28 +18,6 @@ class ProjectTitlePane : ShrinkingTitlePart {
projectTitle.active = value
}
private val pane = object : JPanel(MigLayout("ins 0, gap 0, hidemode 3", "[pref][pref]")){
override fun setForeground(fg: Color?) {
super.setForeground(fg)
projectTitle.component.foreground = fg
unparsed.component.foreground = fg
}
}.apply {
add(unparsed.component)
add(projectTitle.component)
isOpaque = false
}
override fun setToolTip(value: String?) {
unparsed.setToolTip(value)
projectTitle.setToolTip(value)
}
override val component: JComponent
get() = pane
private var state = TitlePart.State.LONG
fun setProject(lng: String, short: String) {
val long = if (lng.length > short.length) lng else short
@@ -75,10 +47,6 @@ class ProjectTitlePane : ShrinkingTitlePart {
}
else false
} ?: false
unparsed.component.isVisible = !parsed
projectTitle.component.isVisible = parsed
}
override val longWidth: Int
@@ -87,49 +55,30 @@ class ProjectTitlePane : ShrinkingTitlePart {
get() = if (parsed) projectTitle.shortWidth else unparsed.shortWidth
override val toolTipPart: String
get() = unparsed.toolTipPart
override val isClipped: Boolean
get() = if (parsed) projectTitle.isClipped else unparsed.isClipped
override fun hide() {
state = TitlePart.State.HIDE
unparsed.hide()
projectTitle.hide()
override fun getLong(): String {
return if(parsed) projectTitle.getLong() else unparsed.getLong()
}
override fun showLong() {
state = TitlePart.State.LONG
unparsed.showLong()
projectTitle.showLong()
override fun getShort(): String {
return if(parsed) projectTitle.getShort() else unparsed.getShort()
}
override fun showShort() {
state = TitlePart.State.SHORT
unparsed.showShort()
projectTitle.showShort()
override fun refresh(label: JComponent, fm: FontMetrics) {
unparsed.refresh(label, fm)
projectTitle.refresh(label, fm)
}
override fun refresh() {
unparsed.refresh()
projectTitle.refresh()
}
override fun shrink(maxWidth: Int): Int {
override fun shrink(label: JComponent, fm: FontMetrics, maxWidth: Int): String {
return when {
parsed -> {
projectTitle.shrink(maxWidth)
projectTitle.shrink(label, fm, maxWidth)
}
else -> {
unparsed.component.isVisible = true
projectTitle.component.isVisible = false
return if (maxWidth > unparsed.longWidth) {
unparsed.showLong()
unparsed.longWidth
unparsed.getLong()
}
else {
unparsed.showShort()
unparsed.shortWidth
unparsed.getShort()
}
}
}
@@ -137,39 +86,19 @@ class ProjectTitlePane : ShrinkingTitlePart {
}
class ProjectTitle : ShrinkingTitlePart {
private val label = DefaultPartTitle.TitleLabel()
protected var text: String =""
private val description = ClippingTitle()
private var projectTextWidth: Int = 0
private var longTextWidth: Int = 0
private var state = TitlePart.State.LONG
override var active: Boolean
get() = description.active
set(value) {
description.active = value
}
private val pane = object : JPanel(MigLayout("ins 0, gap 0", "[pref][pref]")){
override fun setForeground(fg: Color?) {
super.setForeground(fg)
label.foreground = fg
description.component.foreground = fg
}
}.apply {
add(label)
add(description.component)
isOpaque = false
}
override fun setToolTip(value: String?) {
label.toolTipText = value
description.setToolTip(value)
}
override val component: JComponent
get() = pane
var openChar: String
get() = description.prefix
set(value) {
@@ -195,64 +124,39 @@ class ProjectTitle : ShrinkingTitlePart {
override val shortWidth: Int
get() = projectTextWidth
override val toolTipPart: String
get() = if (state == TitlePart.State.IGNORED || project.isEmpty()) "" else project
get() = project+description.getLong()
override fun hide() {
state = TitlePart.State.HIDE
label.text = ""
override fun getLong(): String {
text = project + description.getLong()
return text
}
override val isClipped: Boolean
get() = state != TitlePart.State.LONG || !description.active
override fun showLong() {
label.text = project
description.showLong()
state = TitlePart.State.LONG
override fun getShort(): String {
text = project
return text
}
override fun showShort() {
label.text = project
description.hide()
state = TitlePart.State.SHORT
}
override fun shrink(maxWidth: Int): Int {
override fun shrink(label: JComponent, fm: FontMetrics, maxWidth: Int): String {
return when {
maxWidth > longWidth -> {
label.text = project
description.showLong()
longWidth
text = project + description.getLong()
text
}
maxWidth > shortWidth + description.shortWidth -> {
label.text = project
description.shrink(maxWidth - shortWidth) + shortWidth
text = project + description.shrink(label, fm, maxWidth - shortWidth)
text
}
else -> {
label.text = project
description.hide()
shortWidth
text = project
text
}
}
}
override fun refresh() {
description.refresh()
val fm = label.getFontMetrics(label.font)
override fun refresh(label: JComponent, fm: FontMetrics) {
description.refresh(label, fm)
projectTextWidth = if (project.isEmpty()) 0 else SwingUtilities2.stringWidth(label, fm, project)
longTextWidth = projectTextWidth + description.longWidth
}
}
class BoldTitleLabel : DefaultPartTitle.TitleLabel() {
private fun fontUIResource(font: Font) = FontUIResource(font.deriveFont(font.style or Font.BOLD))
override fun setFont(font: Font) {
super.setFont(fontUIResource(font))
}
init {
font = fontUIResource(font)
}
}
@@ -17,12 +17,11 @@ import com.intellij.openapi.util.registry.RegistryValueListener
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.wm.impl.FrameTitleBuilder
import com.intellij.openapi.wm.impl.IdeFrameImpl
import net.miginfocom.swing.MigLayout
import java.awt.Dimension
import java.awt.event.ComponentAdapter
import java.awt.event.ComponentEvent
import javax.swing.JComponent
import javax.swing.JPanel
import javax.swing.JLabel
open class SelectedEditorFilePath(private val onBoundsChanged: (() -> Unit)? = null ) {
@@ -50,7 +49,7 @@ open class SelectedEditorFilePath(private val onBoundsChanged: (() -> Unit)? = n
}
}
private val pane = object : JPanel(MigLayout("ins 0, gap 0", "[min!][pref][pref][pref][pref]push")){
private val label = object : JLabel() {
override fun addNotify() {
super.addNotify()
installListeners()
@@ -64,14 +63,6 @@ open class SelectedEditorFilePath(private val onBoundsChanged: (() -> Unit)? = n
override fun getMinimumSize(): Dimension {
return Dimension(projectTitle.shortWidth, super.getMinimumSize().height)
}
}.apply {
isOpaque = false
add(projectTitle.component)
add(classTitle.component, "growx")
add(productTitle.component)
add(productVersion.component)
add(superUserSuffix.component)
}
private fun updateTitlePaths() {
@@ -82,7 +73,7 @@ open class SelectedEditorFilePath(private val onBoundsChanged: (() -> Unit)? = n
}
open fun getView(): JComponent {
return pane
return label
}
private var disposable: Disposable? = null
@@ -137,13 +128,6 @@ open class SelectedEditorFilePath(private val onBoundsChanged: (() -> Unit)? = n
getView().removeComponentListener(resizedListener)
}
fun isClipped(): Boolean {
for (component in components) {
if(component.isClipped) return true
}
return false
}
private val resizedListener = object : ComponentAdapter() {
override fun componentResized(e: ComponentEvent?) {
update()
@@ -155,7 +139,7 @@ open class SelectedEditorFilePath(private val onBoundsChanged: (() -> Unit)? = n
val fileEditorManager = FileEditorManager.getInstance(it)
val file = if (fileEditorManager is FileEditorManagerEx) {
val splittersFor = fileEditorManager.getSplittersFor(pane)
val splittersFor = fileEditorManager.getSplittersFor(getView())
splittersFor.currentFile
}
else {
@@ -192,117 +176,103 @@ open class SelectedEditorFilePath(private val onBoundsChanged: (() -> Unit)? = n
}
}
protected var isClipped = false
private fun update() {
val insets = getView().getInsets(null)
val width: Int = getView().width - (insets.right + insets.left)
components.forEach{it.refresh()}
val fm = label.getFontMetrics(label.font)
when {
components.forEach{it.refresh(label, fm)}
isClipped = true
label.text = when {
width > projectTitle.longWidth + classTitle.longWidth + productTitle.longWidth + superUserSuffix.longWidth + productVersion.longWidth -> {
//LOGGER.info("projectTitle.showLong, classTitle.showLong, productTitle.showLong, productVersion.showLong")
projectTitle.showLong()
classTitle.showLong()
productTitle.showLong()
productVersion.showLong()
superUserSuffix.showLong()
isClipped = components.any{!it.active}
projectTitle.getLong()+
classTitle.getLong()+
productTitle.getLong()+
productVersion.getLong()+
superUserSuffix.getLong()
}
width > projectTitle.longWidth + classTitle.longWidth + productTitle.longWidth + productVersion.longWidth + superUserSuffix.shortWidth -> {
//LOGGER.info("projectTitle.showLong, classTitle.showLong, productTitle.showLong, superUserSuffix.SHOW_SHORT")
projectTitle.showLong()
classTitle.showLong()
productTitle.showLong()
productVersion.showLong()
superUserSuffix.showShort()
projectTitle.getLong()+
classTitle.getLong()+
productTitle.getLong()+
productVersion.getLong()+
superUserSuffix.getShort()
}
width > projectTitle.longWidth + classTitle.longWidth + productVersion.longWidth + productTitle.longWidth -> {
//LOGGER.info("projectTitle.showLong, classTitle.showLong, productTitle.showLong, superUserSuffix.HIDE")
projectTitle.showLong()
classTitle.showLong()
productTitle.showLong()
productVersion.showLong()
superUserSuffix.hide()
projectTitle.getLong()+
classTitle.getLong()+
productTitle.getLong()+
productVersion.getLong()
}
width > projectTitle.longWidth + classTitle.longWidth + productVersion.shortWidth + productTitle.longWidth -> {
//LOGGER.info("projectTitle.showLong, classTitle.showLong, productTitle.showLong, productVersion.SHOW_SHORT")
projectTitle.showLong()
classTitle.showLong()
productTitle.showLong()
productVersion.showShort()
superUserSuffix.hide()
projectTitle.getLong()+
classTitle.getLong()+
productTitle.getLong()+
productVersion.getShort()
}
width > projectTitle.longWidth + classTitle.longWidth + productTitle.longWidth -> {
//LOGGER.info("projectTitle.showLong, classTitle.showLong, productTitle.showLong, productVersion.HIDE")
projectTitle.showLong()
classTitle.showLong()
productTitle.showLong()
superUserSuffix.hide()
productVersion.hide()
projectTitle.getLong()+
classTitle.getLong()+
productTitle.getLong()
}
width > projectTitle.longWidth + classTitle.longWidth + productTitle.shortWidth -> {
//LOGGER.info("projectTitle.showLong, classTitle.showLong, productTitle.SHOW_SHORT")
projectTitle.showLong()
classTitle.showLong()
productTitle.showShort()
productVersion.hide()
superUserSuffix.hide()
projectTitle.getLong()+
classTitle.getLong()+
productTitle.getShort()
}
width > projectTitle.longWidth + classTitle.longWidth -> {
//LOGGER.info("projectTitle.showLong, classTitle.showLong, productTitle.HIDE, productVersion.HIDE")
projectTitle.showLong()
classTitle.showLong()
productTitle.hide()
productVersion.hide()
superUserSuffix.hide()
projectTitle.getLong()+
classTitle.getLong()
}
width > projectTitle.longWidth + classTitle.shortWidth -> {
//LOGGER.info("projectTitle.showLong, classTitle.SHRINK: ${width - projectTitle.longWidth}, productTitle.HIDE, productVersion.HIDE")
projectTitle.showLong()
productTitle.hide()
productVersion.hide()
superUserSuffix.hide()
classTitle.shrink(width - projectTitle.longWidth)
projectTitle.getLong()+
classTitle.shrink(label, fm,width - projectTitle.longWidth)
}
width > projectTitle.shortWidth + classTitle.shortWidth -> {
//LOGGER.info("projectTitle.showLong, classTitle.SHOW_SHORT, productTitle.HIDE, productVersion.HIDE")
projectTitle.shrink(width - classTitle.shortWidth)
productTitle.hide()
productVersion.hide()
superUserSuffix.hide()
classTitle.showShort()
projectTitle.shrink(label, fm,width - classTitle.shortWidth)+
classTitle.getShort()
}
width > projectTitle.shortWidth -> {
else -> {
//LOGGER.info("projectTitle.SHOW_SHORT, classTitle.HIDE, productTitle.HIDE, productVersion.HIDE")
projectTitle.showShort()
productTitle.hide()
productVersion.hide()
superUserSuffix.hide()
classTitle.hide()
projectTitle.getShort()
}
}
val clipped = isClipped()
val tooltip = if(!clipped) null else components.joinToString(separator = "", transform = {it.toolTipPart})
components.forEach {it.setToolTip(tooltip)}
label.toolTipText = if(!isClipped) null else components.joinToString(separator = "", transform = {it.toolTipPart})
onBoundsChanged?.invoke()
}
@@ -1,6 +1,7 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.openapi.wm.impl.customFrameDecorations.header.titleLabel
import java.awt.FontMetrics
import javax.swing.JComponent
interface TitlePart {
@@ -13,23 +14,16 @@ interface TitlePart {
}
var active: Boolean
val component: JComponent
val longWidth: Int
val shortWidth: Int
val toolTipPart: String
val isClipped: Boolean
fun refresh()
fun refresh(label: JComponent, fm: FontMetrics)
fun hide()
fun showLong()
fun showShort()
fun setToolTip(value: String?)
fun getLong(): String
fun getShort(): String
}
interface BaseTitlePart : TitlePart {
@@ -38,5 +32,5 @@ interface BaseTitlePart : TitlePart {
}
interface ShrinkingTitlePart : TitlePart {
fun shrink(maxWidth: Int): Int
fun shrink(label: JComponent, fm: FontMetrics, maxWidth: Int): String
}