Tie panel settings to monitor ids

#1782 #1976 #2182
This commit is contained in:
Charles Gagnon
2025-02-06 20:13:47 -05:00
parent 3ad58f17d6
commit 54f7e76263
7 changed files with 262 additions and 117 deletions

View File

@@ -609,14 +609,10 @@
<summary>Use favorite icons as application launchers</summary>
<description>When the applications are ungrouped, this defines if running applications stay separate from the favorite icons.</description>
</key>
<key type="i" name="primary-monitor">
<default>-1</default>
<key type="s" name="primary-monitor">
<default>''</default>
<summary>Primary monitor index</summary>
<description>Specifies the index of the primary monitor. -1 if same as gnome-shell primary monitor.</description>
</key>
<key type="i" name="gs-primary-monitor">
<default>0</default>
<summary>Primary gnome-shell monitor index</summary>
<description>Specifies the id of the primary monitor.</description>
</key>
<key type="b" name="multi-monitors">
<default>true</default>
@@ -1308,6 +1304,10 @@
<default>''</default>
<summary>The preferences page name to display</summary>
</key>
<key type="b" name="prefs-opened">
<default>false</default>
<summary>Track if the preferences window is opened</summary>
</key>
<key type="s" name="hide-donate-icon-unixtime">
<default>''</default>
<summary>Unix time when the donate icon was hidden</summary>

View File

