mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Fix IDEA-212259: proper border width calculation on Windows
Unfortunately, we have to live with integer-based Swing API, and have to perform all the calculations in logical (fake) pixels. Fortunately, the graphics API accepts double coordinates, so we could draw lines of proper width while sometimes reserving a little more space for insets. GitOrigin-RevId: 7b6398842cd7036e1c1b72ceab88172c101f57a1
This commit is contained in:
committed by
intellij-monorepo-bot
parent
fb3c387e40
commit
c5542c8a58
+39
-11
@@ -17,6 +17,7 @@ import com.intellij.ui.Gray
|
||||
import com.intellij.ui.JBColor
|
||||
import com.intellij.ui.awt.RelativeRectangle
|
||||
import com.intellij.ui.paint.LinePainter2D
|
||||
import com.intellij.ui.scale.JBUIScale
|
||||
import com.intellij.ui.scale.ScaleContext
|
||||
import com.intellij.ui.scale.ScaleType
|
||||
import com.intellij.util.ui.JBFont
|
||||
@@ -30,6 +31,7 @@ import java.beans.PropertyChangeListener
|
||||
import java.util.*
|
||||
import javax.swing.*
|
||||
import javax.swing.border.Border
|
||||
import kotlin.math.ceil
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
abstract class CustomHeader(private val window: Window) : JPanel(), Disposable {
|
||||
@@ -75,6 +77,19 @@ abstract class CustomHeader(private val window: Window) : JPanel(), Disposable {
|
||||
private fun createFrameHeader(frame: JFrame): DefaultFrameHeader = DefaultFrameHeader(frame)
|
||||
@JvmStatic
|
||||
fun createMainFrameHeader(frame: JFrame, delegatingMenuBar: IdeMenuBar?): MainFrameHeader = MainFrameHeader(frame, delegatingMenuBar)
|
||||
|
||||
private val borderThicknessInPhysicalPx: Int? = run {
|
||||
// Windows 10 (tested on 1809) determines the window border size by the smallest scale of all devices, rounded down. This value
|
||||
// is calculated once on desktop session start, so it should be okay to store once per IDE session.
|
||||
val minimalScale = GraphicsEnvironment.getLocalGraphicsEnvironment().screenDevices
|
||||
.filterIsInstance<sun.awt.Win32GraphicsDevice>()
|
||||
.map { it.defaultScaleY }
|
||||
.min()
|
||||
if (minimalScale != null)
|
||||
kotlin.math.floor(minimalScale).toInt()
|
||||
else
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private var windowListener: WindowAdapter
|
||||
@@ -261,11 +276,12 @@ abstract class CustomHeader(private val window: Window) : JPanel(), Disposable {
|
||||
}
|
||||
|
||||
inner class CustomFrameTopBorder(val isTopNeeded: ()-> Boolean = {true}, val isBottomNeeded: ()-> Boolean = {false}) : Border {
|
||||
val thickness = 1
|
||||
|
||||
// In reality, Windows uses alpha-blending with alpha=0.34 by default, but we have no (easy) way of doing the same, so let's just
|
||||
// use the value without alpha. Unfortunately, DWM doesn't offer an API to determine this value.
|
||||
private val defaultActiveBorder = Color(0x262626)
|
||||
// In reality, Windows uses #262626 with alpha-blending with alpha=0.34, but we have no (easy) way of doing the same, so let's just
|
||||
// use the value on white background (since it is most noticeable on white).
|
||||
//
|
||||
// Unfortunately, DWM doesn't offer an API to determine this value, so it has to be hardcoded here.
|
||||
private val defaultActiveBorder = Color(0x707070)
|
||||
private val inactiveColor = Color(0xaaaaaa)
|
||||
|
||||
private val menuBarBorderColor: Color = JBColor.namedColor("MenuBar.borderColor", JBColor(Gray.xCD, Gray.x51))
|
||||
@@ -315,6 +331,15 @@ abstract class CustomHeader(private val window: Window) : JPanel(), Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
private fun calculateBorderThicknessInLogicalPx(): Double {
|
||||
val physical = borderThicknessInPhysicalPx
|
||||
if (physical != null) {
|
||||
return physical.toDouble() / JBUIScale.sysScale(window)
|
||||
}
|
||||
|
||||
return 1.0
|
||||
}
|
||||
|
||||
private val listeners = mutableListOf<Pair<String, PropertyChangeListener>>()
|
||||
private inline fun listenForPropertyChanges(vararg propertyNames: String, crossinline action: () -> Unit) {
|
||||
val toolkit = Toolkit.getDefaultToolkit()
|
||||
@@ -348,7 +373,8 @@ abstract class CustomHeader(private val window: Window) : JPanel(), Disposable {
|
||||
fun repaintBorder() {
|
||||
val borderInsets = getBorderInsets(this@CustomHeader)
|
||||
|
||||
repaint(0, 0, width, thickness)
|
||||
val thickness = calculateBorderThicknessInLogicalPx()
|
||||
repaint(0, 0, width, ceil(thickness).toInt())
|
||||
repaint(0, height - borderInsets.bottom, width, borderInsets.bottom)
|
||||
}
|
||||
|
||||
@@ -360,22 +386,24 @@ abstract class CustomHeader(private val window: Window) : JPanel(), Disposable {
|
||||
}
|
||||
|
||||
override fun paintBorder(c: Component, g: Graphics, x: Int, y: Int, width: Int, height: Int) {
|
||||
val thickness = calculateBorderThicknessInLogicalPx()
|
||||
if (isTopNeeded() && shouldDrawTopBorder) {
|
||||
g.color = if (myActive) activeColor else inactiveColor
|
||||
LinePainter2D.paint(g as Graphics2D, x.toDouble(), y.toDouble(), width.toDouble(), y.toDouble())
|
||||
LinePainter2D.paint(g as Graphics2D, x.toDouble(), y.toDouble(), width.toDouble(), y.toDouble(), LinePainter2D.StrokeType.CENTERED, thickness)
|
||||
}
|
||||
|
||||
if (isBottomNeeded()) {
|
||||
g.color = menuBarBorderColor
|
||||
val y1 = y + height - JBUI.scale(thickness)
|
||||
LinePainter2D.paint(g as Graphics2D, x.toDouble(), y1.toDouble(), width.toDouble(), y1.toDouble())
|
||||
val y1 = y + height - thickness
|
||||
LinePainter2D.paint(g as Graphics2D, x.toDouble(), y1, width.toDouble(), y1, LinePainter2D.StrokeType.CENTERED, thickness)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getBorderInsets(c: Component): Insets {
|
||||
val scale = JBUI.scale(thickness)
|
||||
val top = if (isTopNeeded() && (colorizationAffectsBorders || UIUtil.isUnderIntelliJLaF())) thickness else 0
|
||||
return Insets(top, 0, if (isBottomNeeded()) scale else 0, 0)
|
||||
val thickness = calculateBorderThicknessInLogicalPx()
|
||||
val top = if (isTopNeeded() && (colorizationAffectsBorders || UIUtil.isUnderIntelliJLaF())) ceil(thickness).toInt() else 0
|
||||
val bottom = if (isBottomNeeded()) ceil(thickness).toInt() else 0
|
||||
return Insets(top, 0, bottom, 0)
|
||||
}
|
||||
|
||||
override fun isBorderOpaque(): Boolean {
|
||||
|
||||
Reference in New Issue
Block a user