Fix IDEA-219212: add a setting to switch borderless mode for Windows
authorIvan Migalev <ivan.migalev@jetbrains.com>
Thu, 6 Aug 2020 08:47:57 +0000 (15:47 +0700)
committerintellij-monorepo-bot <intellij-monorepo-bot-no-reply@jetbrains.com>
Mon, 10 Aug 2020 15:25:37 +0000 (15:25 +0000)
GitOrigin-RevId: 382ad4219c9659a7d962dafeb9df17591690f4e9

platform/editor-ui-api/src/com/intellij/ide/ui/UISettings.kt
platform/editor-ui-api/src/com/intellij/ide/ui/UISettingsState.kt
platform/platform-api/resources/messages/IdeBundle.properties
platform/platform-impl/src/com/intellij/ide/ui/AppearanceConfigurable.kt
platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeFrameDecorator.java

index d8451669bfc04e835a83dbad73b718e88f3a0b26..026eb77d68b149864c9f50da2d0ebda99e6b1377 100644 (file)
@@ -420,6 +420,12 @@ class UISettings @NonInjectable constructor(private val notRoamableOptions: NotR
       state.fullPathsInWindowHeader = value
     }
 
+  var mergeMainMenuWithWindowTitle: Boolean
+    get() = state.mergeMainMenuWithWindowTitle
+    set(value) {
+      state.mergeMainMenuWithWindowTitle = value
+    }
+
   init {
     // TODO Remove the registry keys and migration code in 2019.3
     if (SystemProperties.`is`("tabs.alphabetical")) {
@@ -563,6 +569,11 @@ class UISettings @NonInjectable constructor(private val notRoamableOptions: NotR
       LOG.info("Loaded: fontSize=$readSize, fontScale=$readScale; restored: fontSize=$size, fontScale=$defFontScale")
       return size
     }
+
+    const val MERGE_MAIN_MENU_WITH_WINDOW_TITLE_PROPERTY = "ide.win.frame.decoration"
+    @JvmStatic
+    val mergeMainMenuWithWindowTitleOverrideValue = System.getProperty(MERGE_MAIN_MENU_WITH_WINDOW_TITLE_PROPERTY)?.toBoolean()
+    val isMergeMainMenuWithWindowTitleOverridden = mergeMainMenuWithWindowTitleOverrideValue != null
   }
 
   @Suppress("DeprecatedCallableAddReplaceWith")
index 18a2e5a3a4048708791b27d8c503beb1ee556ab2..17f0fadc1939c516d3be881746deae392f376bfc 100644 (file)
@@ -173,6 +173,8 @@ class UISettingsState : BaseState() {
   var navigateToPreview by property(false)
   @get:OptionTag("FULL_PATHS_IN_TITLE_BAR")
   var fullPathsInWindowHeader by property(false)
+  @get:OptionTag("BORDERLESS_MODE")
+  var mergeMainMenuWithWindowTitle by property(SystemInfo.isWin10OrNewer && SystemInfo.isJetBrainsJvm)
 
   var animatedScrolling by property(!SystemInfo.isMac || !SystemInfo.isJetBrainsJvm)
   var animatedScrollingDuration by property(
index a2d9722bcc7aba4e1794c81514947b02d1a9fcbb..8cc057b336e9c05b335315220eb978d19de6d722 100644 (file)
@@ -160,6 +160,8 @@ action.previous.problem=Previous Problem
 action.stop=Stop
 errortree.prefix.line=line ({0})
 checkbox.errortree.export.details=Details
+checkbox.merge.main.menu.with.window.title=Merge main menu with window title
+checkbox.merge.main.menu.with.window.title.comment=Requires restart
 
 # Favorites
 action.add.all.open.tabs.to.new.favorites.list=Add All Open Tabs To New Favorites List
index a1f85911c2b7db9fc5be027daa76773917096d75..92cc0a8647e2f772efad69c3a8ac22d9902f5fc1 100644 (file)
@@ -23,6 +23,7 @@ import com.intellij.openapi.ui.ComboBox
 import com.intellij.openapi.ui.DialogPanel
 import com.intellij.openapi.util.registry.Registry
 import com.intellij.openapi.wm.ex.WindowManagerEx
+import com.intellij.openapi.wm.impl.IdeFrameDecorator
 import com.intellij.ui.ContextHelpLabel
 import com.intellij.ui.FontComboBox
 import com.intellij.ui.SimpleListCellRenderer
@@ -63,6 +64,7 @@ private val cdDnDWithAlt                              get() = CheckboxDescriptor
 private val cdUseTransparentMode                      get() = CheckboxDescriptor(message("checkbox.use.transparent.mode.for.floating.windows"), PropertyBinding({ settings.state.enableAlphaMode }, { settings.state.enableAlphaMode = it }))
 private val cdOverrideLaFFont                         get() = CheckboxDescriptor(message("checkbox.override.default.laf.fonts"), settings::overrideLafFonts)
 private val cdUseContrastToolbars                     get() = CheckboxDescriptor(message("checkbox.acessibility.contrast.scrollbars"), settings::useContrastScrollbars)
+private val cdMergeMainMenuWithWindowTitle            get() = CheckboxDescriptor(message("checkbox.merge.main.menu.with.window.title"), settings::mergeMainMenuWithWindowTitle, groupName = windowOptionGroupName)
 private val cdFullPathsInTitleBar                     get() = CheckboxDescriptor(message("checkbox.full.paths.in.window.header"), settings::fullPathsInWindowHeader)
 private val cdShowMenuIcons                           get() = CheckboxDescriptor(message("checkbox.show.icons.in.menu.items"), settings::showIconsInMenus, groupName = windowOptionGroupName)
 
@@ -190,25 +192,41 @@ internal class AppearanceConfigurable : BoundSearchableConfigurable(message("tit
         }
       }
       titledRow(message("group.ui.options")) {
-        twoColumnRow(
-          { checkBox(cdShowTreeIndents) },
+        val hasMergeMainMenuWithWindowTitleOption = IdeFrameDecorator.isCustomDecorationAvailable()
+        twoPanelRow(
+          {
+            fullRow { checkBox(cdShowTreeIndents) }
+            fullRow { checkBox(cdUseCompactTreeIndents) }
+            fullRow { checkBox(cdEnableMenuMnemonics) }
+            fullRow { checkBox(cdEnableControlsMnemonics) }
+            // The last item migrates here from the right column if the right column has the additional item:
+            if (hasMergeMainMenuWithWindowTitleOption)
+              fullRow { checkBox(cdShowMenuIcons) }
+          },
           {
-            checkBox(cdSmoothScrolling)
-            ContextHelpLabel.create(message("checkbox.smooth.scrolling.description"))()
+            fullRow {
+              checkBox(cdSmoothScrolling)
+              ContextHelpLabel.create(message("checkbox.smooth.scrolling.description"))()
+            }
+            fullRow { checkBox(cdDnDWithAlt) }
+
+            if (hasMergeMainMenuWithWindowTitleOption) {
+              fullRow {
+                val overridden = UISettings.isMergeMainMenuWithWindowTitleOverridden
+                checkBox(cdMergeMainMenuWithWindowTitle).enabled(!overridden)
+                if (overridden) {
+                  ContextHelpLabel.create(
+                    message("option.is.overridden.by.jvm.property", UISettings.MERGE_MAIN_MENU_WITH_WINDOW_TITLE_PROPERTY))()
+                }
+                commentNoWrap(message("checkbox.merge.main.menu.with.window.title.comment")).withLargeLeftGap()
+              }
+            }
+            fullRow { checkBox(cdFullPathsInTitleBar) }
+            // The last item migrates to the left column if the right column has the additional item:
+            if (!hasMergeMainMenuWithWindowTitleOption)
+              fullRow { checkBox(cdShowMenuIcons) }
           }
         )
-        twoColumnRow(
-          { checkBox(cdUseCompactTreeIndents) },
-          { checkBox(cdDnDWithAlt) }
-        )
-        twoColumnRow(
-          { checkBox(cdEnableMenuMnemonics) },
-          { checkBox(cdFullPathsInTitleBar) }
-        )
-        twoColumnRow(
-          { checkBox(cdEnableControlsMnemonics) },
-          { checkBox(cdShowMenuIcons) }
-        )
         val backgroundImageAction = ActionManager.getInstance().getAction("Images.SetBackgroundImage")
         if (backgroundImageAction != null) {
           fullRow {
@@ -334,6 +352,16 @@ private fun RowBuilder.twoColumnRow(column1: InnerCell.() -> Unit, column2: Inne
   placeholder().constraints(growX, pushX)
 }
 
+private fun RowBuilder.twoPanelRow(initPanel1: LayoutBuilder.() -> Unit, initPanel2: LayoutBuilder.() -> Unit): Row {
+  val panel1 = panel {
+    initPanel1()
+  }
+  val panel2 = panel {
+    initPanel2()
+  }
+  return twoColumnRow({ component(panel1) }, { component(panel2) })
+}
+
 private fun getIntValue(text: String?, defaultValue: Int): Int {
   if (text != null && text.isNotBlank()) {
     val value = text.toIntOrNull()
index 9043e3a6001af74209ee8e964e243fff26712cc1..fd419d6ba38d9ebb72f3452bc9bc1d2c1816d5a8 100644 (file)
@@ -1,6 +1,7 @@
 // Copyright 2000-2020 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;
 
+import com.intellij.ide.ui.UISettings;
 import com.intellij.jdkEx.JdkEx;
 import com.intellij.openapi.Disposable;
 import com.intellij.openapi.diagnostic.Logger;
@@ -9,7 +10,6 @@ import com.intellij.openapi.util.SystemInfo;
 import com.intellij.ui.ComponentUtil;
 import com.intellij.ui.ScreenUtil;
 import com.intellij.ui.mac.MacMainFrameDecorator;
-import com.intellij.util.SystemProperties;
 import com.intellij.util.ui.UIUtil;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -22,6 +22,8 @@ import java.awt.event.ComponentAdapter;
 import java.awt.event.ComponentEvent;
 import java.awt.event.WindowAdapter;
 import java.awt.event.WindowEvent;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicReference;
 
 public abstract class IdeFrameDecorator implements IdeFrameImpl.FrameDecorator {
   static final String FULL_SCREEN = "ide.frame.full.screen";
@@ -187,7 +189,28 @@ public abstract class IdeFrameDecorator implements IdeFrameImpl.FrameDecorator {
     }
   }
 
+  public static boolean isCustomDecorationAvailable() {
+    return SystemInfo.isWindows && JdkEx.isCustomDecorationSupported();
+  }
+
+  private static final AtomicReference<Boolean> isCustomDecorationActiveCache = new AtomicReference<>();
   public static boolean isCustomDecorationActive() {
-    return SystemInfo.isWindows && SystemProperties.getBooleanProperty("ide.win.frame.decoration", true) && JdkEx.isCustomDecorationSupported();
+    UISettings settings = UISettings.getInstanceOrNull();
+    if (settings == null) {
+      // true by default is no settings is available (e.g. during the initial IDE setup wizard) and not overridden
+      return isCustomDecorationAvailable()
+             && !Objects.equals(UISettings.getMergeMainMenuWithWindowTitleOverrideValue(), false);
+    }
+
+    // Cache the initial value received from settings, because this value doesn't support change in runtime (we can't redraw frame headers
+    // of frames already created, and changing this setting during any frame lifetime will cause weird effects).
+    return isCustomDecorationActiveCache.updateAndGet(
+      cached -> {
+        if (cached != null) return cached;
+        if (!isCustomDecorationAvailable()) return false;
+        Boolean override = UISettings.getMergeMainMenuWithWindowTitleOverrideValue();
+        if (override != null) return override;
+        return settings.getMergeMainMenuWithWindowTitle();
+      });
   }
 }
\ No newline at end of file