diff --git a/src/treeland/data/treeland-foreign-toplevel-manager-v1.xml b/src/treeland/data/treeland-foreign-toplevel-manager-v1.xml
index c9d1a515..31a64338 100644
--- a/src/treeland/data/treeland-foreign-toplevel-manager-v1.xml
+++ b/src/treeland/data/treeland-foreign-toplevel-manager-v1.xml
@@ -60,6 +60,10 @@
the client should free any resources associated with it.
+
+
+
+
@@ -290,4 +294,48 @@
allow-null="true" />
+
+
+ This interface allows dock set windows preview.
+
+ Warning! The protocol described in this file is currently in the testing
+ phase. Backward compatible changes may be added together with the
+ corresponding interface version bump. Backward incompatible changes can
+ only be done by creating a new major version of the extension.
+
+
+
+ This event is sent after mouse enter preview box.
+
+
+
+
+ This event is sent after mouse leave preview box.
+
+
+
+
+
+ X and Y are relative to the relative_surface.
+ surfaces wl_array is identifiers.
+
+
+
+
+
+
+
+
+
+
+ close preview box
+
+
+
+
+
+ Destroy the context object.
+
+
+
diff --git a/src/treeland/protocols/CMakeLists.txt b/src/treeland/protocols/CMakeLists.txt
index f8641490..d2131667 100644
--- a/src/treeland/protocols/CMakeLists.txt
+++ b/src/treeland/protocols/CMakeLists.txt
@@ -20,6 +20,7 @@ target_sources(treeland-protocols PRIVATE
ext_foreign_toplevel_list_impl.cpp
foreigntoplevelhandlev1.cpp
foreigntoplevelmanagerv1.cpp
+ dockpreviewcontextv1.cpp
)
target_compile_definitions(treeland-protocols
diff --git a/src/treeland/protocols/dockpreviewcontextv1.cpp b/src/treeland/protocols/dockpreviewcontextv1.cpp
new file mode 100644
index 00000000..ab618993
--- /dev/null
+++ b/src/treeland/protocols/dockpreviewcontextv1.cpp
@@ -0,0 +1,91 @@
+// Copyright (C) 2023 rewine .
+// SPDX-License-Identifier: Apache-2.0 OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+
+#include "foreigntoplevelhandlev1.h"
+#include "foreign_toplevel_manager_impl.h"
+
+#include
+
+#include
+#include
+#include
+
+QW_USE_NAMESPACE
+
+class TreeLandDockPreviewContextV1Private : public QWObjectPrivate
+{
+public:
+ TreeLandDockPreviewContextV1Private(treeland_dock_preview_context_v1 *handle, bool isOwner, TreeLandDockPreviewContextV1 *qq)
+ : QWObjectPrivate(handle, isOwner, qq)
+ {
+ Q_ASSERT(!map.contains(handle));
+ map.insert(handle, qq);
+ sc.connect(&handle->events.destroy, this, &TreeLandDockPreviewContextV1Private::on_destroy);
+ sc.connect(&handle->events.request_show, this, &TreeLandDockPreviewContextV1Private::on_request_show);
+ }
+ ~TreeLandDockPreviewContextV1Private() {
+ if (!m_handle)
+ return;
+ destroy();
+ if (isHandleOwner)
+ treeland_dock_preview_context_v1_destroy(q_func()->handle());
+ }
+
+ inline void destroy() {
+ Q_ASSERT(m_handle);
+ Q_ASSERT(map.contains(m_handle));
+ Q_EMIT q_func()->beforeDestroy(q_func());
+ map.remove(m_handle);
+ sc.invalidate();
+ }
+
+ void on_destroy(void *);
+ void on_request_show(void *);
+
+ static QHash map;
+ QW_DECLARE_PUBLIC(TreeLandDockPreviewContextV1)
+ QWSignalConnector sc;
+};
+QHash TreeLandDockPreviewContextV1Private::map;
+
+void TreeLandDockPreviewContextV1Private::on_destroy(void *)
+{
+ destroy();
+ m_handle = nullptr;
+ delete q_func();
+}
+
+void TreeLandDockPreviewContextV1Private::on_request_show(void *data)
+{
+ Q_EMIT q_func()->requestShow(static_cast(data));
+}
+
+TreeLandDockPreviewContextV1::TreeLandDockPreviewContextV1(treeland_dock_preview_context_v1 *handle, bool isOwner)
+ : QObject(nullptr)
+ , QWObject(*new TreeLandDockPreviewContextV1Private(handle, isOwner, this))
+{
+
+}
+
+TreeLandDockPreviewContextV1 *TreeLandDockPreviewContextV1::get(treeland_dock_preview_context_v1 *handle)
+{
+ return TreeLandDockPreviewContextV1Private::map.value(handle);
+}
+
+TreeLandDockPreviewContextV1 *TreeLandDockPreviewContextV1::from(treeland_dock_preview_context_v1 *handle)
+{
+ if (auto o = get(handle))
+ return o;
+ return new TreeLandDockPreviewContextV1(handle, false);
+}
+
+void TreeLandDockPreviewContextV1::enter()
+{
+ treeland_dock_preview_context_v1_enter(handle());
+}
+
+void TreeLandDockPreviewContextV1::leave()
+{
+ treeland_dock_preview_context_v1_leave(handle());
+}
diff --git a/src/treeland/protocols/foreign_toplevel_manager_impl.cpp b/src/treeland/protocols/foreign_toplevel_manager_impl.cpp
index 03910ee3..ae75842d 100644
--- a/src/treeland/protocols/foreign_toplevel_manager_impl.cpp
+++ b/src/treeland/protocols/foreign_toplevel_manager_impl.cpp
@@ -43,10 +43,27 @@ static void treeland_foreign_toplevel_handle_set_fullscreen(struct wl_client *cl
struct wl_resource *output);
static void treeland_foreign_toplevel_handle_unset_fullscreen(struct wl_client *client,
struct wl_resource *resource);
+static void treeland_foreign_toplevel_handle_set_preview(struct wl_client *client,
+ struct wl_resource *resource);
+static void treeland_foreign_toplevel_handle_unset_preview(struct wl_client *client,
+ struct wl_resource *resource);
static void treeland_foreign_toplevel_handle_destroy(struct wl_client *client,
struct wl_resource *resource);
static void treeland_foreign_toplevel_manager_handle_stop(struct wl_client *client,
struct wl_resource *resource);
+static void treeland_foreign_toplevel_manager_handle_get_dock_preview_context(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *relative_surface,
+ uint32_t id);
+
+static void treeland_dock_preview_context_handle_show(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_array *surfaces,
+ int32_t x,
+ int32_t y,
+ uint32_t direction);
+
+static void treeland_dock_preview_context_handle_destroy(struct wl_client *client, struct wl_resource *resource);
static const struct ztreeland_foreign_toplevel_handle_v1_interface treeland_toplevel_handle_impl = {
.set_maximized = treeland_foreign_toplevel_handle_set_maximized,
@@ -61,6 +78,12 @@ static const struct ztreeland_foreign_toplevel_handle_v1_interface treeland_topl
.unset_fullscreen = treeland_foreign_toplevel_handle_unset_fullscreen,
};
+static const struct treeland_dock_preview_context_v1_interface
+ treeland_dock_preview_context_impl = {
+ .show = treeland_dock_preview_context_handle_show,
+ .destroy = treeland_dock_preview_context_handle_destroy,
+};
+
static struct treeland_foreign_toplevel_handle_v1 *
toplevel_handle_from_resource(struct wl_resource *resource)
{
@@ -71,6 +94,16 @@ toplevel_handle_from_resource(struct wl_resource *resource)
wl_resource_get_user_data(resource));
}
+static struct treeland_dock_preview_context_v1 *
+toplevel_dock_preview_context_from_resource(struct wl_resource *resource)
+{
+ assert(wl_resource_instance_of(resource,
+ &treeland_dock_preview_context_v1_interface,
+ &treeland_dock_preview_context_impl));
+ return static_cast(
+ wl_resource_get_user_data(resource));
+}
+
static void toplevel_handle_send_maximized_event(struct wl_resource *resource, bool state)
{
struct treeland_foreign_toplevel_handle_v1 *toplevel = toplevel_handle_from_resource(resource);
@@ -143,6 +176,38 @@ static void toplevel_send_fullscreen_event(struct wl_resource *resource,
wl_signal_emit_mutable(&toplevel->events.request_fullscreen, &event);
}
+static void dock_send_preview_event(struct wl_resource *resource, struct wl_array *surfaces, int32_t x, int32_t y, int32_t direction)
+{
+ struct treeland_dock_preview_context_v1 *dock_preview = toplevel_dock_preview_context_from_resource(resource);
+ if (!dock_preview) {
+ return;
+ }
+
+ std::vector s;
+
+ struct treeland_foreign_toplevel_handle_v1 *pos;
+ struct treeland_foreign_toplevel_handle_v1 *tmp;
+ wl_list_for_each_safe(pos, tmp, &dock_preview->manager->toplevels, link) {
+ void *p;
+ wl_array_for_each(p, surfaces) {
+ if (strcmp(static_cast(p), pos->identifier) == 0) {
+ s.push_back(wlr_surface_from_resource(pos->surface));
+ break;
+ }
+ }
+ }
+
+ struct treeland_dock_preview_context_v1_preview_event event = {
+ .toplevel = dock_preview,
+ .surfaces = s,
+ .x = x,
+ .y = y,
+ .direction = direction,
+ };
+
+ wl_signal_emit_mutable(&dock_preview->events.request_show, &event);
+}
+
static void treeland_foreign_toplevel_handle_set_fullscreen([[maybe_unused]] struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *output)
@@ -589,6 +654,34 @@ void treeland_foreign_toplevel_handle_v1_set_parent(
toplevel_update_idle_source(toplevel);
}
+void treeland_dock_preview_context_v1_enter(
+ struct treeland_dock_preview_context_v1 *toplevel)
+{
+ treeland_dock_preview_context_v1_send_enter(toplevel->resource);
+}
+
+void treeland_dock_preview_context_v1_leave(
+ struct treeland_dock_preview_context_v1 *toplevel)
+{
+ treeland_dock_preview_context_v1_send_leave(toplevel->resource);
+}
+
+void treeland_dock_preview_context_v1_destroy(
+ struct treeland_dock_preview_context_v1 *toplevel)
+{
+ if (!toplevel) {
+ return;
+ }
+
+ wl_signal_emit_mutable(&toplevel->events.destroy, toplevel);
+
+ wl_resource_set_user_data(toplevel->resource, NULL);
+ wl_list_remove(wl_resource_get_link(toplevel->resource));
+ wl_list_remove(&toplevel->link);
+
+ free(toplevel);
+}
+
void treeland_foreign_toplevel_handle_v1_destroy(
struct treeland_foreign_toplevel_handle_v1 *toplevel)
{
@@ -643,6 +736,21 @@ static void treeland_foreign_toplevel_resource_destroy(struct wl_resource *resou
wl_list_remove(wl_resource_get_link(resource));
}
+static void treeland_dock_preview_context_handle_show([[maybe_unused]] struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_array *surfaces,
+ int32_t x,
+ int32_t y,
+ uint32_t direction)
+{
+ dock_send_preview_event(resource, surfaces, x, y, direction);
+}
+
+static void treeland_dock_preview_context_handle_destroy([[maybe_unused]] struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
static struct wl_resource *create_toplevel_resource_for_resource(
struct treeland_foreign_toplevel_handle_v1 *toplevel, struct wl_resource *manager_resource)
{
@@ -700,8 +808,17 @@ treeland_foreign_toplevel_handle_v1_create(struct treeland_foreign_toplevel_mana
}
static const struct ztreeland_foreign_toplevel_manager_v1_interface
- treeland_foreign_toplevel_manager_impl = { .stop =
- treeland_foreign_toplevel_manager_handle_stop };
+ treeland_foreign_toplevel_manager_impl = {
+ .stop = treeland_foreign_toplevel_manager_handle_stop,
+ .get_dock_preview_context = treeland_foreign_toplevel_manager_handle_get_dock_preview_context,
+};
+
+struct treeland_foreign_toplevel_manager_v1 *foreign_toplevel_manager_from_resource(struct wl_resource *resource) {
+ assert(wl_resource_instance_of(resource, &ztreeland_foreign_toplevel_manager_v1_interface, &treeland_foreign_toplevel_manager_impl));
+ struct treeland_foreign_toplevel_manager_v1 *manager = static_cast(wl_resource_get_user_data(resource));
+ assert(manager != NULL);
+ return manager;
+}
static void treeland_foreign_toplevel_manager_handle_stop([[maybe_unused]] struct wl_client *client,
struct wl_resource *resource)
@@ -714,6 +831,41 @@ static void treeland_foreign_toplevel_manager_handle_stop([[maybe_unused]] struc
wl_resource_destroy(resource);
}
+static void treeland_dock_preview_context_resource_destroy(struct wl_resource *resource)
+{
+ wl_list_remove(wl_resource_get_link(resource));
+}
+
+static void treeland_foreign_toplevel_manager_handle_get_dock_preview_context(struct wl_client *client,
+ struct wl_resource *manager_resource,
+ struct wl_resource *relative_surface,
+ uint32_t id)
+{
+ auto *manager = foreign_toplevel_manager_from_resource(manager_resource);
+
+ struct treeland_dock_preview_context_v1 *context = static_cast(calloc(1, sizeof(*context)));
+ if (context == NULL) {
+ wl_resource_post_no_memory(manager_resource);
+ return;
+ }
+
+ uint32_t version = wl_resource_get_version(manager_resource);
+ struct wl_resource *resource = wl_resource_create(client, &treeland_dock_preview_context_v1_interface, version, id);
+ if (resource == NULL) {
+ free(context);
+ wl_resource_post_no_memory(manager_resource);
+ return;
+ }
+
+ wl_resource_set_implementation(resource, &treeland_dock_preview_context_impl, context, treeland_dock_preview_context_resource_destroy);
+
+ context->manager = manager;
+ context->relative_surface = relative_surface;
+ context->resource = resource;
+
+ wl_list_insert(&manager->resources, wl_resource_get_link(resource));
+}
+
static void treeland_foreign_toplevel_manager_resource_destroy(struct wl_resource *resource)
{
wl_list_remove(wl_resource_get_link(resource));
@@ -823,6 +975,7 @@ treeland_foreign_toplevel_manager_v1_create(struct wl_display *display)
}
wl_signal_init(&manager->events.destroy);
+ wl_signal_init(&manager->events.dock_preview_created);
wl_list_init(&manager->resources);
wl_list_init(&manager->toplevels);
diff --git a/src/treeland/protocols/foreign_toplevel_manager_impl.h b/src/treeland/protocols/foreign_toplevel_manager_impl.h
index 3e43169e..303c6d82 100644
--- a/src/treeland/protocols/foreign_toplevel_manager_impl.h
+++ b/src/treeland/protocols/foreign_toplevel_manager_impl.h
@@ -5,6 +5,8 @@
#include
+#include
+
struct treeland_foreign_toplevel_manager_v1 {
struct wl_event_loop *event_loop;
struct wl_global *global;
@@ -14,6 +16,7 @@ struct treeland_foreign_toplevel_manager_v1 {
struct wl_listener display_destroy;
struct {
+ struct wl_signal dock_preview_created;
struct wl_signal destroy;
} events;
@@ -38,16 +41,29 @@ struct treeland_foreign_toplevel_handle_v1_output {
struct wl_listener output_destroy;
};
+struct treeland_dock_preview_context_v1 {
+ struct treeland_foreign_toplevel_manager_v1 *manager;
+ struct wl_resource *resource;
+ struct wl_list link;
+ struct wl_resource *relative_surface;
+ struct {
+ struct wl_signal request_show;
+ struct wl_signal destroy;
+ } events;
+};
+
struct treeland_foreign_toplevel_handle_v1 {
struct treeland_foreign_toplevel_manager_v1 *manager;
struct wl_list resources;
struct wl_list link;
struct wl_event_source *idle_source;
+ struct wl_resource *surface;
char *title;
char *app_id;
char *identifier;
pid_t pid;
+
struct treeland_foreign_toplevel_handle_v1 *parent;
struct wl_list outputs; // treeland_foreign_toplevel_v1_output.link
uint32_t state; // enum treeland_foreign_toplevel_v1_state
@@ -98,6 +114,13 @@ struct treeland_foreign_toplevel_handle_v1_set_rectangle_event {
int32_t x, y, width, height;
};
+struct treeland_dock_preview_context_v1_preview_event {
+ struct treeland_dock_preview_context_v1 *toplevel;
+ std::vector surfaces;
+ int32_t x, y;
+ int32_t direction;
+};
+
struct treeland_foreign_toplevel_manager_v1 *
treeland_foreign_toplevel_manager_v1_create(struct wl_display *display);
@@ -135,3 +158,10 @@ void treeland_foreign_toplevel_handle_v1_set_fullscreen(
void treeland_foreign_toplevel_handle_v1_set_parent(
struct treeland_foreign_toplevel_handle_v1 *toplevel,
struct treeland_foreign_toplevel_handle_v1 *parent);
+
+void treeland_dock_preview_context_v1_enter(
+ struct treeland_dock_preview_context_v1 *toplevel);
+void treeland_dock_preview_context_v1_leave(
+ struct treeland_dock_preview_context_v1 *toplevel);
+void treeland_dock_preview_context_v1_destroy(
+ struct treeland_dock_preview_context_v1 *toplevel);
diff --git a/src/treeland/protocols/foreigntoplevelhandlev1.h b/src/treeland/protocols/foreigntoplevelhandlev1.h
index 3c443ca2..38f02b16 100644
--- a/src/treeland/protocols/foreigntoplevelhandlev1.h
+++ b/src/treeland/protocols/foreigntoplevelhandlev1.h
@@ -12,6 +12,7 @@ struct treeland_foreign_toplevel_handle_v1_minimized_event;
struct treeland_foreign_toplevel_handle_v1_activated_event;
struct treeland_foreign_toplevel_handle_v1_fullscreen_event;
struct treeland_foreign_toplevel_handle_v1_set_rectangle_event;
+struct treeland_dock_preview_context_v1_preview_event;
struct treeland_foreign_toplevel_manager_v1;
QW_USE_NAMESPACE
@@ -21,6 +22,7 @@ class QWOutput;
class QWDisplay;
QW_END_NAMESPACE
+class TreeLandDockPreviewContextV1;
class TreeLandForeignToplevelManagerV1Private;
class QW_EXPORT TreeLandForeignToplevelManagerV1 : public QObject, public QWObject
{
@@ -37,6 +39,7 @@ class QW_EXPORT TreeLandForeignToplevelManagerV1 : public QObject, public QWObje
Q_SIGNALS:
void beforeDestroy(TreeLandForeignToplevelManagerV1 *self);
+ void dockPreviewContextCreated(TreeLandDockPreviewContextV1 *context);
private:
TreeLandForeignToplevelManagerV1(treeland_foreign_toplevel_manager_v1 *handle, bool isOwner);
@@ -83,3 +86,31 @@ class TreeLandForeignToplevelHandleV1 : public QObject, public QWObject
private:
TreeLandForeignToplevelHandleV1(treeland_foreign_toplevel_handle_v1 *handle, bool isOwner);
};
+
+struct treeland_dock_preview_context_v1;
+class TreeLandDockPreviewContextV1Private;
+class TreeLandDockPreviewContextV1 : public QObject, public QWObject
+{
+ Q_OBJECT
+ QW_DECLARE_PRIVATE(TreeLandDockPreviewContextV1)
+public:
+ ~TreeLandDockPreviewContextV1() = default;
+
+ inline treeland_dock_preview_context_v1 *handle() const {
+ return QWObject::handle();
+ }
+
+ static TreeLandDockPreviewContextV1 *get(treeland_dock_preview_context_v1 *handle);
+ static TreeLandDockPreviewContextV1 *from(treeland_dock_preview_context_v1 *handle);
+
+ void enter();
+ void leave();
+
+Q_SIGNALS:
+ void beforeDestroy(TreeLandDockPreviewContextV1 *self);
+ void requestShow(treeland_dock_preview_context_v1_preview_event *event);
+
+private:
+ TreeLandDockPreviewContextV1(treeland_dock_preview_context_v1 *handle, bool isOwner);
+};
+
diff --git a/src/treeland/protocols/foreigntoplevelmanagerv1.cpp b/src/treeland/protocols/foreigntoplevelmanagerv1.cpp
index 3e2a119a..2a65c259 100644
--- a/src/treeland/protocols/foreigntoplevelmanagerv1.cpp
+++ b/src/treeland/protocols/foreigntoplevelmanagerv1.cpp
@@ -22,6 +22,7 @@ class TreeLandForeignToplevelManagerV1Private : public QWObjectPrivate
Q_ASSERT(!map.contains(handle));
map.insert(handle, qq);
sc.connect(&handle->events.destroy, this, &TreeLandForeignToplevelManagerV1Private::on_destroy);
+ sc.connect(&handle->events.dock_preview_created, this, &TreeLandForeignToplevelManagerV1Private::on_dock_preview_created);
}
~TreeLandForeignToplevelManagerV1Private() {
if (!m_handle)
@@ -38,6 +39,7 @@ class TreeLandForeignToplevelManagerV1Private : public QWObjectPrivate
}
void on_destroy(void *);
+ void on_dock_preview_created(void *);
static QHash map;
QW_DECLARE_PUBLIC(TreeLandForeignToplevelManagerV1)
@@ -52,6 +54,12 @@ void TreeLandForeignToplevelManagerV1Private::on_destroy(void *)
delete q_func();
}
+void TreeLandForeignToplevelManagerV1Private::on_dock_preview_created(void *data)
+{
+ auto *context = TreeLandDockPreviewContextV1::from(static_cast(data));
+ Q_EMIT q_func()->dockPreviewContextCreated(context);
+}
+
TreeLandForeignToplevelManagerV1::TreeLandForeignToplevelManagerV1(treeland_foreign_toplevel_manager_v1 *handle, bool isOwner)
: QObject(nullptr)
, QWObject(*new TreeLandForeignToplevelManagerV1Private(handle, isOwner, this))
diff --git a/src/treeland/quick/protocols/foreigntoplevelmanagerv1.cpp b/src/treeland/quick/protocols/foreigntoplevelmanagerv1.cpp
index b170abc3..3f33577e 100644
--- a/src/treeland/quick/protocols/foreigntoplevelmanagerv1.cpp
+++ b/src/treeland/quick/protocols/foreigntoplevelmanagerv1.cpp
@@ -5,6 +5,7 @@
#include "foreign-toplevel-manager-server-protocol.h"
#include "foreigntoplevelhandlev1.h"
+#include "foreign_toplevel_manager_impl.h"
#include
#include
@@ -238,6 +239,16 @@ void QuickForeignToplevelManagerV1::create() {
WQuickWaylandServerInterface::create();
d->manager = TreeLandForeignToplevelManagerV1::create(server()->handle());
+
+ connect(d->manager, &TreeLandForeignToplevelManagerV1::dockPreviewContextCreated, this, [this](TreeLandDockPreviewContextV1 *context) {
+ connect(context, &TreeLandDockPreviewContextV1::requestShow, this, [this](struct treeland_dock_preview_context_v1_preview_event *event) {
+ std::vector surfaces;
+ std::transform(event->surfaces.begin(), event->surfaces.end(), surfaces.begin(), [](wlr_surface* surface) {
+ return WSurface::fromHandle(surface);
+ });
+ Q_EMIT requestDockPreview(surfaces, WSurface::fromHandle(wlr_surface_from_resource(event->toplevel->resource)), QPoint(event->x, event->y));
+ });
+ });
}
QuickForeignToplevelManagerAttached *QuickForeignToplevelManagerV1::qmlAttachedProperties(QObject *target)
diff --git a/src/treeland/quick/protocols/foreigntoplevelmanagerv1.h b/src/treeland/quick/protocols/foreigntoplevelmanagerv1.h
index 3d9f0a2d..80d099c0 100644
--- a/src/treeland/quick/protocols/foreigntoplevelmanagerv1.h
+++ b/src/treeland/quick/protocols/foreigntoplevelmanagerv1.h
@@ -103,6 +103,7 @@ class QuickForeignToplevelManagerV1 : public WQuickWaylandServerInterface, publi
void requestFullscreen(WXdgSurface *surface, treeland_foreign_toplevel_handle_v1_fullscreen_event *event);
void requestClose(WXdgSurface *surface);
void rectangleChanged(WXdgSurface *surface, treeland_foreign_toplevel_handle_v1_set_rectangle_event *event);
+ void requestDockPreview(std::vector surfaces, WSurface *target, QPoint abs);
private:
void create() override;
diff --git a/src/treeland/quick/qml/CMakeLists.txt b/src/treeland/quick/qml/CMakeLists.txt
index 05841738..36219c18 100644
--- a/src/treeland/quick/qml/CMakeLists.txt
+++ b/src/treeland/quick/qml/CMakeLists.txt
@@ -23,6 +23,7 @@ qt_add_qml_module(treeland-qml
MiniDock.qml
WindowsSwitcher.qml
WindowsSwitcherPreview.qml
+ DockPreview.qml
RESOURCE_PREFIX
/qt/qml
)
diff --git a/src/treeland/quick/qml/DockPreview.qml b/src/treeland/quick/qml/DockPreview.qml
new file mode 100644
index 00000000..12c54df7
--- /dev/null
+++ b/src/treeland/quick/qml/DockPreview.qml
@@ -0,0 +1,110 @@
+import QtQuick
+import TreeLand.Utils
+
+Item {
+ id: root
+
+ property alias model: model
+
+ signal stopped
+ signal surfaceActivated(surface: XdgSurface)
+
+ visible: false
+
+ property var targetX: 0
+ property var targetY: 0
+
+ // surfaces: std::vector
+ // target: XdgSurface
+ // pos: abs pos
+ function start(surfaces, target, pos) {
+ filterModel.clear()
+
+ for (let surface of surfaces) {
+ filterModel.append(surface)
+ }
+
+ // 怎么知道target在output中的位置?
+ targetX = target.x + pos.x
+ targetY = target.y + pos.y
+
+ visible = true
+ }
+
+ function stop() {
+ visible = false
+ stopped()
+ }
+
+ Loader {
+ id: context
+ }
+
+ Component {
+ id: contextComponent
+
+ WindowsSwitcherPreview {
+ }
+ }
+
+ DockPreviewFilter {
+ id: filterModel
+ sourceModel: model
+ }
+
+ ListModel {
+ id: model
+ function removeSurface(surface) {
+ for (var i = 0; i < model.count; i++) {
+ if (model.get(i).source === surface) {
+ model.remove(i);
+ break;
+ }
+ }
+
+ filterModel.remove(surface.waylandSurface.surface)
+ }
+ }
+
+ Rectangle {
+ width: switcher.width
+ height: switcher.height
+ x: switcher.x
+ y: switcher.y
+ radius: 10
+ opacity: 0.4
+ }
+
+ Row {
+ id: switcher
+
+ x: targetX - width / 2
+ y: targetY
+
+ Repeater {
+ model: filterModel
+ Item {
+ required property XdgSurface surface
+ width: 180
+ height: 160
+ clip: true
+ visible: true
+ ShaderEffectSource {
+ anchors.centerIn: parent
+ width: 150
+ height: Math.min(surface.height * width / surface.width, width)
+ live: true
+ hideSource: false
+ smooth: true
+ sourceItem: surface
+ MouseArea {
+ onEntered: {
+ // TODO: 更新标题
+ surfaceActivated(surface)
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/treeland/quick/qml/LayerSurface.qml b/src/treeland/quick/qml/LayerSurface.qml
index b3555787..b12fb4bc 100644
--- a/src/treeland/quick/qml/LayerSurface.qml
+++ b/src/treeland/quick/qml/LayerSurface.qml
@@ -5,6 +5,7 @@ import QtQuick
import Waylib.Server
import QtQuick.Particles
import TreeLand
+import TreeLand.Utils
Item {
property alias waylandSurface: surfaceItem.surface
diff --git a/src/treeland/quick/qml/StackToplevelHelper.qml b/src/treeland/quick/qml/StackToplevelHelper.qml
index c2df1f66..303dee16 100644
--- a/src/treeland/quick/qml/StackToplevelHelper.qml
+++ b/src/treeland/quick/qml/StackToplevelHelper.qml
@@ -5,6 +5,7 @@ import QtQuick
import Waylib.Server
import QtQuick.Particles
import TreeLand
+import TreeLand.Utils
Item {
id: root
@@ -13,6 +14,7 @@ Item {
required property ToplevelSurface waylandSurface
required property ListModel dockModel
required property ListModel switcherModel
+ required property ListModel dockPreviewModel
required property DynamicCreatorComponent creator
property WindowDecoration decoration
property var quickForeignToplevelManageMapper: waylandSurface.TreeLandForeignToplevelManagerV1
@@ -204,6 +206,7 @@ Item {
}
switcherModel.append({ source: surface });
+ dockPreviewModel.append({ surface: surface, source: waylandSurface.surface });
} else { // if not mapped
if (waylandSurface.isMinimized) {
// mapped becomes false but not pendingDestroy
@@ -220,6 +223,7 @@ Item {
closeAnimation.item.start(surface)
}
switcherModel.removeSurface(surface)
+ dockPreviewModel.removeSurface(surface)
}
}
@@ -227,6 +231,7 @@ Item {
pendingDestroy = true
switcherModel.removeSurface(surface)
+ dockPreviewModel.removeSurface(surface)
if (!surface.visible || !closeAnimation.active) {
if (waylandSurface.isMinimized) {
diff --git a/src/treeland/quick/qml/StackWorkspace.qml b/src/treeland/quick/qml/StackWorkspace.qml
index 69c323d7..1af248b8 100644
--- a/src/treeland/quick/qml/StackWorkspace.qml
+++ b/src/treeland/quick/qml/StackWorkspace.qml
@@ -103,6 +103,7 @@ Item {
waylandSurface: surface.waylandSurface
dockModel: dock.model
switcherModel: switcher.model
+ dockPreviewModel: dockPreview.model
creator: toplevelComponent
decoration: decoration
}
@@ -283,6 +284,24 @@ Item {
}
}
+ Connections {
+ target: treelandForeignToplevelManager
+ function onRequestDockPreview(surfaces, target, abs) {
+ dockPreview.show(surfaces, getSurfaceItemFromWaylandSurface(target), abs)
+ }
+ }
+
+ DockPreview {
+ id: dockPreview
+ z: 1
+ anchors.fill: parent
+ visible: true
+ //onSurfaceActivated: (surface) => {
+ // surface.cancelMinimize()
+ // TreeLandHelper.activatedSurface = surface.waylandSurface
+ //}
+ }
+
Connections {
target: TreeLandHelper
function onSwitcherChanged(mode) {
diff --git a/src/treeland/quick/qml/TiledToplevelHelper.qml b/src/treeland/quick/qml/TiledToplevelHelper.qml
index a22c2278..5cc6b6f3 100644
--- a/src/treeland/quick/qml/TiledToplevelHelper.qml
+++ b/src/treeland/quick/qml/TiledToplevelHelper.qml
@@ -5,6 +5,7 @@ import QtQuick
import QtQuick.Controls
import Waylib.Server
import TreeLand
+import TreeLand.Utils
Item {
required property SurfaceItem surface
diff --git a/src/treeland/quick/qml/XdgSurface.qml b/src/treeland/quick/qml/XdgSurface.qml
index 1a374bdf..06b0ffc6 100644
--- a/src/treeland/quick/qml/XdgSurface.qml
+++ b/src/treeland/quick/qml/XdgSurface.qml
@@ -4,6 +4,7 @@
import QtQuick
import Waylib.Server
import TreeLand
+import TreeLand.Utils
XdgSurfaceItem {
id: surfaceItem
diff --git a/src/treeland/quick/utils/CMakeLists.txt b/src/treeland/quick/utils/CMakeLists.txt
index 2ca5c528..eae16c45 100644
--- a/src/treeland/quick/utils/CMakeLists.txt
+++ b/src/treeland/quick/utils/CMakeLists.txt
@@ -7,6 +7,7 @@ qt_add_qml_module(treeland-quick-utils
SOURCES
helper.cpp
treelandhelper.cpp
+ dockpreviewfilter.cpp
RESOURCE_PREFIX
/qt/qml
)
@@ -16,6 +17,7 @@ FILE_SET HEADERS
FILES
helper.h
treelandhelper.h
+ dockpreviewfilter.h
)
target_compile_definitions(treeland-quick-utils
diff --git a/src/treeland/quick/utils/dockpreviewfilter.cpp b/src/treeland/quick/utils/dockpreviewfilter.cpp
new file mode 100644
index 00000000..f930420a
--- /dev/null
+++ b/src/treeland/quick/utils/dockpreviewfilter.cpp
@@ -0,0 +1,48 @@
+// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "dockpreviewfilter.h"
+
+#include
+#include
+
+DockPreviewFilterProxyModel::DockPreviewFilterProxyModel(QObject *parent)
+ : QSortFilterProxyModel(parent)
+{
+ setFilterCaseSensitivity(Qt::CaseInsensitive);
+}
+
+bool DockPreviewFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
+{
+ QModelIndex modelIndex = this->sourceModel()->index(sourceRow, 0, sourceParent);
+ auto v = sourceModel()->data(modelIndex, sourceModel()->roleNames().key("source"));
+
+ if (v.isNull()) {
+ return false;
+ }
+
+ auto find = std::ranges::find_if(m_surfaces, [v](Waylib::Server::WSurface *s) {
+ return s == v.value();
+ });
+
+ // return find != m_surfaces.end();
+ return true;
+}
+
+void DockPreviewFilterProxyModel::clear()
+{
+ m_surfaces.clear();
+}
+
+void DockPreviewFilterProxyModel::append(Waylib::Server::WSurface *surface)
+{
+ m_surfaces.push_back(surface);
+}
+
+void DockPreviewFilterProxyModel::remove(Waylib::Server::WSurface *surface)
+{
+ std::erase_if(m_surfaces, [surface](auto *s) {
+ return s == surface;
+ });
+}
diff --git a/src/treeland/quick/utils/dockpreviewfilter.h b/src/treeland/quick/utils/dockpreviewfilter.h
new file mode 100644
index 00000000..af31239c
--- /dev/null
+++ b/src/treeland/quick/utils/dockpreviewfilter.h
@@ -0,0 +1,30 @@
+// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include
+#include
+
+#include
+
+class DockPreviewFilterProxyModel : public QSortFilterProxyModel
+{
+ Q_OBJECT
+ QML_ELEMENT
+ QML_NAMED_ELEMENT(DockPreviewFilter)
+
+public:
+ explicit DockPreviewFilterProxyModel(QObject *parent = nullptr);
+
+ Q_INVOKABLE void clear();
+ Q_INVOKABLE void append(Waylib::Server::WSurface *surface);
+ Q_INVOKABLE void remove(Waylib::Server::WSurface *surface);
+
+protected:
+ bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
+
+private:
+ std::vector m_surfaces;
+};