@@ -22,6 +22,7 @@ import Gio from 'gi://Gio'
import * as Main from 'resource:///org/gnome/shell/ui/main.js'
import { EventEmitter } from 'resource:///org/gnome/shell/misc/signals.js'
import { Extension } from 'resource:///org/gnome/shell/extensions/extension.js'
import * as PanelSettings from './panelSettings.js'
import * as PanelManager from './panelManager.js'
import * as AppIcons from './appIcons.js'
@@ -49,7 +50,7 @@ export default class DashToPanelExtension extends Extension {
PERSISTENTSTORAGE = {}
}
enable() {
async enable() {
DTP_EXTENSION = this
SETTINGS = this.getSettings('org.gnome.shell.extensions.dash-to-panel')
DESKTOPSETTINGS = new Gio.Settings({
@@ -63,6 +64,14 @@ export default class DashToPanelExtension extends Extension {
//create a global object that can emit signals and conveniently expose functionalities to other extensions
global.dashToPanel = new EventEmitter()
// reset to be safe
SETTINGS.set_boolean('prefs-opened', false)
await PanelSettings.init(SETTINGS)
// To remove later, try to map settings using monitor indexes to monitor ids
PanelSettings.adjustMonitorSettings(SETTINGS)
let completeEnable = () => {
panelManager = new PanelManager.PanelManager()
panelManager.enable()
@@ -106,6 +115,7 @@ export default class DashToPanelExtension extends Extension {
disable() {
if (ubuntuDockDelayId) clearTimeout(ubuntuDockDelayId)
PanelSettings.disable(SETTINGS)
panelManager.disable()
DTP_EXTENSION = null
@@ -125,4 +135,10 @@ export default class DashToPanelExtension extends Extension {
Main.sessionMode.hasOverview = this._realHasOverview
}
openPreferences() {
if (SETTINGS.get_boolean('prefs-opened')) return
super.openPreferences()
}
}

View File

@@ -490,9 +490,10 @@ export const Panel = GObject.registerClass(
}
updateElementPositions() {
let panelPositions =
this.panelManager.panelsElementPositions[this.monitor.index] ||
Pos.defaults
let panelPositions = PanelSettings.getPanelElementPositions(
SETTINGS,
this.monitor.index,
)
this._updateGroupedElements(panelPositions)

View File

@@ -56,14 +56,12 @@ import {
export const PanelManager = class {
constructor() {
this.overview = new Overview.Overview()
this.panelsElementPositions = {}
this._injectionManager = new InjectionManager()
this._saveMonitors()
}
enable(reset) {
let dtpPrimaryIndex = SETTINGS.get_int('primary-monitor')
let dtpPrimaryIndex =
PanelSettings.monitorIdToIndex[SETTINGS.get_string('primary-monitor')]
this.allPanels = []
this.dtpPrimaryMonitor =
@@ -210,10 +208,10 @@ export const PanelManager = class {
'changed::panel-anchors',
'changed::stockgs-keep-top-panel',
],
(settings,settingChanged) => {
(settings, settingChanged) => {
PanelSettings.clearCache(settingChanged)
this._reset()
}
},
],
[
SETTINGS,
@@ -221,7 +219,7 @@ export const PanelManager = class {
() => {
PanelSettings.clearCache('panel-element-positions')
this._updatePanelElementPositions()
}
},
],
[
SETTINGS,
@@ -241,9 +239,9 @@ export const PanelManager = class {
[
Utils.DisplayWrapper.getMonitorManager(),
'monitors-changed',
() => {
async () => {
if (Main.layoutManager.primaryMonitor) {
this._saveMonitors()
await PanelSettings.setMonitorsInfo(SETTINGS)
this._reset()
}
},
@@ -492,25 +490,6 @@ export const PanelManager = class {
}
}
_saveMonitors() {
//Mutter meta_monitor_manager_get_primary_monitor (global.display.get_primary_monitor()) doesn't return the same
//monitor as GDK gdk_screen_get_primary_monitor (imports.gi.Gdk.Screen.get_default().get_primary_monitor()).
//Since the Mutter function is what's used in gnome-shell and we can't access it from the settings dialog, store
//the monitors information in a setting so we can use the same monitor indexes as the ones in gnome-shell
let keyPrimary = 'primary-monitor'
let primaryIndex = Main.layoutManager.primaryIndex
let dtpPrimaryIndex = SETTINGS.get_int(keyPrimary)
if (
!Main.layoutManager.monitors[dtpPrimaryIndex] ||
dtpPrimaryIndex == primaryIndex
)
dtpPrimaryIndex = -1
SETTINGS.set_int(keyPrimary, dtpPrimaryIndex)
SETTINGS.set_int('gs-primary-monitor', primaryIndex)
}
checkIfFocusedMonitor(monitor) {
return (
Main.overview._overview._controls._workspacesDisplay._primaryIndex ==
@@ -557,10 +536,6 @@ export const PanelManager = class {
}
_updatePanelElementPositions() {
this.panelsElementPositions = PanelSettings.getSettingsJson(
SETTINGS,
'panel-element-positions',
)
this.allPanels.forEach((p) => p.updateElementPositions())
}

View File

@@ -15,12 +15,48 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import Gio from 'gi://Gio'
import * as Pos from './panelPositions.js'
// cache is a different object in the settings dialog (gjs process)
const displayConfigWrapper = Gio.DBusProxy.makeProxyWrapper(
`<node>
<interface name="org.gnome.Mutter.DisplayConfig">
<method name="GetCurrentState">
<arg name="serial" direction="out" type="u" />
<arg name="monitors" direction="out" type="a((ssss)a(siiddada{sv})a{sv})" />
<arg name="logical_monitors" direction="out" type="a(iiduba(ssss)a{sv})" />
<arg name="properties" direction="out" type="a{sv}" />
</method>
</interface>
</node>`,
)
// the module variables here are different in the settings dialog (gjs process)
// and in gnome-shell (gnome-shell process)
let displayConfigProxy = null
let prefsOpenedId = null
let useCache = false
let cache = {}
export var availableMonitors = []
export var monitorIdToIndex = {}
export var monitorIndexToId = {}
export async function init(settings) {
useCache = true
prefsOpenedId = settings.connect(
'changed::prefs-opened',
() => (useCache = !settings.get_boolean('prefs-opened')),
)
await setMonitorsInfo(settings)
}
export async function disable(settings) {
settings.disconnect(prefsOpenedId)
}
export function clearCache(setting) {
if (setting) {
cache[setting] = null
@@ -33,8 +69,7 @@ export function clearCache(setting) {
/** Return object representing a settings value that is stored as JSON. */
export function getSettingsJson(settings, setting) {
try {
if (cache[setting])
return cache[setting]
if (useCache && cache[setting]) return cache[setting]
let res = JSON.parse(settings.get_string(setting))
@@ -56,13 +91,38 @@ export function setSettingsJson(settings, setting, value) {
}
}
// Previously, the monitor index was used as an id to persist per monitor
// settings. Since these indexes are unreliable AF, switch to use the monitor
// serial as its id while keeping it backward compatible.
function getMonitorSetting(settings, settingName, monitorIndex, fallback) {
let monitorId = monitorIndexToId[monitorIndex]
settings = getSettingsJson(settings, settingName)
return settings[monitorId] || settings[monitorIndex] || fallback
}
function setMonitorSetting(settings, settingName, monitorIndex, value) {
let monitorId = monitorIndexToId[monitorIndex]
let usedId = monitorId || monitorIndex
let currentSettings = getSettingsJson(settings, settingName)
if (monitorId) delete currentSettings[monitorIndex]
currentSettings[usedId] = value
setSettingsJson(settings, settingName, currentSettings)
}
/** Returns size of panel on a specific monitor, in pixels. */
export function getPanelSize(settings, monitorIndex) {
const sizes = getSettingsJson(settings, 'panel-sizes')
// Pull in deprecated setting if panel-sizes does not have setting for monitor.
const fallbackSize = settings.get_int('panel-size')
const theDefault = 48
return sizes[monitorIndex] || fallbackSize || theDefault
return getMonitorSetting(
settings,
'panel-sizes',
monitorIndex,
settings.get_int('panel-size') || 48,
)
}
export function setPanelSize(settings, monitorIndex, value) {
@@ -70,9 +130,8 @@ export function setPanelSize(settings, monitorIndex, value) {
console.log('Not setting invalid panel size: ' + value)
return
}
let sizes = getSettingsJson(settings, 'panel-sizes')
sizes[monitorIndex] = value
setSettingsJson(settings, 'panel-sizes', sizes)
setMonitorSetting(settings, 'panel-sizes', monitorIndex, value)
}
/**
@@ -80,9 +139,7 @@ export function setPanelSize(settings, monitorIndex, value) {
* from settings. e.g. 100
*/
export function getPanelLength(settings, monitorIndex) {
const lengths = getSettingsJson(settings, 'panel-lengths')
const theDefault = 100
return lengths[monitorIndex] || theDefault
return getMonitorSetting(settings, 'panel-lengths', monitorIndex, 100)
}
export function setPanelLength(settings, monitorIndex, value) {
@@ -90,17 +147,18 @@ export function setPanelLength(settings, monitorIndex, value) {
console.log('Not setting invalid panel length: ' + value)
return
}
let lengths = getSettingsJson(settings, 'panel-lengths')
lengths[monitorIndex] = value
setSettingsJson(settings, 'panel-lengths', lengths)
setMonitorSetting(settings, 'panel-lengths', monitorIndex, value)
}
/** Returns position of panel on a specific monitor. */
export function getPanelPosition(settings, monitorIndex) {
const positions = getSettingsJson(settings, 'panel-positions')
const fallbackPosition = settings.get_string('panel-position')
const theDefault = Pos.BOTTOM
return positions[monitorIndex] || fallbackPosition || theDefault
return getMonitorSetting(
settings,
'panel-positions',
monitorIndex,
settings.get_string('panel-position') || Pos.BOTTOM,
)
}
export function setPanelPosition(settings, monitorIndex, value) {
@@ -115,16 +173,13 @@ export function setPanelPosition(settings, monitorIndex, value) {
console.log('Not setting invalid panel position: ' + value)
return
}
const positions = getSettingsJson(settings, 'panel-positions')
positions[monitorIndex] = value
setSettingsJson(settings, 'panel-positions', positions)
setMonitorSetting(settings, 'panel-positions', monitorIndex, value)
}
/** Returns anchor location of panel on a specific monitor. */
export function getPanelAnchor(settings, monitorIndex) {
const anchors = getSettingsJson(settings, 'panel-anchors')
const theDefault = Pos.MIDDLE
return anchors[monitorIndex] || theDefault
return getMonitorSetting(settings, 'panel-anchors', monitorIndex, Pos.MIDDLE)
}
export function setPanelAnchor(settings, monitorIndex, value) {
@@ -132,7 +187,109 @@ export function setPanelAnchor(settings, monitorIndex, value) {
console.log('Not setting invalid panel anchor: ' + value)
return
}
const anchors = getSettingsJson(settings, 'panel-anchors')
anchors[monitorIndex] = value
setSettingsJson(settings, 'panel-anchors', anchors)
setMonitorSetting(settings, 'panel-anchors', monitorIndex, value)
}
export function getPanelElementPositions(settings, monitorIndex) {
return getMonitorSetting(
settings,
'panel-element-positions',
monitorIndex,
Pos.defaults,
)
}
export function setPanelElementPositions(settings, monitorIndex, value) {
setMonitorSetting(settings, 'panel-element-positions', monitorIndex, value)
}
export async function setMonitorsInfo(settings) {
return new Promise((resolve, reject) => {
try {
let monitorInfos = []
let saveMonitorState = (proxy) => {
proxy.GetCurrentStateRemote((displayInfo, e) => {
if (e) return reject(`Error getting display state: ${e}`)
let gsPrimaryIndex = 0
//https://gitlab.gnome.org/GNOME/mutter/-/blob/main/data/dbus-interfaces/org.gnome.Mutter.DisplayConfig.xml#L347
displayInfo[2].forEach((logicalMonitor, i) => {
let id = logicalMonitor[5][0][3]
let primary = logicalMonitor[4]
if (primary) gsPrimaryIndex = i
monitorInfos.push({
id,
name: logicalMonitor[5][0][2],
primary,
})
monitorIdToIndex[id] = i
monitorIndexToId[i] = id
})
_saveMonitors(settings, monitorInfos, gsPrimaryIndex)
resolve()
})
}
if (!displayConfigProxy)
displayConfigProxy = new displayConfigWrapper(
Gio.DBus.session,
'org.gnome.Mutter.DisplayConfig',
'/org/gnome/Mutter/DisplayConfig',
(proxy, e) => {
if (e) return reject(`Error creating display proxy: ${e}`)
saveMonitorState(proxy)
},
)
else saveMonitorState(displayConfigProxy)
} catch (e) {
reject(e)
}
})
}
function _saveMonitors(settings, monitorInfos, gsPrimaryIndex) {
let keyPrimary = 'primary-monitor'
let dtpPrimaryMonitor = settings.get_string(keyPrimary)
// convert previously saved index to monitor id
if (dtpPrimaryMonitor.match(/^\d{1,2}$/))
dtpPrimaryMonitor = monitorInfos[dtpPrimaryMonitor]?.id
// default to gnome-shell primary monitor
if (!dtpPrimaryMonitor) dtpPrimaryMonitor = monitorInfos[gsPrimaryIndex]?.id
settings.set_string(keyPrimary, dtpPrimaryMonitor)
availableMonitors = Object.freeze(monitorInfos)
}
// this is for backward compatibility, to remove in a few versions
export function adjustMonitorSettings(settings) {
let updateSettings = (settingName) => {
let monitorSettings = getSettingsJson(settings, settingName)
let updatedSettings = {}
Object.keys(monitorSettings).forEach((key) => {
let initialKey = key
if (key.match(/^\d{1,2}$/)) key = monitorIndexToId[key] || key
updatedSettings[key] = monitorSettings[initialKey]
})
setSettingsJson(settings, settingName, updatedSettings)
}
updateSettings('panel-sizes')
updateSettings('panel-lengths')
updateSettings('panel-positions')
updateSettings('panel-anchors')
updateSettings('panel-element-positions')
}

View File

@@ -290,7 +290,7 @@ const Preferences = class {
)
let topAvailable =
!keepTopPanel ||
(!monitorSync && this._currentMonitorIndex != this.monitors[0])
(!monitorSync && !this.monitors[this._currentMonitorIndex].primary)
let topRadio = this._builder.get_object('position_top_button')
topRadio.set_sensitive(topAvailable)
@@ -310,7 +310,7 @@ const Preferences = class {
'panel-element-positions-monitors-sync',
)
const monitorsToSetFor = monitorSync
? this.monitors
? Object.keys(this.monitors)
: [this._currentMonitorIndex]
monitorsToSetFor.forEach((monitorIndex) => {
PanelSettings.setPanelPosition(this._settings, monitorIndex, position)
@@ -447,18 +447,16 @@ const Preferences = class {
let labels = {}
let panelPosition = this._getPanelPosition(monitorIndex)
let isVertical = panelPosition == Pos.LEFT || panelPosition == Pos.RIGHT
let panelElementPositionsSettings = PanelSettings.getSettingsJson(
let panelElementPositions = PanelSettings.getPanelElementPositions(
this._settings,
'panel-element-positions',
monitorIndex,
)
let panelElementPositions =
panelElementPositionsSettings[monitorIndex] || Pos.defaults
let updateElementsSettings = () => {
let newPanelElementPositions = []
let monitorSync = this._settings.get_boolean(
'panel-element-positions-monitors-sync',
)
let monitors = monitorSync ? this.monitors : [monitorIndex]
let monitors = monitorSync ? Object.keys(this.monitors) : [monitorIndex]
let child = taskbarListBox.get_first_child()
while (child != null) {
@@ -470,13 +468,12 @@ const Preferences = class {
child = child.get_next_sibling()
}
monitors.forEach(
(m) => (panelElementPositionsSettings[m] = newPanelElementPositions),
)
PanelSettings.setSettingsJson(
this._settings,
'panel-element-positions',
panelElementPositionsSettings,
monitors.forEach((m) =>
PanelSettings.setPanelElementPositions(
this._settings,
m,
newPanelElementPositions,
),
)
}
@@ -1235,24 +1232,11 @@ const Preferences = class {
})
//multi-monitor
this.monitors = []
this.monitors = PanelSettings.availableMonitors
for (
let i = 0;
i < Gdk.Display.get_default().get_monitors().get_n_items();
++i
) {
this.monitors.push(i)
}
let primaryMonitorIndex = this._settings.get_int('gs-primary-monitor')
let dtpPrimaryMonitorIndex = this.monitors.indexOf(
this._settings.get_int('primary-monitor'),
)
if (dtpPrimaryMonitorIndex < 0) {
dtpPrimaryMonitorIndex = primaryMonitorIndex
}
let dtpPrimaryMonitorId = this._settings.get_string('primary-monitor')
let dtpPrimaryMonitorIndex =
PanelSettings.monitorIdToIndex[dtpPrimaryMonitorId]
this._currentMonitorIndex = dtpPrimaryMonitorIndex
@@ -1262,10 +1246,12 @@ const Preferences = class {
this._updateVerticalRelatedOptions()
for (let i = 0; i < this.monitors.length; ++i) {
let label =
i == primaryMonitorIndex
? _('Primary monitor')
: _('Monitor ') + (i + 1)
let monitor = this.monitors[i]
let label = monitor.primary
? _('Primary monitor')
: _('Monitor ') + (i + 1)
label += monitor.name ? ` (${monitor.name})` : ''
this._builder.get_object('multimon_primary_combo').append_text(label)
this._builder
@@ -1306,16 +1292,16 @@ const Preferences = class {
this._builder
.get_object('multimon_primary_combo')
.connect('changed', (widget) => {
this._settings.set_int(
this._settings.set_string(
'primary-monitor',
this.monitors[widget.get_active()],
this.monitors[widget.get_active()].id,
)
})
this._builder
.get_object('taskbar_position_monitor_combo')
.connect('changed', (widget) => {
this._currentMonitorIndex = this.monitors[widget.get_active()]
this._currentMonitorIndex = widget.get_active()
this._updateWidgetSettingsForMonitor(this._currentMonitorIndex)
})
@@ -1337,7 +1323,7 @@ const Preferences = class {
'panel-element-positions-monitors-sync',
)
const monitorsToSetFor = monitorSync
? this.monitors
? Object.keys(this.monitors)
: [this._currentMonitorIndex]
monitorsToSetFor.forEach((monitorIndex) => {
PanelSettings.setPanelLength(this._settings, monitorIndex, value)
@@ -1356,7 +1342,7 @@ const Preferences = class {
'panel-element-positions-monitors-sync',
)
const monitorsToSetFor = monitorSync
? this.monitors
? Object.keys(this.monitors)
: [this._currentMonitorIndex]
monitorsToSetFor.forEach((monitorIndex) => {
PanelSettings.setPanelAnchor(this._settings, monitorIndex, value)
@@ -3861,7 +3847,9 @@ const BuilderScope = GObject.registerClass(
)
export default class DashToPanelPreferences extends ExtensionPreferences {
fillPreferencesWindow(window) {
async fillPreferencesWindow(window) {
let closeRequestId = null
window._settings = this.getSettings(
'org.gnome.shell.extensions.dash-to-panel',
)
@@ -3869,6 +3857,14 @@ export default class DashToPanelPreferences extends ExtensionPreferences {
// use default width or window
window.set_default_size(0, 740)
window._settings.set_boolean('prefs-opened', true)
closeRequestId = window.connect('close-request', () => {
window._settings.set_boolean('prefs-opened', false)
window.disconnect(closeRequestId)
})
await PanelSettings.setMonitorsInfo(window._settings)
new Preferences(window, window._settings, this.path)
}
}

View File

@@ -1732,10 +1732,10 @@ export const TaskbarItemContainer = GObject.registerClass(
else return
let panelPosition = this._dtpPanel.getPosition()
let panelElementPositions =
this._dtpPanel.panelManager.panelsElementPositions[
this._dtpPanel.monitor.index
] || Pos.defaults
let panelElementPositions = PanelSettings.getPanelElementPositions(
SETTINGS,
this._dtpPanel.monitor.index,
)
let taskbarPosition = panelElementPositions.filter(
(pos) => pos.element == 'taskbar',
)[0].position