Add Zoom on Focus shortcut support

This commit is contained in:
wheaney 2025-08-20 17:05:50 -07:00
parent 7200ac95f5
commit 1f5e3b1d3f
9 changed files with 174 additions and 89 deletions

View File

@ -17,5 +17,10 @@
<max>250</max> <max>250</max>
<label>All Displays Distance</label> <label>All Displays Distance</label>
</entry> </entry>
<entry name="ZoomOnFocusEnabled" type="Bool">
<default>false</default>
<label>Zoom on Focus Enabled</label>
<description>Enable zooming in on the focused display.</description>
</entry>
</group> </group>
</kcfg> </kcfg>

View File

@ -2,3 +2,4 @@ File=breezydesktopconfig.kcfg
ClassName=BreezyDesktopConfig ClassName=BreezyDesktopConfig
Singleton=true Singleton=true
Mutators=true Mutators=true
Notifiers=true

View File

@ -82,6 +82,10 @@ BreezyDesktopEffect::BreezyDesktopEffect()
BreezyShortcuts::RECENTER, BreezyShortcuts::RECENTER,
[this]() { this->recenter(); } [this]() { this->recenter(); }
); );
setupGlobalShortcut(
BreezyShortcuts::TOGGLE_ZOOM_ON_FOCUS,
[this]() { this->toggleZoomOnFocus(); }
);
connect(effects, &EffectsHandler::cursorShapeChanged, this, &BreezyDesktopEffect::updateCursorImage); connect(effects, &EffectsHandler::cursorShapeChanged, this, &BreezyDesktopEffect::updateCursorImage);
updateCursorImage(); updateCursorImage();
@ -127,14 +131,7 @@ void BreezyDesktopEffect::setupGlobalShortcut(const BreezyShortcuts::Shortcut &s
action->setText(shortcut.actionText); action->setText(shortcut.actionText);
KGlobalAccel::self()->setDefaultShortcut(action, {shortcut.shortcut}); KGlobalAccel::self()->setDefaultShortcut(action, {shortcut.shortcut});
KGlobalAccel::self()->setShortcut(action, {shortcut.shortcut}); KGlobalAccel::self()->setShortcut(action, {shortcut.shortcut});
QList<QKeySequence> shortcutKeys = KGlobalAccel::self()->shortcut(action);
connect(action, &QAction::triggered, this, triggeredFunc); connect(action, &QAction::triggered, this, triggeredFunc);
connect(KGlobalAccel::self(), &KGlobalAccel::globalShortcutChanged, this, [this, shortcut, &shortcutKeys](QAction *action, const QKeySequence &seq) {
if (action->objectName() == shortcut.actionName) {
shortcutKeys.clear();
shortcutKeys.append(seq);
}
});
} }
void BreezyDesktopEffect::reconfigure(ReconfigureFlags) void BreezyDesktopEffect::reconfigure(ReconfigureFlags)
@ -142,6 +139,7 @@ void BreezyDesktopEffect::reconfigure(ReconfigureFlags)
BreezyDesktopConfig::self()->read(); BreezyDesktopConfig::self()->read();
setFocusedDisplayDistance(BreezyDesktopConfig::focusedDisplayDistance() / 100.0f); setFocusedDisplayDistance(BreezyDesktopConfig::focusedDisplayDistance() / 100.0f);
setAllDisplaysDistance(BreezyDesktopConfig::allDisplaysDistance() / 100.0f); setAllDisplaysDistance(BreezyDesktopConfig::allDisplaysDistance() / 100.0f);
setZoomOnFocusEnabled(BreezyDesktopConfig::zoomOnFocusEnabled());
} }
QVariantMap BreezyDesktopEffect::initialProperties(Output *screen) QVariantMap BreezyDesktopEffect::initialProperties(Output *screen)
@ -222,6 +220,11 @@ void BreezyDesktopEffect::recenter()
} }
} }
void BreezyDesktopEffect::toggleZoomOnFocus()
{
setZoomOnFocusEnabled(!m_zoomOnFocusEnabled);
}
void BreezyDesktopEffect::addVirtualDisplay(QSize size) void BreezyDesktopEffect::addVirtualDisplay(QSize size)
{ {
// QSize size(2560, 1440); // QSize size(2560, 1440);
@ -241,6 +244,19 @@ bool BreezyDesktopEffect::isEnabled() const {
return m_enabled; return m_enabled;
} }
bool BreezyDesktopEffect::isZoomOnFocusEnabled() const {
return m_zoomOnFocusEnabled;
}
void BreezyDesktopEffect::setZoomOnFocusEnabled(bool enabled) {
if (m_zoomOnFocusEnabled != enabled) {
m_zoomOnFocusEnabled = enabled;
BreezyDesktopConfig::setZoomOnFocusEnabled(enabled);
BreezyDesktopConfig::self()->save();
Q_EMIT zoomOnFocusChanged();
}
}
bool BreezyDesktopEffect::imuResetState() const { bool BreezyDesktopEffect::imuResetState() const {
return m_imuResetState; return m_imuResetState;
} }

View File

@ -15,6 +15,7 @@ namespace KWin
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(bool isEnabled READ isEnabled NOTIFY enabledStateChanged) Q_PROPERTY(bool isEnabled READ isEnabled NOTIFY enabledStateChanged)
Q_PROPERTY(bool zoomOnFocusEnabled READ isZoomOnFocusEnabled WRITE setZoomOnFocusEnabled NOTIFY zoomOnFocusChanged)
Q_PROPERTY(bool imuResetState READ imuResetState NOTIFY imuRotationsChanged) Q_PROPERTY(bool imuResetState READ imuResetState NOTIFY imuRotationsChanged)
Q_PROPERTY(QList<QQuaternion> imuRotations READ imuRotations NOTIFY imuRotationsChanged) Q_PROPERTY(QList<QQuaternion> imuRotations READ imuRotations NOTIFY imuRotationsChanged)
Q_PROPERTY(quint32 imuTimeElapsedMs READ imuTimeElapsedMs NOTIFY imuRotationsChanged) Q_PROPERTY(quint32 imuTimeElapsedMs READ imuTimeElapsedMs NOTIFY imuRotationsChanged)
@ -42,6 +43,8 @@ namespace KWin
QPointF cursorPos() const; QPointF cursorPos() const;
bool isEnabled() const; bool isEnabled() const;
bool isZoomOnFocusEnabled() const;
void setZoomOnFocusEnabled(bool enabled);
QList<QQuaternion> imuRotations() const; QList<QQuaternion> imuRotations() const;
quint32 imuTimeElapsedMs() const; quint32 imuTimeElapsedMs() const;
quint64 imuTimestamp() const; quint64 imuTimestamp() const;
@ -65,6 +68,7 @@ namespace KWin
void deactivate(); void deactivate();
void toggle(); void toggle();
void recenter(); void recenter();
void toggleZoomOnFocus();
void addVirtualDisplay(QSize size); void addVirtualDisplay(QSize size);
void updateImuRotation(); void updateImuRotation();
void updateCursorImage(); void updateCursorImage();
@ -73,6 +77,7 @@ namespace KWin
Q_SIGNALS: Q_SIGNALS:
void displayDistanceChanged(); void displayDistanceChanged();
void enabledStateChanged(); void enabledStateChanged();
void zoomOnFocusChanged();
void imuRotationsChanged(); void imuRotationsChanged();
void cursorImageChanged(); void cursorImageChanged();
void cursorPosChanged(); void cursorPosChanged();
@ -91,6 +96,7 @@ namespace KWin
QString m_cursorImageSource; QString m_cursorImageSource;
bool m_enabled = false; bool m_enabled = false;
bool m_zoomOnFocusEnabled = false;
bool m_imuResetState; bool m_imuResetState;
QList<QQuaternion> m_imuRotations; QList<QQuaternion> m_imuRotations;
quint32 m_imuTimeElapsedMs; quint32 m_imuTimeElapsedMs;

View File

@ -7,12 +7,18 @@
#include <KActionCollection> #include <KActionCollection>
#include <KGlobalAccel> #include <KGlobalAccel>
#include <KLocalizedString> #include <KLocalizedString>
#include <KConfigWatcher>
#include <KSharedConfig>
#include <KPluginFactory> #include <KPluginFactory>
#include <QAction> #include <QAction>
#include <QFileDialog> #include <QFileDialog>
Q_LOGGING_CATEGORY(KWIN_XR, "kwin.xr")
static const char EFFECT_GROUP[] = "Effect-breezy_desktop_effect";
void addShortcutAction(KActionCollection *collection, const BreezyShortcuts::Shortcut &shortcut) void addShortcutAction(KActionCollection *collection, const BreezyShortcuts::Shortcut &shortcut)
{ {
QAction *action = collection->addAction(shortcut.actionName); QAction *action = collection->addAction(shortcut.actionName);
@ -29,6 +35,22 @@ BreezyDesktopEffectConfig::BreezyDesktopEffectConfig(QObject *parent, const KPlu
{ {
ui.setupUi(widget()); ui.setupUi(widget());
addConfig(BreezyDesktopConfig::self(), widget()); addConfig(BreezyDesktopConfig::self(), widget());
m_configWatcher = KConfigWatcher::create(BreezyDesktopConfig::self()->sharedConfig());
if (m_configWatcher) {
connect(m_configWatcher.data(), &KConfigWatcher::configChanged, this,
[this](const KConfigGroup &group, const QByteArrayList &names) {
if (m_updatingFromConfig) {
return;
}
if (group.name() != QLatin1String(EFFECT_GROUP)) {
return;
}
BreezyDesktopConfig::self()->read();
updateUiFromConfig();
updateUnmanagedState();
});
}
auto actionCollection = new KActionCollection(this, QStringLiteral("kwin")); auto actionCollection = new KActionCollection(this, QStringLiteral("kwin"));
actionCollection->setComponentDisplayName(i18n("KWin")); actionCollection->setComponentDisplayName(i18n("KWin"));
@ -37,6 +59,7 @@ BreezyDesktopEffectConfig::BreezyDesktopEffectConfig(QObject *parent, const KPlu
addShortcutAction(actionCollection, BreezyShortcuts::TOGGLE); addShortcutAction(actionCollection, BreezyShortcuts::TOGGLE);
addShortcutAction(actionCollection, BreezyShortcuts::RECENTER); addShortcutAction(actionCollection, BreezyShortcuts::RECENTER);
addShortcutAction(actionCollection, BreezyShortcuts::TOGGLE_ZOOM_ON_FOCUS);
ui.shortcutsEditor->addCollection(actionCollection); ui.shortcutsEditor->addCollection(actionCollection);
connect(ui.shortcutsEditor, &KShortcutsEditor::keyChange, this, &BreezyDesktopEffectConfig::markAsChanged); connect(ui.shortcutsEditor, &KShortcutsEditor::keyChange, this, &BreezyDesktopEffectConfig::markAsChanged);
connect(ui.kcfg_FocusedDisplayDistance, &QSlider::valueChanged, this, &BreezyDesktopEffectConfig::save); connect(ui.kcfg_FocusedDisplayDistance, &QSlider::valueChanged, this, &BreezyDesktopEffectConfig::save);
@ -56,9 +79,12 @@ void BreezyDesktopEffectConfig::load()
void BreezyDesktopEffectConfig::save() void BreezyDesktopEffectConfig::save()
{ {
// Prevent reacting to the file change we ourselves are about to write.
m_updatingFromConfig = true;
updateConfigFromUi(); updateConfigFromUi();
BreezyDesktopConfig::self()->save(); BreezyDesktopConfig::self()->save();
KCModule::save(); KCModule::save();
m_updatingFromConfig = false;
updateUnmanagedState(); updateUnmanagedState();
OrgKdeKwinEffectsInterface interface(QStringLiteral("org.kde.KWin"), QStringLiteral("/Effects"), QDBusConnection::sessionBus()); OrgKdeKwinEffectsInterface interface(QStringLiteral("org.kde.KWin"), QStringLiteral("/Effects"), QDBusConnection::sessionBus());
@ -79,6 +105,9 @@ void BreezyDesktopEffectConfig::updateConfigFromUi()
void BreezyDesktopEffectConfig::updateUiFromConfig() void BreezyDesktopEffectConfig::updateUiFromConfig()
{ {
ui.kcfg_FocusedDisplayDistance->setValue(BreezyDesktopConfig::self()->focusedDisplayDistance());
ui.kcfg_AllDisplaysDistance->setValue(BreezyDesktopConfig::self()->allDisplaysDistance());
ui.kcfg_ZoomOnFocusEnabled->setChecked(BreezyDesktopConfig::self()->zoomOnFocusEnabled());
} }
void BreezyDesktopEffectConfig::updateUiFromDefaultConfig() void BreezyDesktopEffectConfig::updateUiFromDefaultConfig()
@ -90,4 +119,4 @@ void BreezyDesktopEffectConfig::updateUnmanagedState()
{ {
} }
#include "breezydesktopeffectkcm.moc" #include "breezydesktopeffectkcm.moc"

View File

@ -7,9 +7,14 @@
#pragma once #pragma once
#include <KCModule> #include <KCModule>
#include <KConfigWatcher>
#include <memory>
#include "ui_breezydesktopeffectkcm.h" #include "ui_breezydesktopeffectkcm.h"
class KConfigWatcher;
class KConfigGroup;
class BreezyDesktopEffectConfig : public KCModule class BreezyDesktopEffectConfig : public KCModule
{ {
Q_OBJECT Q_OBJECT
@ -30,4 +35,7 @@ private:
void updateUnmanagedState(); void updateUnmanagedState();
::Ui::BreezyDesktopEffectConfig ui; ::Ui::BreezyDesktopEffectConfig ui;
KConfigWatcher::Ptr m_configWatcher;
bool m_updatingFromConfig = false;
}; };

View File

@ -21,76 +21,86 @@
<height>250</height> <height>250</height>
</size> </size>
</property> </property>
<layout class="QFormLayout" name="formLayout"> <layout class="QFormLayout" name="formLayout">
<item row="0" column="0"> <item row="0" column="0" colspan="2">
<widget class="QLabel" name="labelFocusedDisplayDistance"> <widget class="QCheckBox" name="kcfg_ZoomOnFocusEnabled">
<property name="text"> <property name="text">
<string>Focused Display Distance:</string> <string>Zoom on Focus</string>
</property> </property>
</widget> <property name="checked">
</item> <bool>false</bool>
<item row="0" column="1"> </property>
<widget class="QSlider" name="kcfg_FocusedDisplayDistance"> </widget>
<property name="minimum"> </item>
<double>20</double> <item row="1" column="0">
</property> <widget class="QLabel" name="labelFocusedDisplayDistance">
<property name="maximum"> <property name="text">
<double>250</double> <string>Focused Display Distance:</string>
</property> </property>
<property name="tickPosition"> </widget>
<enum>QSlider::TicksBelow</enum> </item>
</property> <item row="1" column="1">
<property name="tickInterval"> <widget class="QSlider" name="kcfg_FocusedDisplayDistance">
<double>20</double> <property name="minimum">
</property> <double>20</double>
<property name="orientation"> </property>
<enum>Qt::Horizontal</enum> <property name="maximum">
</property> <double>250</double>
<property name="tracking"> </property>
<bool>true</bool> <property name="tickPosition">
</property> <enum>QSlider::TicksBelow</enum>
</widget> </property>
</item> <property name="tickInterval">
<item row="1" column="0"> <double>20</double>
<widget class="QLabel" name="labelAllDisplaysDistance"> </property>
<property name="text"> <property name="orientation">
<string>All Displays Distance:</string> <enum>Qt::Horizontal</enum>
</property> </property>
</widget> <property name="tracking">
</item> <bool>true</bool>
<item row="1" column="1"> </property>
<widget class="QSlider" name="kcfg_AllDisplaysDistance"> </widget>
<property name="minimum"> </item>
<double>20</double> <item row="2" column="0">
</property> <widget class="QLabel" name="labelAllDisplaysDistance">
<property name="maximum"> <property name="text">
<double>250</double> <string>All Displays Distance:</string>
</property> </property>
<property name="tickPosition"> </widget>
<enum>QSlider::TicksBelow</enum> </item>
</property> <item row="2" column="1">
<property name="tickInterval"> <widget class="QSlider" name="kcfg_AllDisplaysDistance">
<double>20</double> <property name="minimum">
</property> <double>20</double>
<property name="orientation"> </property>
<enum>Qt::Horizontal</enum> <property name="maximum">
</property> <double>250</double>
<property name="tracking"> </property>
<bool>true</bool> <property name="tickPosition">
</property> <enum>QSlider::TicksBelow</enum>
</widget> </property>
</item> <property name="tickInterval">
<item row="2" column="0" colspan="2"> <double>20</double>
<widget class="KShortcutsEditor" name="shortcutsEditor" native="true"> </property>
<property name="sizePolicy"> <property name="orientation">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred"> <enum>Qt::Horizontal</enum>
<horstretch>0</horstretch> </property>
<verstretch>0</verstretch> <property name="tracking">
</sizepolicy> <bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
</layout> <item row="3" column="0" colspan="2">
<widget class="KShortcutsEditor" name="shortcutsEditor" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</widget> </widget>
<customwidgets> <customwidgets>
<customwidget> <customwidget>

View File

@ -22,4 +22,10 @@ namespace BreezyShortcuts {
QStringLiteral("Recenter"), QStringLiteral("Recenter"),
QStringLiteral("Recenter") QStringLiteral("Recenter")
}; };
const Shortcut TOGGLE_ZOOM_ON_FOCUS = {
Qt::CTRL | Qt::META | Qt::Key_0,
QStringLiteral("Toggle Zoom on Focus"),
QStringLiteral("Toggle Zoom on Focus")
};
} }

View File

@ -94,20 +94,24 @@ Node {
running: true running: true
onTriggered: { onTriggered: {
if (breezyDesktop.imuRotations && breezyDesktop.imuRotations.length > 0) { if (breezyDesktop.imuRotations && breezyDesktop.imuRotations.length > 0) {
const focusedIndex = displays.findFocusedMonitor( let focusedIndex = -1;
displays.eusToNwuQuat(breezyDesktop.imuRotations[0]),
breezyDesktop.monitorPlacements.map(monitorVectors => monitorVectors.centerLook), if (effect.zoomOnFocusEnabled) {
breezyDesktop.focusedMonitorIndex, focusedIndex = displays.findFocusedMonitor(
false, // TODO smooth follow displays.eusToNwuQuat(breezyDesktop.imuRotations[0]),
breezyDesktop.fovDetails, breezyDesktop.monitorPlacements.map(monitorVectors => monitorVectors.centerLook),
breezyDesktop.screens.map(screen => screen.geometry) breezyDesktop.focusedMonitorIndex,
); false, // TODO smooth follow
breezyDesktop.fovDetails,
breezyDesktop.screens.map(screen => screen.geometry)
);
}
const focusedDisplay = focusedIndex !== -1 ? breezyDesktop.displayAtIndex(focusedIndex) : null; const focusedDisplay = focusedIndex !== -1 ? breezyDesktop.displayAtIndex(focusedIndex) : null;
if (focusedIndex !== breezyDesktop.focusedMonitorIndex) { if (focusedIndex !== breezyDesktop.focusedMonitorIndex) {
zoomOutAnimation.stop(); // zoomOutAnimation.stop();
zoomInAnimation.stop(); // zoomInAnimation.stop();
zoomOnFocusSequence.stop(); // zoomOnFocusSequence.stop();
if (focusedDisplay === null) { if (focusedDisplay === null) {
zoomOutAnimation.target = breezyDesktop.displayAtIndex(breezyDesktop.focusedMonitorIndex); zoomOutAnimation.target = breezyDesktop.displayAtIndex(breezyDesktop.focusedMonitorIndex);
zoomOutAnimation.target.targetDistance = zoomOutAnimation.to; zoomOutAnimation.target.targetDistance = zoomOutAnimation.to;