unite extension tweak
This commit is contained in:
@@ -0,0 +1,444 @@
|
||||
const Bytes = imports.byteArray
|
||||
const GLib = imports.gi.GLib
|
||||
const GObject = imports.gi.GObject
|
||||
const Meta = imports.gi.Meta
|
||||
const Main = imports.ui.main
|
||||
const Util = imports.misc.util
|
||||
const Unite = imports.misc.extensionUtils.getCurrentExtension()
|
||||
const AppMenu = Main.panel.statusArea.appMenu
|
||||
const Handlers = Unite.imports.handlers
|
||||
|
||||
const VALID_TYPES = [
|
||||
Meta.WindowType.NORMAL,
|
||||
Meta.WindowType.DIALOG,
|
||||
Meta.WindowType.MODAL_DIALOG,
|
||||
Meta.WindowType.UTILITY
|
||||
]
|
||||
|
||||
const UNITE_HINTS = '_UNITE_ORIGINAL_STATE'
|
||||
const MOTIF_HINTS = '_MOTIF_WM_HINTS'
|
||||
|
||||
const _SHOW_FLAGS = ['0x2', '0x0', '0x1', '0x0', '0x0']
|
||||
const _HIDE_FLAGS = ['0x2', '0x0', '0x2', '0x0', '0x0']
|
||||
|
||||
function isValid(win) {
|
||||
return win && VALID_TYPES.includes(win.window_type)
|
||||
}
|
||||
|
||||
function getXid(win) {
|
||||
const desc = win.get_description()
|
||||
const match = desc && desc.match(/0x[0-9a-f]+/)
|
||||
|
||||
return match && match[0]
|
||||
}
|
||||
|
||||
function getHint(xid, name, fallback) {
|
||||
const result = GLib.spawn_command_line_sync(`xprop -id ${xid} ${name}`)
|
||||
const string = Bytes.toString(result[1])
|
||||
|
||||
if (!string.match(/=/)) {
|
||||
return fallback
|
||||
}
|
||||
|
||||
return string.split('=')[1].trim().split(',').map(part => {
|
||||
part = part.trim()
|
||||
return part.match(/\dx/) ? part : `0x${part}`
|
||||
})
|
||||
}
|
||||
|
||||
function setHint(xid, hint, value) {
|
||||
value = value.join(', ')
|
||||
Util.spawn(['xprop', '-id', xid, '-f', hint, '32c', '-set', hint, value])
|
||||
}
|
||||
|
||||
function getHints(xid) {
|
||||
let value = getHint(xid, UNITE_HINTS)
|
||||
|
||||
if (!value) {
|
||||
value = getHint(xid, MOTIF_HINTS, _SHOW_FLAGS)
|
||||
setHint(xid, UNITE_HINTS, value)
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
function isDecorated(hints) {
|
||||
return hints[2] != '0x2' && hints[2] != '0x0'
|
||||
}
|
||||
|
||||
var ClientDecorations = class ClientDecorations {
|
||||
constructor(xid) {
|
||||
this.xid = xid
|
||||
}
|
||||
|
||||
show() {
|
||||
return false
|
||||
}
|
||||
|
||||
hide() {
|
||||
return false
|
||||
}
|
||||
|
||||
reset() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
var ServerDecorations = class ServerDecorations {
|
||||
constructor(xid) {
|
||||
this.xid = xid
|
||||
this.initial = getHints(xid)
|
||||
this.current = this.initial
|
||||
}
|
||||
|
||||
get decorated() {
|
||||
return isDecorated(this.current)
|
||||
}
|
||||
|
||||
get handle() {
|
||||
return isDecorated(this.initial)
|
||||
}
|
||||
|
||||
show() {
|
||||
if (this.handle && !this.decorated) {
|
||||
this.current = _SHOW_FLAGS
|
||||
setHint(this.xid, MOTIF_HINTS, _SHOW_FLAGS)
|
||||
}
|
||||
}
|
||||
|
||||
hide() {
|
||||
if (this.handle && this.decorated) {
|
||||
this.current = _HIDE_FLAGS
|
||||
setHint(this.xid, MOTIF_HINTS, _HIDE_FLAGS)
|
||||
}
|
||||
}
|
||||
|
||||
reset() {
|
||||
if (this.handle) {
|
||||
setHint(this.xid, MOTIF_HINTS, this.initial)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var MetaWindow = GObject.registerClass(
|
||||
class UniteMetaWindow extends GObject.Object {
|
||||
_init(win) {
|
||||
win._uniteShellManaged = true
|
||||
|
||||
this.win = win
|
||||
this.xid = getXid(win)
|
||||
|
||||
this.signals = new Handlers.Signals()
|
||||
this.settings = new Handlers.Settings()
|
||||
|
||||
if (this.xid && !this.clientDecorated) {
|
||||
this.decorations = new ServerDecorations(this.xid)
|
||||
} else {
|
||||
this.decorations = new ClientDecorations(this.xid)
|
||||
}
|
||||
|
||||
this.signals.connect(
|
||||
win, 'size-changed', this._onStateChanged.bind(this)
|
||||
)
|
||||
|
||||
this.signals.connect(
|
||||
win, 'notify::title', this._onTitleChanged.bind(this)
|
||||
)
|
||||
|
||||
this.settings.connect(
|
||||
'restrict-to-primary-screen', this.syncComponents.bind(this)
|
||||
)
|
||||
|
||||
this.settings.connect(
|
||||
'hide-window-titlebars', this.syncDecorations.bind(this)
|
||||
)
|
||||
|
||||
this.settings.connect(
|
||||
'show-window-buttons', this.syncControls.bind(this)
|
||||
)
|
||||
|
||||
this.settings.connect(
|
||||
'show-window-title', this.syncAppmenu.bind(this)
|
||||
)
|
||||
|
||||
this.syncComponents()
|
||||
}
|
||||
|
||||
get hasFocus() {
|
||||
return this.win.has_focus()
|
||||
}
|
||||
|
||||
get title() {
|
||||
if (this.showTitle) {
|
||||
return this.win.get_title()
|
||||
} else {
|
||||
return AppMenu._targetApp.get_name()
|
||||
}
|
||||
}
|
||||
|
||||
get clientDecorated() {
|
||||
return this.win.is_client_decorated()
|
||||
}
|
||||
|
||||
get primaryScreen() {
|
||||
return this.win.is_on_primary_monitor()
|
||||
}
|
||||
|
||||
get minimized() {
|
||||
return this.win.minimized
|
||||
}
|
||||
|
||||
get maximized() {
|
||||
return this.win.maximized_horizontally && this.win.maximized_vertically
|
||||
}
|
||||
|
||||
get tiled() {
|
||||
if (this.maximized) {
|
||||
return false
|
||||
} else {
|
||||
return this.win.maximized_horizontally || this.win.maximized_vertically
|
||||
}
|
||||
}
|
||||
|
||||
get bothMaximized() {
|
||||
return this.maximized || this.tiled
|
||||
}
|
||||
|
||||
get restrictToPrimary() {
|
||||
return this.settings.get('restrict-to-primary-screen')
|
||||
}
|
||||
|
||||
get handleScreen() {
|
||||
return this.primaryScreen || !this.restrictToPrimary
|
||||
}
|
||||
|
||||
get showTitle() {
|
||||
return this._parseEnumSetting('show-window-title')
|
||||
}
|
||||
|
||||
get showButtons() {
|
||||
return this._parseEnumSetting('show-window-buttons')
|
||||
}
|
||||
|
||||
get hideTitlebars() {
|
||||
return this._parseEnumSetting('hide-window-titlebars')
|
||||
}
|
||||
|
||||
minimize() {
|
||||
if (this.minimized) {
|
||||
this.win.unminimize()
|
||||
} else {
|
||||
this.win.minimize()
|
||||
}
|
||||
}
|
||||
|
||||
maximize() {
|
||||
if (this.maximized) {
|
||||
this.win.unmaximize(Meta.MaximizeFlags.BOTH)
|
||||
} else {
|
||||
this.win.maximize(Meta.MaximizeFlags.BOTH)
|
||||
}
|
||||
}
|
||||
|
||||
close() {
|
||||
const time = global.get_current_time()
|
||||
time && this.win.delete(time)
|
||||
}
|
||||
|
||||
syncDecorations() {
|
||||
if (this.hideTitlebars) {
|
||||
this.decorations.hide()
|
||||
} else {
|
||||
this.decorations.show()
|
||||
}
|
||||
}
|
||||
|
||||
syncControls() {
|
||||
if (this.hasFocus) {
|
||||
const controls = Main.panel.statusArea.uniteWindowControls
|
||||
controls && controls.setVisible(this.showButtons)
|
||||
}
|
||||
}
|
||||
|
||||
syncAppmenu() {
|
||||
if (this.hasFocus) {
|
||||
const current = AppMenu._label.get_text()
|
||||
current != this.title && AppMenu._label.set_text(this.title)
|
||||
}
|
||||
}
|
||||
|
||||
syncComponents() {
|
||||
this.syncDecorations()
|
||||
this.syncControls()
|
||||
this.syncAppmenu()
|
||||
}
|
||||
|
||||
_parseEnumSetting(name) {
|
||||
switch (this.settings.get(name)) {
|
||||
case 'always': return true
|
||||
case 'never': return false
|
||||
case 'tiled': return this.handleScreen && this.tiled
|
||||
case 'maximized': return this.handleScreen && this.maximized
|
||||
case 'both': return this.handleScreen && this.bothMaximized
|
||||
}
|
||||
}
|
||||
|
||||
_onStateChanged() {
|
||||
this.syncComponents()
|
||||
}
|
||||
|
||||
_onTitleChanged() {
|
||||
this.syncAppmenu()
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.decorations.reset()
|
||||
|
||||
this.signals.disconnectAll()
|
||||
this.settings.disconnectAll()
|
||||
|
||||
this.win._uniteShellManaged = false
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
var WindowManager = GObject.registerClass(
|
||||
class UniteWindowManager extends GObject.Object {
|
||||
_init() {
|
||||
this.windows = new Map()
|
||||
this.signals = new Handlers.Signals()
|
||||
this.settings = new Handlers.Settings()
|
||||
this.styles = new Handlers.Styles()
|
||||
|
||||
this.signals.connect(
|
||||
global.window_manager, 'map', this._onMapWindow.bind(this)
|
||||
)
|
||||
|
||||
this.signals.connect(
|
||||
global.window_manager, 'destroy', this._onDestroyWindow.bind(this)
|
||||
)
|
||||
|
||||
this.signals.connect(
|
||||
global.display, 'notify::focus-window', this._onFocusWindow.bind(this)
|
||||
)
|
||||
|
||||
this.signals.connect(
|
||||
global.display, 'window-demands-attention', this._onAttention.bind(this)
|
||||
)
|
||||
|
||||
this.signals.connect(
|
||||
AppMenu._label, 'notify::text', this._onAppmenuChanged.bind(this)
|
||||
)
|
||||
|
||||
this.settings.connect(
|
||||
'hide-window-titlebars', this._onStylesChange.bind(this)
|
||||
)
|
||||
|
||||
this.settings.connect(
|
||||
'window-buttons-position', this._onStylesChange.bind(this)
|
||||
)
|
||||
}
|
||||
|
||||
get focusWindow() {
|
||||
const win = global.display.get_focus_window()
|
||||
return this.getWindow(win)
|
||||
}
|
||||
|
||||
get hideTitlebars() {
|
||||
return this.settings.get('hide-window-titlebars')
|
||||
}
|
||||
|
||||
hasWindow(win) {
|
||||
return win && this.windows.has(`${win}`)
|
||||
}
|
||||
|
||||
getWindow(win) {
|
||||
return win && this.windows.get(`${win}`)
|
||||
}
|
||||
|
||||
setWindow(win) {
|
||||
if (!this.hasWindow(win)) {
|
||||
const meta = new MetaWindow(win)
|
||||
this.windows.set(`${win}`, meta)
|
||||
}
|
||||
}
|
||||
|
||||
deleteWindow(win) {
|
||||
if (this.hasWindow(win)) {
|
||||
const meta = this.getWindow(win)
|
||||
meta.destroy()
|
||||
|
||||
this.windows.delete(`${win}`)
|
||||
}
|
||||
}
|
||||
|
||||
clearWindows() {
|
||||
for (const key of this.windows.keys()) {
|
||||
this.deleteWindow(key)
|
||||
}
|
||||
}
|
||||
|
||||
_onMapWindow(shellwm, { meta_window }) {
|
||||
if (isValid(meta_window)) {
|
||||
this.setWindow(meta_window)
|
||||
}
|
||||
}
|
||||
|
||||
_onDestroyWindow(shellwm, { meta_window }) {
|
||||
if (isValid(meta_window)) {
|
||||
this.deleteWindow(meta_window)
|
||||
}
|
||||
}
|
||||
|
||||
_onFocusWindow(display) {
|
||||
if (this.focusWindow) {
|
||||
this.focusWindow.syncComponents()
|
||||
}
|
||||
}
|
||||
|
||||
_onAppmenuChanged() {
|
||||
const focused = this.focusWindow
|
||||
const current = AppMenu._label.get_text()
|
||||
|
||||
if (focused && current != focused.title) {
|
||||
AppMenu._label.set_text(focused.title)
|
||||
}
|
||||
}
|
||||
|
||||
_onAttention(actor, win) {
|
||||
const auto = this.settings.get('autofocus-windows')
|
||||
const time = global.get_current_time()
|
||||
|
||||
auto && Main.activateWindow(win, time)
|
||||
}
|
||||
|
||||
_onStylesChange() {
|
||||
if (this.hideTitlebars != 'never') {
|
||||
const variant = this.settings.get('window-buttons-position')
|
||||
const folder = `${Unite.path}/styles/buttons-${variant}`
|
||||
const content = `@import url('${folder}/${this.hideTitlebars}.css');`
|
||||
|
||||
this.styles.addGtkStyle('windowDecorations', content)
|
||||
} else {
|
||||
this.styles.deleteStyle('windowDecorations')
|
||||
}
|
||||
}
|
||||
|
||||
activate() {
|
||||
GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
|
||||
const actors = global.get_window_actors()
|
||||
actors.forEach(actor => this._onMapWindow(null, actor))
|
||||
})
|
||||
|
||||
this._onStylesChange()
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.clearWindows()
|
||||
|
||||
this.signals.disconnectAll()
|
||||
this.settings.disconnectAll()
|
||||
this.styles.removeAll()
|
||||
}
|
||||
}
|
||||
)
|
Reference in New Issue
Block a user