Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions src/core/qml/Animations/MinimizeAnimation.qml
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,7 @@ Item {
property int animationDuration: duration
readonly property real minimizedRotation: -30
readonly property bool showShadow: !target.noDecoration
&& (direction === MinimizeAnimation.Direction.Hide
? target.previousSurfaceState === SurfaceWrapper.State.Normal
: target.surfaceState === SurfaceWrapper.State.Normal)
&& target.surfaceState === SurfaceWrapper.State.Normal
Comment thread
zccrs marked this conversation as resolved.

function start() {
configureAnimation(direction === MinimizeAnimation.Direction.Hide
Expand Down
6 changes: 3 additions & 3 deletions src/core/qml/WorkspaceProxy.qml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2024 UnionTech Software Technology Co., Ltd.
// Copyright (C) 2024-2026 UnionTech Software Technology Co., Ltd.
// SPDX-License-Identifier: Apache-2.0 OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only

import QtQuick
Expand All @@ -25,7 +25,7 @@ Item {
y: surface.y - output.outputItem.y
z: orderIndex
active: surface.ownsOutput === output
&& surface.surfaceState !== SurfaceWrapper.State.Minimized
&& !surface.minimized
sourceComponent: SurfaceProxy {
surface: loader.surface
fullProxy: true
Expand All @@ -44,7 +44,7 @@ Item {
y: surface.y - output.outputItem.y
z: orderIndex
active: surface.ownsOutput === output
&& surface.surfaceState !== SurfaceWrapper.State.Minimized
&& !surface.minimized
sourceComponent: SurfaceProxy {
surface: allLoader.surface
fullProxy: true
Expand Down
1 change: 1 addition & 0 deletions src/modules/resource/treelandremotesource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,7 @@ WindowInfo TreelandRemoteSource::buildWindowInfo(SurfaceWrapper *surface,
info.setPosition(surface->position());
info.setVisible(surface->isVisible());
info.setActive(surface->isActivated());
info.setMinimized(surface->isMinimized());
info.setType(static_cast<int>(surface->type()));
info.setState(static_cast<int>(surface->surfaceState()));

Expand Down
1 change: 1 addition & 0 deletions src/modules/resource/treelandwindowtree.rep
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ POD WindowInfo(
int state,
bool visible,
bool active,
bool minimized,
QRectF geometry,
QRectF titlebarGeometry,
QRectF boundingRect,
Expand Down
14 changes: 7 additions & 7 deletions src/plugins/multitaskview/multitaskview.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -159,9 +159,9 @@ void MultitaskviewSurfaceModel::initializeModel()
&MultitaskviewSurfaceModel::handleWrapperOutputChanged,
Qt::UniqueConnection);
connect(surface,
&SurfaceWrapper::surfaceStateChanged,
&SurfaceWrapper::minimizedChanged,
this,
&MultitaskviewSurfaceModel::handleSurfaceStateChanged,
&MultitaskviewSurfaceModel::handleMinimizedChanged,
Qt::UniqueConnection);
}
std::sort(m_data.begin(),
Expand Down Expand Up @@ -498,7 +498,7 @@ void MultitaskviewSurfaceModel::handleWrapperOutputChanged()
}
}

void MultitaskviewSurfaceModel::handleSurfaceStateChanged()
void MultitaskviewSurfaceModel::handleMinimizedChanged()
{
auto surface = qobject_cast<SurfaceWrapper *>(sender());
Q_ASSERT(surface);
Expand Down Expand Up @@ -559,9 +559,9 @@ void MultitaskviewSurfaceModel::handleSurfaceAdded(SurfaceWrapper *surface)
&MultitaskviewSurfaceModel::handleWrapperOutputChanged,
Qt::UniqueConnection);
connect(surface,
&SurfaceWrapper::surfaceStateChanged,
&SurfaceWrapper::minimizedChanged,
this,
&MultitaskviewSurfaceModel::handleSurfaceStateChanged,
&MultitaskviewSurfaceModel::handleMinimizedChanged,
Qt::UniqueConnection);
if (surface->ownsOutput() == output()) {
if (surfaceReady(surface)) {
Expand Down Expand Up @@ -701,9 +701,9 @@ void MultitaskviewSurfaceModel::disconnectSurface(SurfaceWrapper *surface)
this,
&MultitaskviewSurfaceModel::handleWrapperOutputChanged);
disconnect(surface,
&SurfaceWrapper::surfaceStateChanged,
&SurfaceWrapper::minimizedChanged,
this,
&MultitaskviewSurfaceModel::handleSurfaceStateChanged);
&MultitaskviewSurfaceModel::handleMinimizedChanged);
disconnect(surface,
&SurfaceWrapper::normalGeometryChanged,
this,
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/multitaskview/multitaskview.h
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ class MultitaskviewSurfaceModel : public QAbstractListModel
std::pair<int, int> commitAndGetUpdateRange(const QList<ModelDataPtr> &rawData);
void handleWrapperGeometryChanged();
void handleWrapperOutputChanged();
void handleSurfaceStateChanged();
void handleMinimizedChanged();
void handleSurfaceMappedChanged();
void handleSurfaceAdded(SurfaceWrapper *surface);
void handleSurfaceRemoved(SurfaceWrapper *surface);
Expand Down
115 changes: 67 additions & 48 deletions src/surface/surfacewrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1175,6 +1175,11 @@ void SurfaceWrapper::setSurfaceState(State newSurfaceState)
// request until the visual animation finishes.
abortGeometryAnimation();

if (!isVisible()) {
setSurfaceStateDirectly(newSurfaceState);
return;
}

const QRectF targetGeometry = targetGeometryForState(newSurfaceState);

if (targetGeometry.isValid()) {
Expand Down Expand Up @@ -1254,7 +1259,7 @@ bool SurfaceWrapper::isMaximized() const

bool SurfaceWrapper::isMinimized() const
{
return m_surfaceState == State::Minimized;
return m_minimized;
}

bool SurfaceWrapper::isTiling() const
Expand Down Expand Up @@ -1615,36 +1620,17 @@ void SurfaceWrapper::doSetSurfaceState(State newSurfaceState)
return;
}

const bool wasMinimized = (m_surfaceState == State::Minimized);
const bool willBeMinimized = (newSurfaceState == State::Minimized);
const bool needMinimizeLinkage = (wasMinimized != willBeMinimized);

setVisibleDecoration(newSurfaceState == State::Minimized || newSurfaceState == State::Normal);
setVisibleDecoration(newSurfaceState == State::Normal);
setNoCornerRadius(newSurfaceState == State::Maximized || newSurfaceState == State::Fullscreen
|| newSurfaceState == State::Tiling);

m_previousSurfaceState.setValueBypassingBindings(m_surfaceState);
m_surfaceState.setValueBypassingBindings(newSurfaceState);

// Keep modal/parent minimize linkage ahead of this surface's own state change
// so focus fallback never sees the parent in the old state first.
if (needMinimizeLinkage && modal() && m_parentSurface) {
if (willBeMinimized && !m_parentSurface->isMinimized()) {
m_parentSurface->minimize(false);
} else if (!willBeMinimized && m_parentSurface->isMinimized()) {
m_parentSurface->restoreFromMinimized(false);
}
}

switch (m_previousSurfaceState.value()) {
case State::Maximized:
m_shellSurface->setMaximize(false);
break;
case State::Minimized:
m_shellSurface->setMinimize(false);
updateFocusControlState(FocusControlState::UnMinimized, true);
updateHasActiveCapability(ActiveControlState::UnMinimized, true);
break;
case State::Fullscreen:
m_shellSurface->setFullScreen(false);
break;
Expand All @@ -1661,11 +1647,6 @@ void SurfaceWrapper::doSetSurfaceState(State newSurfaceState)
case State::Maximized:
m_shellSurface->setMaximize(true);
break;
case State::Minimized:
updateFocusControlState(FocusControlState::UnMinimized, false);
updateHasActiveCapability(ActiveControlState::UnMinimized, false);
m_shellSurface->setMinimize(true);
break;
case State::Fullscreen:
m_shellSurface->setFullScreen(true);
break;
Expand All @@ -1679,19 +1660,6 @@ void SurfaceWrapper::doSetSurfaceState(State newSurfaceState)
m_surfaceState.notify();
updateTitleBar();
updateVisible();

if (needMinimizeLinkage) {
for (SurfaceWrapper *child : std::as_const(m_subSurfaces)) {
if (willBeMinimized && child->modal())
continue; // Modal children stay visible when parent is minimized.
if (child->isMinimized() != willBeMinimized) {
if (willBeMinimized)
child->minimize(false);
else
child->restoreFromMinimized(false);
}
}
}
}

void SurfaceWrapper::onAnimationReady()
Expand Down Expand Up @@ -1924,21 +1892,76 @@ void SurfaceWrapper::setRadius(qreal newRadius)

void SurfaceWrapper::minimize(bool onAnimation)
{
if (m_surfaceState == State::Minimized)
if (m_wrapperAboutToRemove)
return;

if (m_minimized)
return;

// The parent container may reject state changes during interactive edge resize.
if (container()->filterSurfaceStateChange(this, m_surfaceState, m_surfaceState))
return;

abortGeometryAnimation();

m_minimized = true;
Comment thread
glyvut marked this conversation as resolved.

if (!m_shellSurface) {
updateVisible();
return;
setSurfaceState(State::Minimized);
}

// Keep modal/parent minimize linkage ahead of this surface's own state change
// so focus fallback never sees the parent in the old state first.
if (modal() && m_parentSurface && !m_parentSurface->isMinimized())
m_parentSurface->minimize(false);

m_shellSurface->setMinimize(true);
Comment thread
glyvut marked this conversation as resolved.
updateFocusControlState(FocusControlState::UnMinimized, false);
updateHasActiveCapability(ActiveControlState::UnMinimized, false);
updateVisible();

for (SurfaceWrapper *child : std::as_const(m_subSurfaces)) {
if (child->modal())
continue; // Modal children stay visible when parent is minimized.
if (!child->isMinimized())
child->minimize(false);
}

if (onAnimation)
startMinimizeAnimation(iconGeometry(), CLOSE_ANIMATION);
}

void SurfaceWrapper::restoreFromMinimized(bool onAnimation)
{
if (m_surfaceState != State::Minimized && m_hideByshowDesk)
if (m_wrapperAboutToRemove)
return;

if (!m_minimized && m_hideByshowDesk)
return;
if (!m_hideByshowDesk)
setHideByShowDesk(true);

doSetSurfaceState(m_previousSurfaceState);
m_minimized = false;

if (!m_shellSurface) {
updateVisible();
} else {
if (modal() && m_parentSurface && m_parentSurface->isMinimized())
m_parentSurface->restoreFromMinimized(false);

updateFocusControlState(FocusControlState::UnMinimized, true);
updateHasActiveCapability(ActiveControlState::UnMinimized, true);

m_shellSurface->setMinimize(false);
updateVisible();

for (SurfaceWrapper *child : std::as_const(m_subSurfaces)) {
if (child->isMinimized())
child->restoreFromMinimized(false);
}
}

if (onAnimation)
startMinimizeAnimation(iconGeometry(), OPEN_ANIMATION);
}
Expand All @@ -1952,8 +1975,7 @@ void SurfaceWrapper::maximize()
return;
}

if (m_surfaceState == State::Minimized || m_surfaceState == State::Fullscreen
|| !isMaximizable())
if (m_surfaceState == State::Fullscreen || !isMaximizable())
return;

setSurfaceState(State::Maximized);
Expand Down Expand Up @@ -2022,9 +2044,6 @@ void SurfaceWrapper::enterFullscreen(WOutput *targetOutput)
return;
}

if (m_surfaceState == State::Minimized)
return;

if (targetOutput) {
auto *helper = Helper::instance();
auto *target = helper ? helper->getOutput(targetOutput) : nullptr;
Expand Down
8 changes: 7 additions & 1 deletion src/surface/surfacewrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ class SurfaceWrapper : public QQuickItem
Q_PROPERTY(bool isResizable READ isResizable NOTIFY resizableChanged FINAL)
Q_PROPERTY(bool isMaximizable READ isMaximizable NOTIFY maximizableChanged FINAL)
Q_PROPERTY(bool modal READ modal NOTIFY modalChanged FINAL)
Q_PROPERTY(bool minimized READ isMinimized NOTIFY minimizedChanged FINAL)

public:
enum class Type
Expand All @@ -99,7 +100,6 @@ class SurfaceWrapper : public QQuickItem
{
Normal,
Maximized,
Minimized,
Fullscreen,
Tiling,
Comment thread
zccrs marked this conversation as resolved.
};
Expand Down Expand Up @@ -354,6 +354,7 @@ public Q_SLOTS:
void positionAutomaticChanged();
void previousSurfaceStateChanged();
void surfaceStateChanged();
void minimizedChanged();
void radiusChanged();
void moveRequested();
void resizeRequested(Qt::Edges edges);
Expand Down Expand Up @@ -498,6 +499,11 @@ public Q_SLOTS:
m_surfaceState,
State::Normal,
&SurfaceWrapper::surfaceStateChanged)
Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(SurfaceWrapper,
bool,
m_minimized,
false,
&SurfaceWrapper::minimizedChanged)
int m_workspaceId = -1;
int m_explicitAlwaysOnTop = 0;
bool m_explicitAlwaysOnBottom = false;
Expand Down
4 changes: 2 additions & 2 deletions tests/protocols/INDEX.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Wayland 线上请求与事件;本文档规定发出请求后,测试必须观
| [capture-unstable-v1](treeland-capture-unstable-v1/README.md) | E / V / P | 真实窗口选择、frame copy 与目标 buffer 像素;无 source 错误 |
| [dde-shell-v1](treeland-dde-shell-v1/README.md) | E / P | mapped wrapper 的 DDE 元数据、锁屏;picker 的真实选中/PID 回传测试待执行确认 |
| [ddm-v1](treeland-ddm-v1/README.md) | I | 客户端连接生命周期 |
| [foreign-toplevel-manager-v2](treeland-foreign-toplevel-manager-v2/README.md) | E | 真实 toplevel、dock preview、窗口状态、激活/焦点与 icon rectangle |
| [foreign-toplevel-manager-v2](treeland-foreign-toplevel-manager-v2/README.md) | E | 真实 toplevel、dock preview、窗口状态(含最小化与布局正交)、激活/焦点与 icon rectangle |
| [input-manager-unstable-v1](treeland-input-manager-unstable-v1/README.md) | I / E(可选) | 默认空设备 manager 生命周期;uinput 驱动真实 libinput capability 热插拔 |
| [keyboard-state-notify-unstable-v1](treeland-keyboard-state-notify-unstable-v1/README.md) | P | watcher 配置与空键盘/空 modifier 边界 |
| [output-manager-v1](treeland-output-manager-v1/README.md) | I / P | 真实 `wl_output` 的 primary-output 链路 |
Expand Down Expand Up @@ -80,7 +80,7 @@ request stub 算作 request 覆盖;生成的 client-protocol 文件本身不
| capture-unstable-v1 | 8 / 11 | `source_ready/failed`、`buffer/buffer_done/ready/failed`;64×64 红色像素读回 | session 的 `start/frame_done`,及 `frame/object/ready/cancel` 持久流;OUTPUT/REGION、cursor、mask |
| dde-shell-v1 | 26 / 27 | checker/active/picker 事件;真实 wrapper DDE 元数据、lockscreen、picker PID | `set_xwindow_position_relative`;multitask 只证明 `toggle` 请求/信号,未证明真实 UI 状态;`shutdown/switch_user` 外部会话流程 |
| ddm-v1 | 0 / 7 | 无未请求 VT event;生产连接生命周期 | 所有会话/渲染控制 request 与 `switch_to_vt/acquire_vt` 的实际系统流程 |
| foreign-toplevel-manager-v2 | 16 / 16 | `toplevel/identifier/closed`;真实最小化、最大化、全屏、焦点与 icon rectangle | `pid/title/app_id/output_enter/output_leave/state/done/parent` payload;指定 `wl_output` 的 fullscreen hint、preview 像素 |
| foreign-toplevel-manager-v2 | 16 / 16 | `toplevel/identifier/closed`;真实最小化、最大化、全屏、焦点与 icon rectangle;最小化与布局状态正交(含取消布局后不卡在最小化) | `pid/title/app_id/output_enter/output_leave/state/done/parent` payload;指定 `wl_output` 的 fullscreen hint、preview 像素;`Tiling` 组合 |
| input-manager-unstable-v1 | 1 / 22 | 默认测试仅证明空设备 manager 可绑定;uinput target 断言 Keyboard capability 热插拔 | settings/apply、真实 mouse/touchpad 配置生效、无设备 failed;uinput E 层需显式启用并实际执行 |
| keyboard-state-notify-unstable-v1 | 6 / 6 | watcher 配置、`apply` 的空键盘/空 modifier 边界 | `current_state/state_changed`、多 watcher、初始 locked、seat 销毁、重复 apply 与物理键盘对照 |
| output-manager-v1 | 4 / 7 | `primary_output`;未知 output 的 color-control 错误 | `set_color_temperature/set_brightness/commit` 成功路径及 `result/color_temperature/brightness`,真实 output/像素变化 |
Expand Down
10 changes: 10 additions & 0 deletions tests/protocols/treeland-foreign-toplevel-manager-v2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
| 最小化与关闭 | 对 handle 发 `set_minimized`、`unset_minimized`、`close` | 真实 wrapper 的 minimized 状态变化;xdg client 收到 close |
| 最大化 | 对 handle 发 `set_maximized`、`unset_maximized` | 同一真实 `SurfaceWrapper` 进入、退出 `Maximized` 状态 |
| 全屏 | 对 handle 发 `set_fullscreen(NULL)`、`unset_fullscreen` | 同一真实 `SurfaceWrapper` 进入、退出 `Fullscreen` 状态 |
| 布局与最小化正交 | `set_maximized`/`set_fullscreen` 后发 `set_minimized`、`unset_minimized`,再 `unset_maximized`/`unset_fullscreen` | 最小化期间 `SurfaceWrapper::surfaceState()` 保持布局状态且窗口不可见;恢复后仍为该布局状态并重新可见;取消布局状态后回到 `Normal` 且不再最小化 |
| 激活 | 对 handle 发 `activate(wl_seat)` | `Helper` 将 wrapper 设为 activated,且它成为该 seat 的真实 keyboard focus surface |
| 窗口代表区域 | 对 handle 的 mapped xdg `wl_surface` 发 `set_icon_geometry(11,12,130,140)` | production rectangle handler 将局部坐标换算为 wrapper 的 `iconGeometry`:`wrapper.position + QRect(11,12,130,140)` |

Expand All @@ -40,6 +41,12 @@ minimized;`set_maximized`/`unset_maximized` 分别改变 wrapper 的 `Maximize
`set_fullscreen(NULL)`/`unset_fullscreen` 分别改变其 `Fullscreen` 状态。这里读取的是
compositor 的 `SurfaceWrapper::surfaceState()`,不是 foreign handle 自身缓存的 state event。

最小化标志与布局状态正交:在 `Maximized` 或 `Fullscreen` 状态下发 `set_minimized`,测试断言
`SurfaceWrapper::isMinimized()` 为真、`surfaceState()` 仍为原布局状态、且窗口不可见;
`unset_minimized` 后布局状态不变且窗口重新可见;随后 `unset_maximized`/`unset_fullscreen`
必须回到 `Normal`、可见且不再最小化。这覆盖了“全屏→最小化→恢复→取消全屏后卡在最小化”的
回归路径,而不只是顺序地单独测试各状态。

`activate(wl_seat)` 进入 manager 连接的
`Helper::forceActivateSurface(wrapper, ..., seat)`,测试读取 wrapper 的 activated 标记和
root surface container 默认 seat container 的 `keyboardFocusSurface()`,两者都必须指向同一
Expand All @@ -55,6 +62,9 @@ mapped wrapper。`set_icon_geometry` 则
`set/unset_fullscreen`、`activate`、`set_icon_geometry`、`close`,以及 manager/context 的
`stop`、`get_dock_preview_context`、`show`、`show_tooltip`、`close`、`destroy`。

布局与最小化的组合已覆盖 `Maximized`、`Fullscreen` 两种布局;`Tiling` 没有对应的 foreign
handle 请求,仍未被组合覆盖。

仍未验证 dock UI 或 preview 的渲染像素;`output_enter` / `output_leave`、title、app-id、pid、
parent 和 foreign handle 对 state event 的线上内容也尚未逐项断言。`set_fullscreen` 当前传
`NULL` output,因此未覆盖“指定 `wl_output` 是 fullscreen hint”这一分支。fixture 的
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ extern "C" void ftm_read_server_state(void *data)
g_state.output_ready = !Helper::instance()->rootSurfaceContainer()->outputs().isEmpty() ? 1 : 0;
g_state.wrapper_minimized = g_wrapper && g_wrapper->shellSurface()
&& g_wrapper->shellSurface()->isMinimized() ? 1 : 0;
g_state.wrapper_self_minimized = g_wrapper && g_wrapper->isMinimized() ? 1 : 0;
g_state.wrapper_state =
g_wrapper ? static_cast<int>(g_wrapper->surfaceState()) : -1;
g_state.wrapper_visible = g_wrapper && g_wrapper->isVisible() ? 1 : 0;
g_state.wrapper_maximized = g_wrapper && g_wrapper->isMaximized() ? 1 : 0;
g_state.wrapper_fullscreen = g_wrapper
&& g_wrapper->surfaceState() == SurfaceWrapper::State::Fullscreen ? 1 : 0;
Expand Down
Loading
Loading