UI WIP
This commit is contained in:
parent
538782b585
commit
7eab1b5ba1
|
|
@ -17,6 +17,11 @@
|
|||
<max>230</max>
|
||||
<label>All Displays Distance</label>
|
||||
</entry>
|
||||
<entry name="measurement_units" type="String">
|
||||
<default>cm</default>
|
||||
<label>Measurement units</label>
|
||||
<description>Units for displaying distances in the KCM UI. Valid values: "cm" or "in".</description>
|
||||
</entry>
|
||||
<entry name="ZoomOnFocusEnabled" type="Bool">
|
||||
<default>false</default>
|
||||
<label>Zoom on Focus Enabled</label>
|
||||
|
|
@ -30,7 +35,7 @@
|
|||
<description>How far apart the displays are visually (not logically)</description>
|
||||
</entry>
|
||||
<entry name="DisplaySize" type="Int">
|
||||
<default>100</default>
|
||||
<default>97</default>
|
||||
<min>10</min>
|
||||
<max>250</max>
|
||||
<label>Display Size</label>
|
||||
|
|
|
|||
|
|
@ -48,6 +48,8 @@
|
|||
#include <QFile>
|
||||
#include <QDir>
|
||||
#include <QJsonDocument>
|
||||
#include <QLocale>
|
||||
#include <QSignalBlocker>
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
|
||||
|
|
@ -201,6 +203,27 @@ BreezyDesktopEffectConfig::BreezyDesktopEffectConfig(QObject *parent, const KPlu
|
|||
ui.setupUi(widget());
|
||||
addConfig(BreezyDesktopConfig::self(), widget());
|
||||
|
||||
// Advanced tab: measurement units selector (stored as "cm" or "in")
|
||||
if (ui.comboMeasurementUnits) {
|
||||
ui.comboMeasurementUnits->clear();
|
||||
ui.comboMeasurementUnits->addItem(i18n("Centimeters (cm)"), QStringLiteral("cm"));
|
||||
ui.comboMeasurementUnits->addItem(i18n("Inches (in)"), QStringLiteral("in"));
|
||||
|
||||
{
|
||||
QSignalBlocker b(ui.comboMeasurementUnits);
|
||||
const QString saved = KConfigGroup(BreezyDesktopConfig::self()->sharedConfig(), QLatin1String(EFFECT_GROUP))
|
||||
.readEntry(QStringLiteral("measurement_units"), QStringLiteral("cm"));
|
||||
const int idx = ui.comboMeasurementUnits->findData(saved);
|
||||
ui.comboMeasurementUnits->setCurrentIndex(idx >= 0 ? idx : 0);
|
||||
}
|
||||
|
||||
connect(ui.comboMeasurementUnits, qOverload<int>(&QComboBox::currentIndexChanged), this, [this](int) {
|
||||
if (m_updatingFromConfig) return;
|
||||
applyDistanceLabelFormatters();
|
||||
save();
|
||||
});
|
||||
}
|
||||
|
||||
// One-time check if the KWin effect backend is actually loaded. If not, disable UI early.
|
||||
checkEffectLoaded();
|
||||
|
||||
|
|
@ -394,6 +417,8 @@ BreezyDesktopEffectConfig::BreezyDesktopEffectConfig(QObject *parent, const KPlu
|
|||
lookAheadOverrideSlider->setValueText(-1, i18n("Default"));
|
||||
}
|
||||
|
||||
applyDistanceLabelFormatters();
|
||||
|
||||
renderVirtualDisplays(dbusListVirtualDisplays());
|
||||
|
||||
m_virtualDisplayPollTimer.setInterval(15000);
|
||||
|
|
@ -431,6 +456,14 @@ void BreezyDesktopEffectConfig::save()
|
|||
m_updatingFromConfig = true;
|
||||
updateConfigFromUi();
|
||||
BreezyDesktopConfig::self()->save();
|
||||
|
||||
// Store measurement_units explicitly (snake_case key) without depending on KConfigXT accessor naming.
|
||||
{
|
||||
KConfigGroup grp(BreezyDesktopConfig::self()->sharedConfig(), QLatin1String(EFFECT_GROUP));
|
||||
grp.writeEntry(QStringLiteral("measurement_units"), measurementUnitsFromUi());
|
||||
grp.sync();
|
||||
}
|
||||
|
||||
KCModule::save();
|
||||
ui.kcfg_FocusedDisplayDistance->setEnabled(
|
||||
ui.kcfg_ZoomOnFocusEnabled->isChecked() || ui.SmoothFollowEnabled->isChecked());
|
||||
|
|
@ -472,11 +505,29 @@ void BreezyDesktopEffectConfig::updateUiFromConfig()
|
|||
ui.kcfg_FocusedDisplayDistance->setEnabled(
|
||||
ui.kcfg_ZoomOnFocusEnabled->isChecked() || ui.SmoothFollowEnabled->isChecked());
|
||||
ui.kcfg_SmoothFollowThreshold->setValue(BreezyDesktopConfig::self()->smoothFollowThreshold());
|
||||
|
||||
if (ui.comboMeasurementUnits) {
|
||||
QSignalBlocker b(ui.comboMeasurementUnits);
|
||||
const QString saved = KConfigGroup(BreezyDesktopConfig::self()->sharedConfig(), QLatin1String(EFFECT_GROUP))
|
||||
.readEntry(QStringLiteral("measurement_units"), QStringLiteral("cm"));
|
||||
const int idx = ui.comboMeasurementUnits->findData(saved);
|
||||
ui.comboMeasurementUnits->setCurrentIndex(idx >= 0 ? idx : 0);
|
||||
}
|
||||
|
||||
applyDistanceLabelFormatters();
|
||||
}
|
||||
|
||||
void BreezyDesktopEffectConfig::updateUiFromDefaultConfig()
|
||||
{
|
||||
ui.shortcutsEditor->allDefault();
|
||||
|
||||
if (ui.comboMeasurementUnits) {
|
||||
QSignalBlocker b(ui.comboMeasurementUnits);
|
||||
const int idx = ui.comboMeasurementUnits->findData(QStringLiteral("cm"));
|
||||
ui.comboMeasurementUnits->setCurrentIndex(idx >= 0 ? idx : 0);
|
||||
}
|
||||
|
||||
applyDistanceLabelFormatters();
|
||||
}
|
||||
|
||||
void BreezyDesktopEffectConfig::updateUnmanagedState()
|
||||
|
|
@ -648,6 +699,11 @@ void BreezyDesktopEffectConfig::pollDriverState()
|
|||
auto stateJson = stateJsonOpt.value();
|
||||
m_connectedDeviceBrand = stateJson.value(QStringLiteral("connected_device_brand")).toString();
|
||||
m_connectedDeviceModel = stateJson.value(QStringLiteral("connected_device_model")).toString();
|
||||
m_connectedDeviceFullDistanceCm = stateJson.value(QStringLiteral("connected_device_full_distance_cm")).toDouble(0.0);
|
||||
m_connectedDeviceFullSizeCm = stateJson.value(QStringLiteral("connected_device_full_size_cm")).toDouble(0.0);
|
||||
m_connectedDevicePoseHasPosition = stateJson.value(QStringLiteral("connected_device_pose_has_position")).toBool(false);
|
||||
|
||||
applyDistanceLabelFormatters();
|
||||
|
||||
const bool smoothFollow = smoothFollowEnabled(stateJsonOpt);
|
||||
if (ui.SmoothFollowEnabled->isChecked() != smoothFollow) {
|
||||
|
|
@ -711,6 +767,46 @@ void BreezyDesktopEffectConfig::pollDriverState()
|
|||
m_driverStateInitialized = true;
|
||||
}
|
||||
|
||||
QString BreezyDesktopEffectConfig::measurementUnitsFromUi() const
|
||||
{
|
||||
if (!ui.comboMeasurementUnits) return QStringLiteral("cm");
|
||||
const QString v = ui.comboMeasurementUnits->currentData().toString();
|
||||
if (v == QLatin1String("in")) return QStringLiteral("in");
|
||||
return QStringLiteral("cm");
|
||||
}
|
||||
|
||||
void BreezyDesktopEffectConfig::applyDistanceLabelFormatters()
|
||||
{
|
||||
auto *focused = ui.kcfg_FocusedDisplayDistance;
|
||||
auto *all = ui.kcfg_AllDisplaysDistance;
|
||||
if (!focused || !all) return;
|
||||
|
||||
// Only apply the unit conversion labels when the driver reports positional tracking.
|
||||
if (!m_connectedDevicePoseHasPosition) {
|
||||
focused->clearValueToDisplayStringFn();
|
||||
all->clearValueToDisplayStringFn();
|
||||
return;
|
||||
}
|
||||
|
||||
const double fullCm = static_cast<double>(m_connectedDeviceFullDistanceCm);
|
||||
const QString units = measurementUnitsFromUi();
|
||||
const QLocale loc;
|
||||
|
||||
LabeledSlider::ValueToDisplayStringFn fn = [fullCm, units, loc](int raw) -> QString {
|
||||
if (fullCm <= 0.0) return QString();
|
||||
const double ratio = static_cast<double>(raw) / 100.0; // slider uses a 2-decimal fixed-point scale
|
||||
const double cm = ratio * fullCm;
|
||||
if (units == QLatin1String("in")) {
|
||||
const double inches = cm / 2.54;
|
||||
return loc.toString(inches, 'f', 1) + QStringLiteral(" in");
|
||||
}
|
||||
return loc.toString(cm, 'f', 0) + QStringLiteral(" cm");
|
||||
};
|
||||
|
||||
focused->setValueToDisplayStringFn(fn);
|
||||
all->setValueToDisplayStringFn(fn);
|
||||
}
|
||||
|
||||
double BreezyDesktopEffectConfig::neckSaverHorizontalMultiplier(std::optional<QJsonObject> configJsonOpt)
|
||||
{
|
||||
if (!configJsonOpt) return 1.0;
|
||||
|
|
|
|||
|
|
@ -28,6 +28,9 @@ public Q_SLOTS:
|
|||
void defaults() override;
|
||||
|
||||
private:
|
||||
QString measurementUnitsFromUi() const;
|
||||
void applyDistanceLabelFormatters();
|
||||
|
||||
void updateDriverEnabled();
|
||||
void updateMultitapEnabled();
|
||||
void updateSmoothFollowEnabled();
|
||||
|
|
@ -73,8 +76,9 @@ private:
|
|||
int m_smoothFollowThreshold = 1;
|
||||
QString m_connectedDeviceBrand;
|
||||
QString m_connectedDeviceModel;
|
||||
int m_connectedDeviceFullDistanceCm = 0;
|
||||
int m_connectedDeviceFullSizeCm = 0;
|
||||
float m_connectedDeviceFullDistanceCm = 0.0;
|
||||
float m_connectedDeviceFullSizeCm = 0.0;
|
||||
bool m_connectedDevicePoseHasPosition = false;
|
||||
QTimer m_statePollTimer; // periodic driver state polling
|
||||
QTimer m_virtualDisplayPollTimer; // periodic virtual display list polling
|
||||
bool m_licenseLoading = false;
|
||||
|
|
|
|||
|
|
@ -159,11 +159,14 @@
|
|||
</item>
|
||||
<item row="6" column="1">
|
||||
<widget class="LabeledSlider" name="kcfg_DisplaySize">
|
||||
<property name="decimalShift">
|
||||
<double>2</double>
|
||||
</property>
|
||||
<property name="tickPosition">
|
||||
<enum>QSlider::NoTicks</enum>
|
||||
</property>
|
||||
<property name="tickStartOffset">
|
||||
<double>10</double>
|
||||
<double>5</double>
|
||||
</property>
|
||||
<property name="tickInterval">
|
||||
<double>25</double>
|
||||
|
|
@ -631,6 +634,17 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="13" column="0">
|
||||
<widget class="QLabel" name="labelMeasurementUnits">
|
||||
<property name="text">
|
||||
<string>Measurement units:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="13" column="1">
|
||||
<widget class="QComboBox" name="comboMeasurementUnits">
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tabLicenseDetails">
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#include <QPainterPath>
|
||||
#include <QStyleOptionSlider>
|
||||
#include <algorithm> // for std::max
|
||||
#include <functional>
|
||||
#include <QtCore/QMap>
|
||||
#include <QtCore/QString>
|
||||
#include <QStringList>
|
||||
|
|
@ -33,6 +34,8 @@ class LabeledSlider : public QSlider {
|
|||
// Example: minimum=0, tickInterval=20, tickStartOffset=10 -> labels at 10,30,50,...
|
||||
Q_PROPERTY(int tickStartOffset READ tickStartOffset WRITE setTickStartOffset)
|
||||
public:
|
||||
using ValueToDisplayStringFn = std::function<QString(int)>;
|
||||
|
||||
explicit LabeledSlider(QWidget *parent = nullptr)
|
||||
: QSlider(Qt::Horizontal, parent)
|
||||
{
|
||||
|
|
@ -74,6 +77,24 @@ public:
|
|||
|
||||
QMap<int, QString> valueTexts() const { return m_valueTexts; }
|
||||
|
||||
// Optional custom formatter for displayed values.
|
||||
// If set, it is consulted for values without an explicit setValueText() override.
|
||||
// Returning a null QString (QString()) falls back to the built-in formatting.
|
||||
void setValueToDisplayStringFn(ValueToDisplayStringFn fn) {
|
||||
m_valueToDisplayStringFn = std::move(fn);
|
||||
updateGeometry();
|
||||
update();
|
||||
}
|
||||
|
||||
void clearValueToDisplayStringFn() {
|
||||
if (!m_valueToDisplayStringFn) return;
|
||||
m_valueToDisplayStringFn = nullptr;
|
||||
updateGeometry();
|
||||
update();
|
||||
}
|
||||
|
||||
bool hasValueToDisplayStringFn() const { return static_cast<bool>(m_valueToDisplayStringFn); }
|
||||
|
||||
int decimalShift() const { return m_decimalShift; }
|
||||
void setDecimalShift(int shift) {
|
||||
// clamp to sensible range
|
||||
|
|
@ -193,6 +214,14 @@ private:
|
|||
if (it != m_valueTexts.constEnd()) {
|
||||
return *it;
|
||||
}
|
||||
|
||||
if (m_valueToDisplayStringFn) {
|
||||
QString formatted = m_valueToDisplayStringFn(raw);
|
||||
if (!formatted.isNull()) {
|
||||
return formatted;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_decimalShift == 0) {
|
||||
return QString::number(raw);
|
||||
}
|
||||
|
|
@ -210,6 +239,7 @@ private:
|
|||
int m_decimalShift = 0; // display-only decimal shift
|
||||
int m_tickStartOffset = 0; // label positions start offset relative to minimum
|
||||
QMap<int, QString> m_valueTexts; // optional text overrides for specific values
|
||||
ValueToDisplayStringFn m_valueToDisplayStringFn; // optional custom formatter
|
||||
private:
|
||||
int labelInterval() const {
|
||||
int ti = tickInterval();
|
||||
|
|
|
|||
Loading…
Reference in New Issue