Compare commits

..

No commits in common. "ad5fd9116e25bc502468f4dfa884ee027887c51c" and "50cdf9886803c0279aafa43d0b590abdc34f5766" have entirely different histories.

7 changed files with 18 additions and 219 deletions

View file

@ -4,7 +4,6 @@ project(quickshell VERSION "0.2.1" LANGUAGES CXX C)
set(UNRELEASED_FEATURES
"network.2"
"colorquant-imagerect"
"window-parent"
)
set(QT_MIN_VERSION "6.6.0")

View file

@ -30,7 +30,6 @@ set shell id.
- Added ext-background-effect window blur support.
- Added per-corner radius support to Region.
- Added ColorQuantizer region selection.
- Added dialog window support to FloatingWindow.
## Other Changes
@ -67,9 +66,6 @@ set shell id.
- Worked around Qt bug causing crashes when plugging and unplugging monitors.
- Fixed HyprlandFocusGrab crashing if windows were destroyed after being passed to it.
- Fixed ScreencopyView pixelation when scaled.
- Fixed JsonAdapter crashing and providing bad data on read when using JsonObject.
- Fixed JsonAdapter sending unnecessary property changes for primitive values.
- Fixed JsonAdapter serialization for lists.
## Packaging Changes

View file

@ -1,13 +1,11 @@
#include "jsonadapter.hpp"
#include <qassociativeiterable.h>
#include <qcontainerfwd.h>
#include <qjsonarray.h>
#include <qjsondocument.h>
#include <qjsonobject.h>
#include <qjsonvalue.h>
#include <qjsvalue.h>
#include <qmetacontainer.h>
#include <qmetaobject.h>
#include <qnamespace.h>
#include <qobject.h>
@ -16,7 +14,6 @@
#include <qqmlengine.h>
#include <qqmlinfo.h>
#include <qqmllist.h>
#include <qsequentialiterable.h>
#include <qstringview.h>
#include <qvariant.h>
@ -134,22 +131,13 @@ QJsonObject JsonAdapter::serializeRec(const QObject* obj, const QMetaObject* bas
}
json.insert(prop.name(), array);
} else if (val.canConvert<QJSValue>()) {
auto variant = val.value<QJSValue>().toVariant();
auto jv = QJsonValue::fromVariant(variant);
json.insert(prop.name(), jv);
} else {
if (val.canConvert<QJSValue>()) val = val.value<QJSValue>().toVariant();
auto jsonVal = QJsonValue::fromVariant(val);
if (jsonVal.isNull() && !val.isNull() && val.isValid()) {
if (val.canConvert<QAssociativeIterable>()) {
val.convert(QMetaType::fromType<QVariantMap>());
} else if (val.canConvert<QSequentialIterable>()) {
val.convert(QMetaType::fromType<QVariantList>());
}
jsonVal = QJsonValue::fromVariant(val);
}
json.insert(prop.name(), jsonVal);
auto jv = QJsonValue::fromVariant(val);
json.insert(prop.name(), jv);
}
}
}
@ -166,16 +154,14 @@ void JsonAdapter::deserializeRec(const QJsonObject& json, QObject* obj, const QM
auto jval = json.value(prop.name());
if (prop.metaType() == QMetaType::fromType<QVariant>()) {
auto newVariant = jval.toVariant();
auto oldValue = prop.read(obj);
auto oldVariant =
oldValue.canConvert<QJSValue>() ? oldValue.value<QJSValue>().toVariant() : oldValue;
auto variant = jval.toVariant();
auto oldValue = prop.read(this).value<QJSValue>();
// Calling prop.write with a new QJSValue will cause a property update
// even if content is identical.
if (newVariant != oldVariant) {
auto jsValue = qmlEngine(this)->fromVariant<QJSValue>(newVariant);
prop.write(obj, QVariant::fromValue(jsValue));
if (jval.toVariant() != oldValue.toVariant()) {
auto jsValue = qmlEngine(this)->fromVariant<QJSValue>(jval.toVariant());
prop.write(this, QVariant::fromValue(jsValue));
}
} else if (QMetaType::canView(prop.metaType(), QMetaType::fromType<JsonObject*>())) {
// FIXME: This doesn't support creating descendants of JsonObject, as QMetaType.metaObject()
@ -210,7 +196,7 @@ void JsonAdapter::deserializeRec(const QJsonObject& json, QObject* obj, const QM
QMetaType::fromType<QQmlListProperty<JsonObject>>()
))
{
auto pval = prop.read(obj);
auto pval = prop.read(this);
if (pval.canConvert<QQmlListProperty<JsonObject>>()) {
auto lp = pval.value<QQmlListProperty<JsonObject>>();
@ -261,35 +247,12 @@ void JsonAdapter::deserializeRec(const QJsonObject& json, QObject* obj, const QM
}
} else {
auto variant = jval.toVariant();
auto convVariant = variant;
if (convVariant.convert(prop.metaType())) {
prop.write(obj, convVariant);
if (variant.convert(prop.metaType())) {
prop.write(obj, variant);
} else {
auto pval = prop.read(obj);
if (variant.canConvert<QSequentialIterable>() && pval.canView<QSequentialIterable>()) {
auto targetv = QVariant(pval.metaType());
auto target = targetv.view<QSequentialIterable>().metaContainer();
auto valueType = target.valueMetaType();
auto i = 0;
for (QVariant item: variant.value<QSequentialIterable>()) {
if (item.convert(valueType)) {
target.addValueAtEnd(targetv.data(), item.constData());
} else {
qmlWarning(this) << "Failed to deserialize list member " << i << " of property "
<< prop.name() << ": expected " << valueType.name() << " but got "
<< item.typeName();
}
++i;
}
prop.write(obj, targetv);
} else {
qmlWarning(this) << "Failed to deserialize property " << prop.name() << ": expected "
<< prop.metaType().name() << " but got "
<< jval.toVariant().typeName();
}
qmlWarning(this) << "Failed to deserialize property " << prop.name() << ": expected "
<< prop.metaType().name() << " but got " << jval.toVariant().typeName();
}
}
}

View file

@ -3,7 +3,7 @@
#include <qnamespace.h>
#include <qobject.h>
#include <qqmlengine.h>
#include <qqmlinfo.h>
#include <qqmllist.h>
#include <qtmetamacros.h>
#include <qtypes.h>
#include <qwindow.h>
@ -11,27 +11,6 @@
#include "proxywindow.hpp"
#include "windowinterface.hpp"
ProxyFloatingWindow::ProxyFloatingWindow(QObject* parent): ProxyWindowBase(parent) {
this->bTargetVisible.setBinding([this] {
if (!this->bWantsVisible) return false;
auto* parent = this->bParentProxyWindow.value();
if (!parent) return true;
return parent->bindableBackerVisibility().value();
});
}
void ProxyFloatingWindow::targetVisibleChanged() {
if (this->window && this->bParentProxyWindow) {
auto* bw = this->bParentProxyWindow.value()->backingWindow();
if (bw != this->window->transientParent()) {
this->window->setTransientParent(bw);
}
}
this->ProxyWindowBase::setVisible(this->bTargetVisible);
}
void ProxyFloatingWindow::connectWindow() {
this->ProxyWindowBase::connectWindow();
@ -40,25 +19,6 @@ void ProxyFloatingWindow::connectWindow() {
this->window->setMaximumSize(this->bMaximumSize);
}
void ProxyFloatingWindow::completeWindow() {
this->ProxyWindowBase::completeWindow();
auto* parent = this->bParentProxyWindow.value();
this->window->setTransientParent(parent ? parent->backingWindow() : nullptr);
}
void ProxyFloatingWindow::postCompleteWindow() {
this->ProxyWindowBase::setVisible(this->bTargetVisible);
}
void ProxyFloatingWindow::onParentDestroyed() {
this->mParentWindow = nullptr;
this->bParentProxyWindow = nullptr;
emit this->parentWindowChanged();
}
void ProxyFloatingWindow::setVisible(bool visible) { this->bWantsVisible = visible; }
void ProxyFloatingWindow::trySetWidth(qint32 implicitWidth) {
if (!this->window->isVisible()) {
this->ProxyWindowBase::trySetWidth(implicitWidth);
@ -86,42 +46,6 @@ void ProxyFloatingWindow::onMaximumSizeChanged() {
emit this->maximumSizeChanged();
}
QObject* ProxyFloatingWindow::parentWindow() const { return this->mParentWindow; }
void ProxyFloatingWindow::setParentWindow(QObject* window) {
if (window == this->mParentWindow) return;
if (this->window && this->window->isVisible()) {
qmlWarning(this) << "parentWindow cannot be changed after the window is visible.";
return;
}
if (this->bParentProxyWindow) {
QObject::disconnect(this->bParentProxyWindow, nullptr, this, nullptr);
}
if (this->mParentWindow) {
QObject::disconnect(this->mParentWindow, nullptr, this, nullptr);
}
this->mParentWindow = nullptr;
this->bParentProxyWindow = nullptr;
if (auto* proxy = ProxyWindowBase::forObject(window)) {
this->mParentWindow = window;
this->bParentProxyWindow = proxy;
QObject::connect(
this->mParentWindow,
&QObject::destroyed,
this,
&ProxyFloatingWindow::onParentDestroyed
);
}
emit this->parentWindowChanged();
}
// FloatingWindowInterface
FloatingWindowInterface::FloatingWindowInterface(QObject* parent)
@ -133,7 +57,6 @@ FloatingWindowInterface::FloatingWindowInterface(QObject* parent)
QObject::connect(this->window, &ProxyFloatingWindow::titleChanged, this, &FloatingWindowInterface::titleChanged);
QObject::connect(this->window, &ProxyFloatingWindow::minimumSizeChanged, this, &FloatingWindowInterface::minimumSizeChanged);
QObject::connect(this->window, &ProxyFloatingWindow::maximumSizeChanged, this, &FloatingWindowInterface::maximumSizeChanged);
QObject::connect(this->window, &ProxyFloatingWindow::parentWindowChanged, this, &FloatingWindowInterface::parentWindowChanged);
QObject::connect(this->window, &ProxyWindowBase::windowConnected, this, &FloatingWindowInterface::onWindowConnected);
// clang-format on
}
@ -246,9 +169,3 @@ bool FloatingWindowInterface::startSystemResize(Qt::Edges edges) const {
if (!qw) return false;
return qw->startSystemResize(edges);
}
QObject* FloatingWindowInterface::parentWindow() const { return this->window->parentWindow(); }
void FloatingWindowInterface::setParentWindow(QObject* window) {
this->window->setParentWindow(window);
}

View file

@ -16,15 +16,9 @@ class ProxyFloatingWindow: public ProxyWindowBase {
Q_OBJECT;
public:
explicit ProxyFloatingWindow(QObject* parent = nullptr);
explicit ProxyFloatingWindow(QObject* parent = nullptr): ProxyWindowBase(parent) {}
void connectWindow() override;
void completeWindow() override;
void postCompleteWindow() override;
void setVisible(bool visible) override;
[[nodiscard]] QObject* parentWindow() const;
void setParentWindow(QObject* window);
// Setting geometry while the window is visible makes the content item shrink but not the window
// which is awful so we disable it for floating windows.
@ -35,28 +29,11 @@ signals:
void minimumSizeChanged();
void maximumSizeChanged();
void titleChanged();
void parentWindowChanged();
private slots:
void onParentDestroyed();
private:
void onMinimumSizeChanged();
void onMaximumSizeChanged();
void onTitleChanged();
void targetVisibleChanged();
QObject* mParentWindow = nullptr;
Q_OBJECT_BINDABLE_PROPERTY(ProxyFloatingWindow, ProxyWindowBase*, bParentProxyWindow);
Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(ProxyFloatingWindow, bool, bWantsVisible, true);
Q_OBJECT_BINDABLE_PROPERTY(
ProxyFloatingWindow,
bool,
bTargetVisible,
&ProxyFloatingWindow::targetVisibleChanged
);
public:
Q_OBJECT_BINDABLE_PROPERTY(
@ -98,11 +75,6 @@ class FloatingWindowInterface: public WindowInterface {
Q_PROPERTY(bool maximized READ isMaximized WRITE setMaximized NOTIFY maximizedChanged);
/// Whether the window is currently fullscreen.
Q_PROPERTY(bool fullscreen READ isFullscreen WRITE setFullscreen NOTIFY fullscreenChanged);
/// The parent window of this window. Setting this makes the window a child of the parent,
/// which affects window stacking behavior.
///
/// > [!NOTE] This property cannot be changed after the window is visible.
Q_PROPERTY(QObject* parentWindow READ parentWindow WRITE setParentWindow NOTIFY parentWindowChanged);
// clang-format on
QML_NAMED_ELEMENT(FloatingWindow);
@ -129,9 +101,6 @@ public:
/// Start a system resize operation. Must be called during a pointer press/drag.
Q_INVOKABLE [[nodiscard]] bool startSystemResize(Qt::Edges edges) const;
[[nodiscard]] QObject* parentWindow() const;
void setParentWindow(QObject* window);
signals:
void minimumSizeChanged();
void maximumSizeChanged();
@ -139,7 +108,6 @@ signals:
void minimizedChanged();
void maximizedChanged();
void fullscreenChanged();
void parentWindowChanged();
private slots:
void onWindowConnected();

View file

@ -1,42 +0,0 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls.Fusion
import Quickshell
Scope {
FloatingWindow {
id: control
color: contentItem.palette.window
ColumnLayout {
CheckBox {
id: parentCb
text: "Show parent"
}
CheckBox {
id: dialogCb
text: "Show dialog"
}
}
}
FloatingWindow {
id: parentw
Text {
text: "parent"
}
visible: parentCb.checked
color: contentItem.palette.window
FloatingWindow {
id: dialog
parentWindow: parentw
visible: dialogCb.checked
color: contentItem.palette.window
Text {
text: "dialog"
}
}
}
}

View file

@ -21,8 +21,6 @@ WindowManager* WindowManager::instance() {
}
ScreenProjection* WindowManager::screenProjection(QuickshellScreenInfo* screen) {
if (!screen) return nullptr;
auto* qscreen = screen->screen;
auto it = this->mScreenProjections.find(qscreen);
if (it != this->mScreenProjections.end()) {