5b1f7704a6
The new logical dimensions are reported in the overlay, rather than the pixel dimensions. That is: if scaleFactor is 2, a window might be resized to 2400×1350 device pixels, which will be reported as 1200×675 in the overlay. This is consistent with (for example) the DevTools in Chrome, which reports the logical size of the viewport when you resize the window, rather than the physical pixel size. Tested with a freely-resizable window and with a constrained-geometry window (GNOME Terminal), on a hidpi display. https://bugzilla.gnome.org/show_bug.cgi?id=754607
158 lines
5.2 KiB
JavaScript
158 lines
5.2 KiB
JavaScript
/* Screenshot Window Sizer for Gnome Shell
|
||
*
|
||
* Copyright (c) 2013 Owen Taylor <otaylor@redhat.com>
|
||
* Copyright (c) 2013 Richard Hughes <richard@hughsie.com>
|
||
*
|
||
* This program is free software; you can redistribute it and/or
|
||
* modify it under the terms of the GNU General Public License
|
||
* as published by the Free Software Foundation; either version 2
|
||
* of the License, or (at your option) any later version.
|
||
*
|
||
* This program is distributed in the hope that it will be useful,
|
||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
* GNU General Public License for more details.
|
||
*
|
||
* You should have received a copy of the GNU General Public License
|
||
* along with this program; if not, write to the Free Software
|
||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||
*/
|
||
|
||
const Gio = imports.gi.Gio;
|
||
const Meta = imports.gi.Meta;
|
||
const Shell = imports.gi.Shell;
|
||
const St = imports.gi.St;
|
||
|
||
const Main = imports.ui.main;
|
||
const Tweener = imports.ui.tweener;
|
||
|
||
const ExtensionUtils = imports.misc.extensionUtils;
|
||
const Me = ExtensionUtils.getCurrentExtension();
|
||
const Convenience = Me.imports.convenience;
|
||
|
||
const MESSAGE_FADE_TIME = 2;
|
||
|
||
let text, button;
|
||
|
||
function hideMessage() {
|
||
text.destroy();
|
||
text = null;
|
||
}
|
||
|
||
function flashMessage(message) {
|
||
if (!text) {
|
||
text = new St.Label({ style_class: 'screenshot-sizer-message' });
|
||
Main.uiGroup.add_actor(text);
|
||
}
|
||
|
||
Tweener.removeTweens(text);
|
||
text.text = message;
|
||
|
||
text.opacity = 255;
|
||
|
||
let monitor = Main.layoutManager.primaryMonitor;
|
||
text.set_position(monitor.x + Math.floor(monitor.width / 2 - text.width / 2),
|
||
monitor.y + Math.floor(monitor.height / 2 - text.height / 2));
|
||
|
||
Tweener.addTween(text,
|
||
{ opacity: 0,
|
||
time: MESSAGE_FADE_TIME,
|
||
transition: 'easeOutQuad',
|
||
onComplete: hideMessage });
|
||
}
|
||
|
||
let SIZES = [
|
||
[624, 351],
|
||
[800, 450],
|
||
[1024, 576],
|
||
[1200, 675],
|
||
[1600, 900]
|
||
];
|
||
|
||
function cycleScreenshotSizes(display, screen, window, binding) {
|
||
// Probably this isn't useful with 5 sizes, but you can decrease instead
|
||
// of increase by holding down shift.
|
||
let modifiers = binding.get_modifiers();
|
||
let backwards = (modifiers & Meta.VirtualModifier.SHIFT_MASK) != 0;
|
||
|
||
// Unmaximize first
|
||
if (window.maximized_horizontally || window.maximizedVertically)
|
||
window.unmaximize(Meta.MaximizeFlags.HORIZONTAL | Meta.MaximizeFlags.VERTICAL);
|
||
|
||
let workArea = window.get_work_area_current_monitor();
|
||
let outerRect = window.get_frame_rect();
|
||
|
||
// Double both axes if on a hidpi display
|
||
let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
|
||
let scaledSizes = SIZES.map(function(size) {
|
||
return size.map(function(wh) {
|
||
return wh * scaleFactor;
|
||
});
|
||
});
|
||
|
||
// Find the nearest 16:9 size for the current window size
|
||
let nearestIndex;
|
||
let nearestError;
|
||
|
||
for (let i = 0; i < scaledSizes.length; i++) {
|
||
let [width, height] = scaledSizes[i];
|
||
|
||
// ignore sizes bigger than the workArea
|
||
if (width > workArea.width || height > workArea.height)
|
||
continue;
|
||
|
||
// get the best initial window size
|
||
let error = Math.abs(width - outerRect.width) + Math.abs(height - outerRect.height);
|
||
if (nearestIndex == null || error < nearestError) {
|
||
nearestIndex = i;
|
||
nearestError = error;
|
||
}
|
||
}
|
||
|
||
// get the next size up or down from ideal
|
||
let newIndex = (nearestIndex + (backwards ? -1 : 1)) % scaledSizes.length;
|
||
let newWidth, newHeight;
|
||
[newWidth, newHeight] = scaledSizes[newIndex];
|
||
if (newWidth > workArea.width || newHeight > workArea.height)
|
||
[newWidth, newHeight] = scaledSizes[0];
|
||
|
||
// Push the window onscreen if it would be resized offscreen
|
||
let newX = outerRect.x;
|
||
let newY = outerRect.y;
|
||
if (newX + newWidth > workArea.x + workArea.width)
|
||
newX = Math.max(workArea.x + workArea.width - newWidth);
|
||
if (newY + newHeight > workArea.y + workArea.height)
|
||
newY = Math.max(workArea.y + workArea.height - newHeight);
|
||
|
||
window.move_resize_frame(true, newX, newY, newWidth, newHeight);
|
||
|
||
let newOuterRect = window.get_frame_rect();
|
||
let message = '%d×%d'.format(
|
||
(newOuterRect.width / scaleFactor),
|
||
(newOuterRect.height / scaleFactor));
|
||
|
||
// The new size might have been constrained by geometry hints (e.g. for
|
||
// a terminal) - in that case, include the actual ratio to the message
|
||
// we flash
|
||
let actualNumerator = (newOuterRect.width / newOuterRect.height) * 9;
|
||
if (Math.abs(actualNumerator - 16) > 0.01)
|
||
message += ' (%.2f:9)'.format(actualNumerator);
|
||
|
||
flashMessage(message);
|
||
}
|
||
|
||
function init() {
|
||
}
|
||
|
||
function enable() {
|
||
Main.wm.addKeybinding('cycle-screenshot-sizes',
|
||
Convenience.getSettings(),
|
||
Meta.KeyBindingFlags.PER_WINDOW | Meta.KeyBindingFlags.REVERSES,
|
||
Shell.ActionMode.NORMAL,
|
||
cycleScreenshotSizes);
|
||
}
|
||
|
||
function disable() {
|
||
Main.wm.removeKeybinding('cycle-screenshot-sizes');
|
||
}
|