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; +};