appearance.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. // Gnome imports
  2. import Adw from "gi://Adw";
  3. import GObject from "gi://GObject";
  4. import Gdk from "gi://Gdk";
  5. // Extension imports
  6. import { gettext as _ } from "resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js";
  7. // Shared state
  8. import { ConfigManager } from "../shared/settings.js";
  9. import { PrefsThemeManager } from "./prefs-theme-manager.js";
  10. // Prefs UI
  11. import { ColorRow, PreferencesPage, ResetButton, SpinButtonRow, SwitchRow } from "./widgets.js";
  12. import { Logger } from "../shared/logger.js";
  13. export class AppearancePage extends PreferencesPage {
  14. static {
  15. GObject.registerClass(this);
  16. }
  17. /**
  18. * @param {string} selector
  19. */
  20. static getCssSelectorAsMessage(selector) {
  21. switch (selector) {
  22. // TODO: make separate color selection for preview hint
  23. case ".window-tiled-border":
  24. return _("Tiled window");
  25. case ".window-tabbed-border":
  26. return _("Tabbed window");
  27. case ".window-stacked-border":
  28. return _("Stacked window");
  29. case ".window-floated-border":
  30. return _("Floating window");
  31. case ".window-split-border":
  32. return _("Split direction hint");
  33. }
  34. }
  35. constructor({ settings, dir }) {
  36. super({ title: _("Appearance"), icon_name: "brush-symbolic" });
  37. this.settings = settings;
  38. this.configMgr = new ConfigManager({ dir });
  39. this.themeMgr = new PrefsThemeManager(this);
  40. this.add_group({
  41. title: _("Gaps"),
  42. description: _("Change the gap size between windows"),
  43. children: [
  44. new SpinButtonRow({
  45. title: _("Gap size"),
  46. range: [0, 32, 1],
  47. settings,
  48. bind: "window-gap-size",
  49. }),
  50. new SpinButtonRow({
  51. title: _("Gap size multiplier"),
  52. range: [0, 32, 1],
  53. settings,
  54. bind: "window-gap-size-increment",
  55. }),
  56. new SwitchRow({
  57. title: _("Disable gaps for single window"),
  58. subtitle: _("Disables window gaps when only a single window is present"),
  59. settings,
  60. bind: "window-gap-hidden-on-single",
  61. }),
  62. ],
  63. });
  64. this.add_group({
  65. title: _("Style"),
  66. description: _("Change how the shell looks"),
  67. children: [
  68. new SwitchRow({
  69. title: _("Preview hint"),
  70. subtitle: _("Shows where the window will be tiled when you let go of it"),
  71. experimental: true,
  72. settings,
  73. bind: "preview-hint-enabled",
  74. }),
  75. new SwitchRow({
  76. title: _("Border around focused window"),
  77. subtitle: _("Display a colored border around the focused window"),
  78. settings,
  79. bind: "focus-border-toggle",
  80. }),
  81. new SwitchRow({
  82. title: _("Window split hint border"),
  83. subtitle: _("Show split direction border on focused window"),
  84. settings,
  85. bind: "split-border-toggle",
  86. }),
  87. new SwitchRow({
  88. title: _("Forge in quick settings"),
  89. subtitle: _("Toggles the Forge tile in quick settings"),
  90. experimental: true,
  91. settings,
  92. bind: "quick-settings-enabled",
  93. }),
  94. ],
  95. });
  96. this.add_group({
  97. title: _("Color"),
  98. description: _("Changes the focused window's border and preview hint colors"),
  99. children: [
  100. "window-tiled-border",
  101. "window-tabbed-border",
  102. "window-stacked-border",
  103. "window-floated-border",
  104. "window-split-border",
  105. ].map((x) => this._createColorOptionWidget(x)),
  106. });
  107. }
  108. /**
  109. * @param {string} prefix
  110. */
  111. _createColorOptionWidget(prefix) {
  112. const selector = `.${prefix}`;
  113. const theme = this.themeMgr;
  114. const title = AppearancePage.getCssSelectorAsMessage(selector);
  115. const colorScheme = theme.getColorSchemeBySelector(selector);
  116. const row = new Adw.ExpanderRow({ title });
  117. const borderSizeRow = new SpinButtonRow({
  118. title: _("Border size"),
  119. range: [1, 6, 1],
  120. // subtitle: 'Properties of the focus hint',
  121. max_width_chars: 1,
  122. max_length: 1,
  123. width_chars: 2,
  124. xalign: 1,
  125. init: theme.removePx(theme.getCssProperty(selector, "border-width").value),
  126. onChange: (value) => {
  127. const px = theme.addPx(value);
  128. Logger.debug(`Setting border width for selector: ${selector} ${px}`);
  129. theme.setCssProperty(selector, "border-width", px);
  130. },
  131. });
  132. borderSizeRow.add_suffix(
  133. new ResetButton({
  134. onReset: () => {
  135. const borderDefault = theme.defaultPalette[colorScheme]["border-width"];
  136. theme.setCssProperty(selector, "border-width", theme.addPx(borderDefault));
  137. borderSizeRow.activatable_widget.value = borderDefault;
  138. },
  139. })
  140. );
  141. const updateCssColors = (rgbaString) => {
  142. const rgba = new Gdk.RGBA();
  143. if (rgba.parse(rgbaString)) {
  144. Logger.debug(`Setting color for selector: ${selector} ${rgbaString}`);
  145. const previewBorderRgba = rgba.copy();
  146. const previewBackgroundRgba = rgba.copy();
  147. const overviewBackgroundRgba = rgba.copy();
  148. previewBorderRgba.alpha = 0.3;
  149. previewBackgroundRgba.alpha = 0.2;
  150. overviewBackgroundRgba.alpha = 0.5;
  151. // The primary color updates the focus hint:
  152. theme.setCssProperty(selector, "border-color", rgba.to_string());
  153. // Only apply below on the tabbed scheme
  154. if (colorScheme === "tabbed") {
  155. const tabBorderRgba = rgba.copy();
  156. const tabActiveBackgroundRgba = rgba.copy();
  157. tabBorderRgba.alpha = 0.6;
  158. theme.setCssProperty(
  159. `.window-${colorScheme}-tab`,
  160. "border-color",
  161. tabBorderRgba.to_string()
  162. );
  163. theme.setCssProperty(
  164. `.window-${colorScheme}-tab-active`,
  165. "background-color",
  166. tabActiveBackgroundRgba.to_string()
  167. );
  168. }
  169. // And then finally the preview when doing drag/drop tiling:
  170. theme.setCssProperty(
  171. `.window-tilepreview-${colorScheme}`,
  172. "border-color",
  173. previewBorderRgba.to_string()
  174. );
  175. theme.setCssProperty(
  176. `.window-tilepreview-${colorScheme}`,
  177. "background-color",
  178. previewBackgroundRgba.to_string()
  179. );
  180. }
  181. };
  182. const borderColorRow = new ColorRow({
  183. title: _("Border color"),
  184. init: theme.getCssProperty(selector, "border-color").value,
  185. onChange: updateCssColors,
  186. });
  187. borderColorRow.add_suffix(
  188. new ResetButton({
  189. onReset: () => {
  190. const selectorColor = theme.defaultPalette[colorScheme].color;
  191. updateCssColors(selectorColor);
  192. const rgba = new Gdk.RGBA();
  193. if (rgba.parse(selectorColor)) {
  194. borderColorRow.colorButton.set_rgba(rgba);
  195. }
  196. },
  197. })
  198. );
  199. row.add_row(borderColorRow);
  200. row.add_row(borderSizeRow);
  201. return row;
  202. }
  203. }