Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,14 @@
<default><![CDATA[['']]]></default>
<summary>Focus the window prior to the current focused window</summary>
</key>
<key type="as" name="shift-focus-window-next">
<default><![CDATA[['']]]></default>
<summary>Switch next window with focused window</summary>
</key>
<key type="as" name="shift-focus-window-prev">
<default><![CDATA[['']]]></default>
<summary>Switch previous window with focused window</summary>
</key>
<key type="as" name="highlight-current-window">
<default><![CDATA[['']]]></default>
<summary>Minimize all the other windows and show only the focused window</summary>
Expand Down
110 changes: 96 additions & 14 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,17 @@ export default class TilingShellExtension extends Extension {
this._onKeyboardFocusWinDirection(dp, dir);
},
);
this._signals.connect(
this._keybindings,
'shift-focus-window',
(
kb: KeyBindings,
dp: Meta.Display,
dir: FocusSwitchDirection,
) => {
this._onKeyboardFocusSwitchWin(dp, dir);
},
);
this._signals.connect(
this._keybindings,
'highlight-current-window',
Expand Down Expand Up @@ -649,7 +660,7 @@ export default class TilingShellExtension extends Extension {
bestWindow.activate(global.get_current_time());
}

private _onKeyboardFocusWin(
private _getWindowsAndFocusAdjacent(
display: Meta.Display,
direction: FocusSwitchDirection,
) {
Expand All @@ -661,36 +672,107 @@ export default class TilingShellExtension extends Extension {
(focus_window.get_wm_class() &&
focus_window.get_wm_class() === 'gjs')
)
return;
return {};

const windowList = filterUnfocusableWindows(
focus_window.get_workspace().list_windows(),
focus_window
.get_workspace()
.list_windows()
.sort((a, b) => {
// goal is to have a stable ordering based on windows placement
// This is "clock-like" -- not perfect, but roughly quadrant-ordered
const aRect = a.get_frame_rect();
const bRect = b.get_frame_rect();
return (
bRect.y - aRect.y ||
aRect.x - bRect.x || // reversed
bRect.width - aRect.width ||
bRect.height - aRect.height ||
b.get_stable_sequence() - a.get_stable_sequence()
);
}),
);
if (windowList.length < 2) return {};

const focusParent = focus_window.get_transient_for() || focus_window;
const focusedIdx = windowList.findIndex((win) => {
// in case we are iterating over a modal dialog for our focused window
return win === focusParent;
});

let nextIndex = -1;
// default to staying same place
let nextIdx = focusedIdx;
switch (direction) {
case FocusSwitchDirection.PREV:
if (focusedIdx === 0 && Settings.WRAPAROUND_FOCUS) {
windowList[windowList.length - 1].activate(
global.get_current_time(),
);
if (focusedIdx === 0) {
if (Settings.WRAPAROUND_FOCUS)
nextIdx = windowList.length - 1;
} else {
windowList[focusedIdx - 1].activate(
global.get_current_time(),
);
nextIdx = focusedIdx - 1;
}
break;
case FocusSwitchDirection.NEXT:
nextIndex = (focusedIdx + 1) % windowList.length;
if (nextIndex > 0 || Settings.WRAPAROUND_FOCUS)
windowList[nextIndex].activate(global.get_current_time());
nextIdx = (focusedIdx + 1) % windowList.length;
if (nextIdx === 0 && !Settings.WRAPAROUND_FOCUS)
nextIdx = focusedIdx;
break;
}
if (focusedIdx === nextIdx) return {};

return {
focus_window,
windowList,
focusedIdx,
nextIdx,
};
}

private _onKeyboardFocusWin(
display: Meta.Display,
direction: FocusSwitchDirection,
) {
const focusWindowsContext = this._getWindowsAndFocusAdjacent(
display,
direction,
);
if (!focusWindowsContext.focus_window) return;

focusWindowsContext.windowList[focusWindowsContext.nextIdx].activate(
global.get_current_time(),
);
}

private _onKeyboardFocusSwitchWin(
display: Meta.Display,
direction: FocusSwitchDirection,
) {
const focusWindowsContext = this._getWindowsAndFocusAdjacent(
display,
direction,
);
if (!focusWindowsContext.focus_window) return;

const nextWin =
focusWindowsContext.windowList[focusWindowsContext.nextIdx];
const focusRect = focusWindowsContext.focus_window.get_frame_rect();
const nextRect = nextWin.get_frame_rect();

nextWin.move_resize_frame(
false,
focusRect.x,
focusRect.y,
focusRect.width,
focusRect.height,
);
focusWindowsContext.focus_window.move_resize_frame(
true, // based on user action
nextRect.x,
nextRect.y,
nextRect.width,
nextRect.height,
);

focusWindowsContext.focus_window.activate(global.get_current_time());
}

private _onKeyboardUntileWindow(kb: KeyBindings, display: Meta.Display) {
Expand Down
35 changes: 35 additions & 0 deletions src/keybindings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ export default class KeyBindings extends GObject.Object {
'focus-window': {
param_types: [Meta.Display.$gtype, GObject.TYPE_INT], // Meta.Display, FocusSwitchDirection
},
'shift-focus-window': {
param_types: [Meta.Display.$gtype, GObject.TYPE_INT], // Meta.Display, FocusSwitchDirection
},
'highlight-current-window': {
param_types: [Meta.Display.$gtype], // Meta.Display
},
Expand Down Expand Up @@ -232,6 +235,34 @@ export default class KeyBindings extends GObject.Object {
},
);

Main.wm.addKeybinding(
Settings.SETTING_SHIFT_FOCUS_WINDOW_NEXT,
extensionSettings,
Meta.KeyBindingFlags.NONE,
Shell.ActionMode.NORMAL,
(display: Meta.Display) => {
this.emit(
'shift-focus-window',
display,
FocusSwitchDirection.NEXT,
);
},
);

Main.wm.addKeybinding(
Settings.SETTING_SHIFT_FOCUS_WINDOW_PREV,
extensionSettings,
Meta.KeyBindingFlags.NONE,
Shell.ActionMode.NORMAL,
(display: Meta.Display) => {
this.emit(
'shift-focus-window',
display,
FocusSwitchDirection.PREV,
);
},
);

Main.wm.addKeybinding(
Settings.SETTING_HIGHLIGHT_CURRENT_WINDOW,
extensionSettings,
Expand Down Expand Up @@ -347,7 +378,11 @@ export default class KeyBindings extends GObject.Object {
Main.wm.removeKeybinding(Settings.SETTING_FOCUS_WINDOW_RIGHT);
Main.wm.removeKeybinding(Settings.SETTING_FOCUS_WINDOW_NEXT);
Main.wm.removeKeybinding(Settings.SETTING_FOCUS_WINDOW_PREV);
Main.wm.removeKeybinding(Settings.SETTING_SHIFT_FOCUS_WINDOW_NEXT);
Main.wm.removeKeybinding(Settings.SETTING_SHIFT_FOCUS_WINDOW_PREV);

Main.wm.removeKeybinding(Settings.SETTING_HIGHLIGHT_CURRENT_WINDOW);

Main.wm.removeKeybinding(Settings.SETTING_CYCLE_LAYOUTS);
}

Expand Down
14 changes: 14 additions & 0 deletions src/prefs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -683,6 +683,20 @@ export default class TilingShellExtensionPreferences extends ExtensionPreference
false,
false,
],
[
Settings.SETTING_SHIFT_FOCUS_WINDOW_NEXT,
_('Switch next window with focused window'),
_('Flip the next window with focused window'),
false,
false,
],
[
Settings.SETTING_SHIFT_FOCUS_WINDOW_PREV,
_('Switch previous window with focused window'),
_('Flip the previous window with focused window'),
false,
false,
],
[
Settings.SETTING_HIGHLIGHT_CURRENT_WINDOW,
_('Highlight focused window'),
Expand Down
2 changes: 2 additions & 0 deletions src/settings/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ export default class Settings {
static SETTING_FOCUS_WINDOW_DOWN = 'focus-window-down';
static SETTING_FOCUS_WINDOW_NEXT = 'focus-window-next';
static SETTING_FOCUS_WINDOW_PREV = 'focus-window-prev';
static SETTING_SHIFT_FOCUS_WINDOW_NEXT = 'shift-focus-window-next';
static SETTING_SHIFT_FOCUS_WINDOW_PREV = 'shift-focus-window-prev';
static SETTING_HIGHLIGHT_CURRENT_WINDOW = 'highlight-current-window';
static SETTING_CYCLE_LAYOUTS = 'cycle-layouts';

Expand Down