From 9bdfca4660ac61ddac745f485001f8bea0d1ce19 Mon Sep 17 00:00:00 2001 From: Zoey Ahmed Date: Sat, 20 Dec 2025 19:27:41 +0000 Subject: [PATCH 1/8] screenshot: Introduce keyboard nav in selection mode Similar to resizing windows using the arrow keys in shell, this lets the user change the size of the selection rectangle in the screenshot overlay, using arrow keys, by controlling the direction in a single plane, anchored around a the last changed side, changing the side when an arrow key of a different corresponding key is pressed. Closes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/8729 --- js/ui/screenshot.js | 83 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/js/ui/screenshot.js b/js/ui/screenshot.js index e0ec8b76ac..cdfc007f99 100644 --- a/js/ui/screenshot.js +++ b/js/ui/screenshot.js @@ -250,6 +250,8 @@ class UIAreaIndicator extends St.Widget { } }); +const SELECTION_KEYBOARD_INCREMENT = 5; + const UIAreaSelector = GObject.registerClass({ Signals: {'drag-started': {}, 'drag-ended': {}}, }, class UIAreaSelector extends St.Widget { @@ -298,6 +300,9 @@ const UIAreaSelector = GObject.registerClass({ this._lastX = 0; this._lastY = 0; + this._currentSide = St.DirectionType.LEFT; + this._lastResizeDirection = St.DirectionType.LEFT; + this.reset(); } @@ -331,6 +336,82 @@ const UIAreaSelector = GObject.registerClass({ } } + _resizeInDirection(direction) { + let newStartX = this._startX; + let newStartY = this._startY; + let newLastX = this._lastX; + let newLastY = this._lastY; + + // Either move the current side of the area selected in the corresponding + // direction to the key just pressed if the pressed key is on the same plane, + // or switch to the opposite plane. + switch (direction) { + case St.DirectionType.LEFT: + if (this._lastResizeDirection === St.DirectionType.UP || + this._lastResizeDirection === St.DirectionType.DOWN) + this._currentSide = direction; + + if (this._currentSide === St.DirectionType.LEFT) + newStartX -= SELECTION_KEYBOARD_INCREMENT; + else if (this._currentSide === St.DirectionType.RIGHT) + newLastX -= SELECTION_KEYBOARD_INCREMENT; + break; + + case St.DirectionType.RIGHT: + if (this._lastResizeDirection === St.DirectionType.UP || + this._lastResizeDirection === St.DirectionType.DOWN) + this._currentSide = direction; + + if (this._currentSide === St.DirectionType.LEFT) + newStartX += SELECTION_KEYBOARD_INCREMENT; + else if (this._currentSide === St.DirectionType.RIGHT) + newLastX += SELECTION_KEYBOARD_INCREMENT; + break; + + case St.DirectionType.UP: + if (this._lastResizeDirection === St.DirectionType.LEFT || + this._lastResizeDirection === St.DirectionType.RIGHT) + this._currentSide = direction; + + if (this._currentSide === St.DirectionType.UP) + newStartY -= SELECTION_KEYBOARD_INCREMENT; + else if (this._currentSide === St.DirectionType.DOWN) + newLastY -= SELECTION_KEYBOARD_INCREMENT; + break; + + case St.DirectionType.DOWN: + if (this._lastResizeDirection === St.DirectionType.LEFT || + this._lastResizeDirection === St.DirectionType.RIGHT) + this._currentSide = direction; + + if (this._currentSide === St.DirectionType.UP) + newStartY += SELECTION_KEYBOARD_INCREMENT; + else if (this._currentSide === St.DirectionType.DOWN) + newLastY += SELECTION_KEYBOARD_INCREMENT; + break; + } + + // Ensure new resized area does not go off the stage edge. + if (newStartX < 0) + newStartX = 0; + else if (newLastX > this.width - 1) + newLastX = this.width - 1; + + if (newStartY < 0) + newStartY = 0; + else if (newLastY > this.height - 1) + newLastY = this.height - 1; + + // Update selection rectangle props. + this._startX = newStartX; + this._startY = newStartY; + this._lastX = newLastX; + this._lastY = newLastY; + this._lastResizeDirection = direction; + + this._updateSelectionRect(); + } + getGeometry() { const leftX = Math.min(this._startX, this._lastX); const topY = Math.min(this._startY, this._lastY); @@ -2215,6 +2296,8 @@ export const ScreenshotUI = GObject.registerClass({ const screen = this._screenSelectors.find(selector => selector.checked) ?? null; this.navigate_focus(screen, direction, false); + } else if (this._selectionButton.checked) { + this._areaSelector._resizeInDirection(direction); } return Clutter.EVENT_STOP; -- GitLab From 60f09323aba1148bc533043efee6252b261edec3 Mon Sep 17 00:00:00 2001 From: Zoey Ahmed Date: Sat, 20 Dec 2025 19:44:44 +0000 Subject: [PATCH 2/8] screenshot: Reset area on "R" key pressed Similar to other screenshot shortcuts, on selection mode, reset the area of the rectangle back to the center of screen when "R" key is pressed. This makes keyboard navigation of the selection mode easier. --- js/ui/screenshot.js | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/js/ui/screenshot.js b/js/ui/screenshot.js index cdfc007f99..0176d1628d 100644 --- a/js/ui/screenshot.js +++ b/js/ui/screenshot.js @@ -321,19 +321,23 @@ const UIAreaSelector = GObject.registerClass({ this._lastX = 0; this._lastY = 0; - // This can happen when running headless without any monitors. - if (Main.layoutManager.primaryIndex !== -1) { - const monitor = - Main.layoutManager.monitors[Main.layoutManager.primaryIndex]; - - this._startX = monitor.x + Math.floor(monitor.width * 3 / 8); - this._startY = monitor.y + Math.floor(monitor.height * 3 / 8); - this._lastX = monitor.x + Math.floor(monitor.width * 5 / 8) - 1; - this._lastY = monitor.y + Math.floor(monitor.height * 5 / 8) - 1; - } + this._resetArea(); + } + } - this._updateSelectionRect(); + _resetArea() { + // This can called when running headless without any monitors. + if (Main.layoutManager.primaryIndex !== -1) { + const monitor = + Main.layoutManager.monitors[Main.layoutManager.primaryIndex]; + + this._startX = monitor.x + Math.floor(monitor.width * 3 / 8); + this._startY = monitor.y + Math.floor(monitor.height * 3 / 8); + this._lastX = monitor.x + Math.floor(monitor.width * 5 / 8) - 1; + this._lastY = monitor.y + Math.floor(monitor.height * 5 / 8) - 1; } + + this._updateSelectionRect(); } _resizeInDirection(direction) { @@ -2275,6 +2279,12 @@ export const ScreenshotUI = GObject.registerClass({ return Clutter.EVENT_STOP; } + if (this._selectionButton.checked && + (symbol === Clutter.KEY_r || symbol === Clutter.KEY_R)) { + this._areaSelector._resetArea(); + return Clutter.EVENT_STOP; + } + if (symbol === Clutter.KEY_Left || symbol === Clutter.KEY_Right || symbol === Clutter.KEY_Up || symbol === Clutter.KEY_Down) { let direction; -- GitLab From f6f0974871e4ad5d24fed8680a1e3b48f2cc44ef Mon Sep 17 00:00:00 2001 From: Zoey Ahmed Date: Mon, 5 Jan 2026 22:21:25 +0000 Subject: [PATCH 3/8] screenshot: Announce area selection changes to Orca The screen-reader now reads out when the area is reset or resized using keybinds --- js/ui/screenshot.js | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/js/ui/screenshot.js b/js/ui/screenshot.js index 0176d1628d..b3eeb8941d 100644 --- a/js/ui/screenshot.js +++ b/js/ui/screenshot.js @@ -1,3 +1,4 @@ +import Atk from 'gi://Atk'; import Clutter from 'gi://Clutter'; import Cogl from 'gi://Cogl'; import Gio from 'gi://Gio'; @@ -406,14 +407,33 @@ const UIAreaSelector = GObject.registerClass({ else if (newLastY > this.height - 1) newLastY = this.height - 1; - // Update selection rectangle props. + // Update selection rectangle props, then announce + // change of direction to screen reader. this._startX = newStartX; this._startY = newStartY; this._lastX = newLastX; this._lastY = newLastY; this._lastResizeDirection = direction; - this._updateSelectionRect(); + + let directionString = ''; + switch (direction) { + case St.DirectionType.LEFT: + directionString = _('Left'); + break; + case St.DirectionType.RIGHT: + directionString = _('Right'); + break; + case St.DirectionType.UP: + directionString = _('Up'); + break; + case St.DirectionType.DOWN: + directionString = _('Down'); + break; + } + + const message = _('Resize Selection Area %s'.format(directionString)); + this.get_accessible().emit('notification', message, Atk.Live.ASSERTIVE); } getGeometry() { @@ -2282,6 +2302,7 @@ export const ScreenshotUI = GObject.registerClass({ if (this._selectionButton.checked && (symbol === Clutter.KEY_r || symbol === Clutter.KEY_R)) { this._areaSelector._resetArea(); + this.get_accessible().emit('notification', _('Reset Selection Area'), Atk.Live.ASSERTIVE); return Clutter.EVENT_STOP; } -- GitLab From fb673218c23c9f9f9caf209ed47ed5f5dd5964bc Mon Sep 17 00:00:00 2001 From: Zoey Ahmed Date: Tue, 20 Jan 2026 00:38:51 +0000 Subject: [PATCH 4/8] screenshot: Cap all X/Y values to be in bounds This ensures that that selection area does not go out of bounds while using arrow keys to resize, when the selection rubberbands over iself. --- js/ui/screenshot.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/js/ui/screenshot.js b/js/ui/screenshot.js index b3eeb8941d..00e8698af4 100644 --- a/js/ui/screenshot.js +++ b/js/ui/screenshot.js @@ -399,11 +399,21 @@ const UIAreaSelector = GObject.registerClass({ // Ensure new resized area does not go off the stage edge. if (newStartX < 0) newStartX = 0; + else if (newStartX > this.width - 1) + newStartX = this.width - 1; + + if (newLastX < 0) + newLastX = 0; else if (newLastX > this.width - 1) newLastX = this.width - 1; if (newStartY < 0) newStartY = 0; + else if (newStartY > this.height - 1) + newStartY = this.height - 1; + + if (newLastY < 0) + newLastY = 0; else if (newLastY > this.height - 1) newLastY = this.height - 1; -- GitLab From 8b993c15cbe41065bf39ade68ed210bc7624215d Mon Sep 17 00:00:00 2001 From: Zoey Ahmed Date: Tue, 20 Jan 2026 01:43:18 +0000 Subject: [PATCH 5/8] screenshot: Move selection area via keyboard Area is moved when first modifier key (left alt in most cases) is pressed alongside the arrow keys. The area moves directly in the arrow direction pressed, and does not consider the previous directions, copying the behaviour of moving windows in Gnome-Shell using the arrow keys. --- js/ui/screenshot.js | 78 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 77 insertions(+), 1 deletion(-) diff --git a/js/ui/screenshot.js b/js/ui/screenshot.js index 00e8698af4..8b1cea7415 100644 --- a/js/ui/screenshot.js +++ b/js/ui/screenshot.js @@ -341,6 +341,78 @@ const UIAreaSelector = GObject.registerClass({ this._updateSelectionRect(); } + _moveInDirection(direction) { + const [,, selectionWidth, selectionHeight] = this.getGeometry(); + + let newStartX = this._startX; + let newStartY = this._startY; + let newLastX = this._lastX; + let newLastY = this._lastY; + + switch (direction) { + case St.DirectionType.LEFT: + newStartX -= SELECTION_KEYBOARD_INCREMENT; + newLastX -= SELECTION_KEYBOARD_INCREMENT; + break; + case St.DirectionType.RIGHT: + newStartX += SELECTION_KEYBOARD_INCREMENT; + newLastX += SELECTION_KEYBOARD_INCREMENT; + break; + case St.DirectionType.UP: + newStartY -= SELECTION_KEYBOARD_INCREMENT; + newLastY -= SELECTION_KEYBOARD_INCREMENT; + break; + case St.DirectionType.DOWN: + newStartY += SELECTION_KEYBOARD_INCREMENT; + newLastY += SELECTION_KEYBOARD_INCREMENT; + break; + } + + // Ensure area does not move off the stage edge. + if (newStartX < 0) { + newStartX = 0; + newLastX = newStartX + (selectionWidth - 1); + } else if (newLastX > this.width - 1) { + newLastX = this.width - 1; + newStartX = newLastX - (selectionWidth - 1); + } + + if (newStartY < 0) { + newStartY = 0; + newLastY = newStartY + (selectionHeight - 1); + } else if (newLastY > this.height - 1) { + newLastY = this.height - 1; + newStartY = newLastY - (selectionHeight - 1); + } + // Update selection rectangle props, then announce + // move direction to screen reader. + + this._startX = newStartX; + this._startY = newStartY; + this._lastX = newLastX; + this._lastY = newLastY; + + this._updateSelectionRect(); + let directionString = ''; + switch (direction) { + case St.DirectionType.LEFT: + directionString = _('Left'); + break; + case St.DirectionType.RIGHT: + directionString = _('Right'); + break; + case St.DirectionType.UP: + directionString = _('Up'); + break; + case St.DirectionType.DOWN: + directionString = _('Down'); + break; + } + + const message = _('Move Selection %s'.format(directionString)); + this.get_accessible().emit('notification', message, Atk.Live.ASSERTIVE); + } + _resizeInDirection(direction) { let newStartX = this._startX; let newStartY = this._startY; @@ -2338,7 +2410,11 @@ export const ScreenshotUI = GObject.registerClass({ this._screenSelectors.find(selector => selector.checked) ?? null; this.navigate_focus(screen, direction, false); } else if (this._selectionButton.checked) { - this._areaSelector._resizeInDirection(direction); + const [pressed,,] = event.get_key_state(); + if (pressed & Clutter.ModifierType.MOD1_MASK) + this._areaSelector._moveInDirection(direction); + else + this._areaSelector._resizeInDirection(direction); } return Clutter.EVENT_STOP; -- GitLab From 7b00fa6b44d46f668d943dfe8043a6d1cf126363 Mon Sep 17 00:00:00 2001 From: Zoey Ahmed Date: Thu, 29 Jan 2026 13:48:47 +0000 Subject: [PATCH 6/8] screenshot: Change increment when Ctrl/Shift pressed Holding Ctrl will move the box or the current side in the given direction by 1 pixel. Holding Shift will cause the box or current side to go to the end of the screen in the inputted direction. This requires changing _resizeInDirection and _moveInDirection to take the increment as another param. --- js/ui/screenshot.js | 54 ++++++++++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/js/ui/screenshot.js b/js/ui/screenshot.js index 8b1cea7415..e82f0613ec 100644 --- a/js/ui/screenshot.js +++ b/js/ui/screenshot.js @@ -251,6 +251,7 @@ class UIAreaIndicator extends St.Widget { } }); +const CTRL_SELECTION_KEYBOARD_INCREMENT = 1; const SELECTION_KEYBOARD_INCREMENT = 5; const UIAreaSelector = GObject.registerClass({ @@ -341,7 +342,7 @@ const UIAreaSelector = GObject.registerClass({ this._updateSelectionRect(); } - _moveInDirection(direction) { + _moveInDirection(direction, increment) { const [,, selectionWidth, selectionHeight] = this.getGeometry(); let newStartX = this._startX; @@ -351,20 +352,20 @@ const UIAreaSelector = GObject.registerClass({ switch (direction) { case St.DirectionType.LEFT: - newStartX -= SELECTION_KEYBOARD_INCREMENT; - newLastX -= SELECTION_KEYBOARD_INCREMENT; + newStartX -= increment; + newLastX -= increment; break; case St.DirectionType.RIGHT: - newStartX += SELECTION_KEYBOARD_INCREMENT; - newLastX += SELECTION_KEYBOARD_INCREMENT; + newStartX += increment; + newLastX += increment; break; case St.DirectionType.UP: - newStartY -= SELECTION_KEYBOARD_INCREMENT; - newLastY -= SELECTION_KEYBOARD_INCREMENT; + newStartY -= increment; + newLastY -= increment; break; case St.DirectionType.DOWN: - newStartY += SELECTION_KEYBOARD_INCREMENT; - newLastY += SELECTION_KEYBOARD_INCREMENT; + newStartY += increment; + newLastY += increment; break; } @@ -413,7 +414,7 @@ const UIAreaSelector = GObject.registerClass({ this.get_accessible().emit('notification', message, Atk.Live.ASSERTIVE); } - _resizeInDirection(direction) { + _resizeInDirection(direction, increment) { let newStartX = this._startX; let newStartY = this._startY; let newLastX = this._lastX; @@ -429,9 +430,9 @@ const UIAreaSelector = GObject.registerClass({ this._currentSide = direction; if (this._currentSide === St.DirectionType.LEFT) - newStartX -= SELECTION_KEYBOARD_INCREMENT; + newStartX -= increment; else if (this._currentSide === St.DirectionType.RIGHT) - newLastX -= SELECTION_KEYBOARD_INCREMENT; + newLastX -= increment; break; case St.DirectionType.RIGHT: @@ -440,9 +441,9 @@ const UIAreaSelector = GObject.registerClass({ this._currentSide = direction; if (this._currentSide === St.DirectionType.LEFT) - newStartX += SELECTION_KEYBOARD_INCREMENT; + newStartX += increment; else if (this._currentSide === St.DirectionType.RIGHT) - newLastX += SELECTION_KEYBOARD_INCREMENT; + newLastX += increment; break; case St.DirectionType.UP: @@ -451,9 +452,9 @@ const UIAreaSelector = GObject.registerClass({ this._currentSide = direction; if (this._currentSide === St.DirectionType.UP) - newStartY -= SELECTION_KEYBOARD_INCREMENT; + newStartY -= increment; else if (this._currentSide === St.DirectionType.DOWN) - newLastY -= SELECTION_KEYBOARD_INCREMENT; + newLastY -= increment; break; case St.DirectionType.DOWN: @@ -462,9 +463,9 @@ const UIAreaSelector = GObject.registerClass({ this._currentSide = direction; if (this._currentSide === St.DirectionType.UP) - newStartY += SELECTION_KEYBOARD_INCREMENT; + newStartY += increment; else if (this._currentSide === St.DirectionType.DOWN) - newLastY += SELECTION_KEYBOARD_INCREMENT; + newLastY += increment; break; } @@ -2411,10 +2412,23 @@ export const ScreenshotUI = GObject.registerClass({ this.navigate_focus(screen, direction, false); } else if (this._selectionButton.checked) { const [pressed,,] = event.get_key_state(); + + let increment; + if (pressed & Clutter.ModifierType.CONTROL_MASK) { + increment = CTRL_SELECTION_KEYBOARD_INCREMENT; + } else if (pressed & Clutter.ModifierType.SHIFT_MASK) { + if (direction === St.DirectionType.LEFT || direction === St.DirectionType.RIGHT) + increment = Main.layoutManager.monitors[Main.layoutManager.primaryIndex].width; + else if (direction === St.DirectionType.DOWN || direction === St.DirectionType.UP) + increment = Main.layoutManager.monitors[Main.layoutManager.primaryIndex].width; + } else { + increment = SELECTION_KEYBOARD_INCREMENT; + } + if (pressed & Clutter.ModifierType.MOD1_MASK) - this._areaSelector._moveInDirection(direction); + this._areaSelector._moveInDirection(direction, increment); else - this._areaSelector._resizeInDirection(direction); + this._areaSelector._resizeInDirection(direction, increment); } return Clutter.EVENT_STOP; -- GitLab From 3768bb2597e0f3f76e016a08225b98db7a083818 Mon Sep 17 00:00:00 2001 From: Zoey Ahmed Date: Fri, 30 Jan 2026 13:40:23 +0000 Subject: [PATCH 7/8] screenshot: Move cursor on selection kbd move Introduce two new methods to attach the cursor to either a given side, or in the center of the selection area, and another method to attach the cursor to the new direction when the perpendicular direction the user is resizing changes. _resizeByArea also now first checks if the new direction is in the opposite perpendicular direction to the previous resize direction. If so, the cursor is updated to the new direction, and it leaves early, leaving the selection size unchanged. This makes the behaviour of the cursor similar to resizing windows via the cursor. --- js/ui/screenshot.js | 118 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 97 insertions(+), 21 deletions(-) diff --git a/js/ui/screenshot.js b/js/ui/screenshot.js index e82f0613ec..012b54be19 100644 --- a/js/ui/screenshot.js +++ b/js/ui/screenshot.js @@ -342,6 +342,71 @@ const UIAreaSelector = GObject.registerClass({ this._updateSelectionRect(); } + _attachCursorToCenter() { + const [leftX, topY, selectionWidth, selectionHeight] = this.getGeometry(); + const [rightX, bottomY] = [leftX + selectionWidth - 1, topY + selectionHeight - 1]; + const seat = global.stage.context.get_backend().get_default_seat(); + const cursorX = ((rightX - leftX) / 2) + leftX; + const cursorY = ((topY - bottomY) / 2) + bottomY; + + seat.warp_pointer(cursorX, cursorY); + this._updateCursor(cursorX, cursorY); + } + + _attachCursorToSide(direction) { + const seat = global.stage.context.get_backend().get_default_seat(); + const [leftX, topY, width, height] = this.getGeometry(); + const [rightX, bottomY] = [leftX + width - 1, topY + height - 1]; + let cursorX, cursorY; + + switch (direction) { + case St.DirectionType.LEFT: + cursorX = leftX; + cursorY = ((topY - bottomY) / 2) + bottomY; + break; + case St.DirectionType.RIGHT: + cursorX = rightX; + cursorY = ((topY - bottomY) / 2) + bottomY; + break; + case St.DirectionType.UP: + cursorX = ((rightX - leftX) / 2) + leftX; + cursorY = topY; + break; + case St.DirectionType.DOWN: + cursorX = ((rightX - leftX) / 2) + leftX; + cursorY = bottomY; + break; + } + seat.warp_pointer(cursorX, cursorY); + this._updateCursor(cursorX, cursorY); + } + + _attachCursor(direction) { + this._attachCursorToSide(direction); + this._currentSide = direction; + this._lastResizeDirection = direction; + + // Announce change of attached side to screen reader. + let directionString = ''; + switch (direction) { + case St.DirectionType.LEFT: + directionString = _('Left'); + break; + case St.DirectionType.RIGHT: + directionString = _('Right'); + break; + case St.DirectionType.UP: + directionString = _('Up'); + break; + case St.DirectionType.DOWN: + directionString = _('Down'); + break; + } + + const message = _('Attach Selection %s'.format(directionString)); + this.get_accessible().emit('notification', message, Atk.Live.ASSERTIVE); + } + _moveInDirection(direction, increment) { const [,, selectionWidth, selectionHeight] = this.getGeometry(); @@ -412,23 +477,44 @@ const UIAreaSelector = GObject.registerClass({ const message = _('Move Selection %s'.format(directionString)); this.get_accessible().emit('notification', message, Atk.Live.ASSERTIVE); + + // Update cursor to center of the selection rectangle + this._attachCursorToCenter(); } _resizeInDirection(direction, increment) { + // If the direction is in the opposite plane to the last direction, + // and position cursor to new side. Return early so the first press + // in the new direction does not resize the selection. + if ((this._lastResizeDirection === St.DirectionType.UP || + this._lastResizeDirection === St.DirectionType.DOWN) && + direction === St.DirectionType.LEFT) { + this._attachCursor(direction); + return; + } else if ((this._lastResizeDirection === St.DirectionType.UP || + this._lastResizeDirection === St.DirectionType.DOWN) && + direction === St.DirectionType.RIGHT) { + this._attachCursor(direction); + return; + } else if ((this._lastResizeDirection === St.DirectionType.LEFT || + this._lastResizeDirection === St.DirectionType.RIGHT) && + direction === St.DirectionType.UP) { + this._attachCursor(direction); + return; + } else if ((this._lastResizeDirection === St.DirectionType.LEFT || + this._lastResizeDirection === St.DirectionType.RIGHT) && + direction === St.DirectionType.DOWN) { + this._attachCursor(direction); + return; + } + let newStartX = this._startX; let newStartY = this._startY; let newLastX = this._lastX; let newLastY = this._lastY; - // Either move the current side of the area selected in the corresponding - // direction to the key just pressed if the pressed key is on the same plane, - // or switch to the opposite plane. switch (direction) { case St.DirectionType.LEFT: - if (this._lastResizeDirection === St.DirectionType.UP || - this._lastResizeDirection === St.DirectionType.DOWN) - this._currentSide = direction; - if (this._currentSide === St.DirectionType.LEFT) newStartX -= increment; else if (this._currentSide === St.DirectionType.RIGHT) @@ -436,10 +522,6 @@ const UIAreaSelector = GObject.registerClass({ break; case St.DirectionType.RIGHT: - if (this._lastResizeDirection === St.DirectionType.UP || - this._lastResizeDirection === St.DirectionType.DOWN) - this._currentSide = direction; - if (this._currentSide === St.DirectionType.LEFT) newStartX += increment; else if (this._currentSide === St.DirectionType.RIGHT) @@ -447,10 +529,6 @@ const UIAreaSelector = GObject.registerClass({ break; case St.DirectionType.UP: - if (this._lastResizeDirection === St.DirectionType.LEFT || - this._lastResizeDirection === St.DirectionType.RIGHT) - this._currentSide = direction; - if (this._currentSide === St.DirectionType.UP) newStartY -= increment; else if (this._currentSide === St.DirectionType.DOWN) @@ -458,10 +536,6 @@ const UIAreaSelector = GObject.registerClass({ break; case St.DirectionType.DOWN: - if (this._lastResizeDirection === St.DirectionType.LEFT || - this._lastResizeDirection === St.DirectionType.RIGHT) - this._currentSide = direction; - if (this._currentSide === St.DirectionType.UP) newStartY += increment; else if (this._currentSide === St.DirectionType.DOWN) @@ -490,8 +564,8 @@ const UIAreaSelector = GObject.registerClass({ else if (newLastY > this.height - 1) newLastY = this.height - 1; - // Update selection rectangle props, then announce - // change of direction to screen reader. + // Update selection rectangle props and cursor position/type, then + // announce change of direction to screen reader. this._startX = newStartX; this._startY = newStartY; this._lastX = newLastX; @@ -499,6 +573,8 @@ const UIAreaSelector = GObject.registerClass({ this._lastResizeDirection = direction; this._updateSelectionRect(); + this._attachCursorToSide(this._currentSide); + let directionString = ''; switch (direction) { case St.DirectionType.LEFT: -- GitLab From 21a417e9c9bf5b1cf324b7114b8318bf4cbdf88a Mon Sep 17 00:00:00 2001 From: Zoey Ahmed Date: Fri, 30 Jan 2026 14:29:36 +0000 Subject: [PATCH 8/8] screenshot: Add method to announce changes via Orca _announceSelectionChanges() is used when the area is resized, moved or attached, to announce a short message then the direction of the change. --- js/ui/screenshot.js | 48 +++++++++------------------------------------ 1 file changed, 9 insertions(+), 39 deletions(-) diff --git a/js/ui/screenshot.js b/js/ui/screenshot.js index 012b54be19..5d2adab4e4 100644 --- a/js/ui/screenshot.js +++ b/js/ui/screenshot.js @@ -387,6 +387,11 @@ const UIAreaSelector = GObject.registerClass({ this._lastResizeDirection = direction; // Announce change of attached side to screen reader. + this._announceSelectionChanges('Attach To Side', direction); + } + + _announceSelectionChanges(message, direction) { + // Announce change of attached side to screen . let directionString = ''; switch (direction) { case St.DirectionType.LEFT: @@ -403,8 +408,8 @@ const UIAreaSelector = GObject.registerClass({ break; } - const message = _('Attach Selection %s'.format(directionString)); - this.get_accessible().emit('notification', message, Atk.Live.ASSERTIVE); + const announcedMessage = _('%s %s'.format(message, directionString)); + this.get_accessible().emit('notification', announcedMessage, Atk.Live.ASSERTIVE); } _moveInDirection(direction, increment) { @@ -457,26 +462,9 @@ const UIAreaSelector = GObject.registerClass({ this._startY = newStartY; this._lastX = newLastX; this._lastY = newLastY; - this._updateSelectionRect(); - let directionString = ''; - switch (direction) { - case St.DirectionType.LEFT: - directionString = _('Left'); - break; - case St.DirectionType.RIGHT: - directionString = _('Right'); - break; - case St.DirectionType.UP: - directionString = _('Up'); - break; - case St.DirectionType.DOWN: - directionString = _('Down'); - break; - } - const message = _('Move Selection %s'.format(directionString)); - this.get_accessible().emit('notification', message, Atk.Live.ASSERTIVE); + this._announceSelectionChanges('Move Selection', direction); // Update cursor to center of the selection rectangle this._attachCursorToCenter(); @@ -574,25 +562,7 @@ const UIAreaSelector = GObject.registerClass({ this._updateSelectionRect(); this._attachCursorToSide(this._currentSide); - - let directionString = ''; - switch (direction) { - case St.DirectionType.LEFT: - directionString = _('Left'); - break; - case St.DirectionType.RIGHT: - directionString = _('Right'); - break; - case St.DirectionType.UP: - directionString = _('Up'); - break; - case St.DirectionType.DOWN: - directionString = _('Down'); - break; - } - - const message = _('Resize Selection Area %s'.format(directionString)); - this.get_accessible().emit('notification', message, Atk.Live.ASSERTIVE); + this._announceSelectionChanges('Resize Selection Area', direction); } getGeometry() { -- GitLab