Skip to content
Draft
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
2 changes: 2 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ set ( deepin-screen-recorder_HDRS
widgets/tooltips.h
widgets/filter.h
utils/screengrabber.h
utils/treelandtoolbarplacement.h
dbusinterface/drawinterface.h
screen_shot_event.h
lib/GifH/gif.h
Expand Down Expand Up @@ -145,6 +146,7 @@ set ( deepin-screen-recorder_SRCS
widgets/filter.cpp
utils/desktopinfo.cpp
utils/screengrabber.cpp
utils/treelandtoolbarplacement.cpp
dbusinterface/drawinterface.cpp
screen_shot_event.cpp
RecorderRegionShow.cpp
Expand Down
64 changes: 54 additions & 10 deletions src/main_window.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "utils/configsettings.h"
#include "utils/shortcut.h"
#include "utils/screengrabber.h"
#include "utils/treelandtoolbarplacement.h"
#include "utils/log.h"
#include "camera_process.h"
#include "widgets/tooltips.h"
Expand Down Expand Up @@ -8081,7 +8082,24 @@ void MainWindow::updateCaptureRegion()
recordHeight = region.height();

m_toolBar->showWidget();
const QPoint pos(region.x(), qMin(region.bottom(), height() - 2 * m_toolBar->height()));
m_toolBar->adjustSize();

QList<QRect> screens;
const auto qScreens = QGuiApplication::screens();
screens.reserve(qScreens.size());
for (const QScreen *screen : qScreens) {
if (screen) {
screens.append(screen->geometry());
}
}
QRect fallback = geometry();
if (fallback.isEmpty() && QGuiApplication::primaryScreen()) {
fallback = QGuiApplication::primaryScreen()->geometry();
}
const QRect screenGeom =
TreelandToolBarPlacement::pickScreenGeometry(region, screens, fallback);
const QPoint pos = TreelandToolBarPlacement::placeToolBar(
region, m_toolBar->size(), screenGeom, TOOLBAR_Y_SPACING);
m_toolBar->showAt(pos);

if (windowHandle() && m_toolBar->windowHandle()
Expand Down Expand Up @@ -8289,21 +8307,48 @@ void MainWindow::onTreelandSwitchToShotUI()
m_toolBar->setFocusPolicy(Qt::NoFocus);
m_toolBar->setWindowFlags(m_toolBar->windowFlags() | Qt::Tool | Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint);

QList<QRect> screens;
const auto qScreens = QGuiApplication::screens();
screens.reserve(qScreens.size());
for (const QScreen *screen : qScreens) {
if (screen) {
screens.append(screen->geometry());
}
}
QRect fallback = geometry();
#if (QT_VERSION_MAJOR == 5)
const QRect scr = QApplication::desktop()->screenGeometry();
if (fallback.isEmpty()) {
fallback = QApplication::desktop()->screenGeometry();
}
#elif (QT_VERSION_MAJOR == 6)
const QRect scr = QGuiApplication::primaryScreen() ? QGuiApplication::primaryScreen()->geometry() : this->geometry();
if (fallback.isEmpty() && QGuiApplication::primaryScreen()) {
fallback = QGuiApplication::primaryScreen()->geometry();
}
#endif
const QRect restoreRegion = m_hasLastTreelandShotRegion
? m_lastTreelandShotRegion
: QRect(recordX, recordY, recordWidth, recordHeight);
const QRect screenGeom =
TreelandToolBarPlacement::pickScreenGeometry(restoreRegion, screens, fallback);

m_toolBar->hide();
m_toolBar->showWidget();
m_toolBar->adjustSize();

QPoint targetPos;
if (m_hasLastTreelandShotToolBarPos) {
targetPos = m_lastTreelandShotToolBarPos;
targetPos = TreelandToolBarPlacement::clampToScreen(
m_lastTreelandShotToolBarPos, m_toolBar->size(), screenGeom);
} else if (!restoreRegion.isEmpty() && restoreRegion.isValid()) {
targetPos = TreelandToolBarPlacement::placeToolBar(
restoreRegion, m_toolBar->size(), screenGeom, TOOLBAR_Y_SPACING);
} else {
targetPos = QPoint(scr.x() + scr.width() / 2 - m_toolBar->width() / 2,
scr.y() + scr.height() / 2 - m_toolBar->height() / 2);
targetPos = TreelandToolBarPlacement::clampToScreen(
QPoint(screenGeom.x() + screenGeom.width() / 2 - m_toolBar->width() / 2,
screenGeom.y() + screenGeom.height() / 2 - m_toolBar->height() / 2),
m_toolBar->size(),
screenGeom);
}
m_toolBar->hide();
m_toolBar->showWidget();
m_toolBar->adjustSize();
m_toolBar->showAt(targetPos);
m_toolBar->raise();
m_toolBar->show();
Expand Down Expand Up @@ -8354,4 +8399,3 @@ void MainWindow::initAudioAndCameraWatchers()
qCDebug(dsrApp) << "摄像头监视器初始化完成";
}
}

2 changes: 2 additions & 0 deletions src/src.pro
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ HEADERS += main_window.h \
widgets/tooltips.h \
widgets/filter.h \
utils/screengrabber.h \
utils/treelandtoolbarplacement.h \
RecorderRegionShow.h \
recordertablet.h \
dbusinterface/ocrinterface.h \
Expand Down Expand Up @@ -312,6 +313,7 @@ SOURCES += main.cpp \
widgets/tooltips.cpp \
widgets/filter.cpp \
utils/screengrabber.cpp \
utils/treelandtoolbarplacement.cpp \
RecorderRegionShow.cpp \
recordertablet.cpp \
dbusinterface/ocrinterface.cpp \
Expand Down
126 changes: 126 additions & 0 deletions src/utils/treelandtoolbarplacement.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// SPDX-FileCopyrightText: 2026 svan71
//
// SPDX-License-Identifier: GPL-3.0-or-later

#include "treelandtoolbarplacement.h"

#include <algorithm>

Check warning on line 7 in src/utils/treelandtoolbarplacement.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <algorithm> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <limits>

Check warning on line 8 in src/utils/treelandtoolbarplacement.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <limits> not found. Please note: Cppcheck does not need standard library headers to get proper results.

namespace TreelandToolBarPlacement {

namespace {

bool toolbarFitsAt(const QPoint &topLeft,
const QSize &toolBarSize,
const QRect &screenGeom)
{
if (toolBarSize.width() <= 0 || toolBarSize.height() <= 0 || screenGeom.isEmpty()) {
return false;
}
const QRect bar(topLeft, toolBarSize);
return screenGeom.contains(bar);
}

} // namespace

QRect pickScreenGeometry(const QRect &captureRegion,

Check warning on line 27 in src/utils/treelandtoolbarplacement.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

The function 'pickScreenGeometry' is never used.
const QList<QRect> &screens,
const QRect &fallback)
{
if (screens.isEmpty()) {
return fallback;
}

const QPoint center = captureRegion.center();
for (const QRect &screen : screens) {
if (!screen.isEmpty() && screen.contains(center)) {

Check warning on line 37 in src/utils/treelandtoolbarplacement.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Consider using std::find_if algorithm instead of a raw loop.
return screen;
}
}

QRect best;
int bestArea = -1;
for (const QRect &screen : screens) {
if (screen.isEmpty()) {
continue;
}
const QRect inter = screen.intersected(captureRegion);
const int area = inter.isEmpty() ? 0 : inter.width() * inter.height();
if (area > bestArea) {
bestArea = area;
best = screen;
}
}

if (bestArea > 0 && !best.isEmpty()) {
return best;
}

// No intersection: pick the screen whose center is nearest the capture center.
best = screens.first();
qint64 bestDist = std::numeric_limits<qint64>::max();
for (const QRect &screen : screens) {
if (screen.isEmpty()) {
continue;
}
const QPoint sc = screen.center();
const qint64 dx = qint64(sc.x()) - center.x();
const qint64 dy = qint64(sc.y()) - center.y();
const qint64 dist = dx * dx + dy * dy;
if (dist < bestDist) {
bestDist = dist;
best = screen;
}
}
return best.isEmpty() ? fallback : best;
}

QPoint clampToScreen(const QPoint &topLeft,
const QSize &toolBarSize,
const QRect &screenGeom)
{
if (screenGeom.isEmpty() || toolBarSize.width() <= 0 || toolBarSize.height() <= 0) {
return topLeft;
}

const int maxX = screenGeom.x() + std::max(0, screenGeom.width() - toolBarSize.width());
const int maxY = screenGeom.y() + std::max(0, screenGeom.height() - toolBarSize.height());
const int x = std::clamp(topLeft.x(), screenGeom.x(), maxX);
const int y = std::clamp(topLeft.y(), screenGeom.y(), maxY);
return QPoint(x, y);
}

QPoint placeToolBar(const QRect &captureRegion,

Check warning on line 94 in src/utils/treelandtoolbarplacement.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

The function 'placeToolBar' is never used.
const QSize &toolBarSize,
const QRect &screenGeom,
int ySpacing)
{
if (screenGeom.isEmpty() || toolBarSize.width() <= 0 || toolBarSize.height() <= 0) {
return clampToScreen(captureRegion.topLeft(), toolBarSize, screenGeom);
}

// Right-align to the capture region (same preference as updateToolBarPos on X11).
const int preferredX = captureRegion.x() + captureRegion.width() - toolBarSize.width();
const int x = clampToScreen(QPoint(preferredX, screenGeom.y()), toolBarSize, screenGeom).x();

const int spacing = std::max(0, ySpacing);

// Prefer outside below the selection.
const int yBelow = captureRegion.y() + captureRegion.height() + spacing;
if (toolbarFitsAt(QPoint(x, yBelow), toolBarSize, screenGeom)) {
return QPoint(x, yBelow);
}

// Else outside above the selection.
const int yAbove = captureRegion.y() - toolBarSize.height() - spacing;
if (toolbarFitsAt(QPoint(x, yAbove), toolBarSize, screenGeom)) {
return QPoint(x, yAbove);
}

// Neither outside slot fits: place inside the capture near its top, then clamp.
const int yInside = captureRegion.y() + spacing;
return clampToScreen(QPoint(x, yInside), toolBarSize, screenGeom);
}

} // namespace TreelandToolBarPlacement
59 changes: 59 additions & 0 deletions src/utils/treelandtoolbarplacement.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// SPDX-FileCopyrightText: 2026 svan71
//
// SPDX-License-Identifier: GPL-3.0-or-later

#ifndef TREELANDTOOLBARPLACEMENT_H
#define TREELANDTOOLBARPLACEMENT_H

#include <QList>

Check warning on line 8 in src/utils/treelandtoolbarplacement.h

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QList> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QPoint>

Check warning on line 9 in src/utils/treelandtoolbarplacement.h

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QPoint> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QRect>

Check warning on line 10 in src/utils/treelandtoolbarplacement.h

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QRect> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QSize>

Check warning on line 11 in src/utils/treelandtoolbarplacement.h

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QSize> not found. Please note: Cppcheck does not need standard library headers to get proper results.

/**
* Pure geometry helpers for Treeland screenshot toolbar placement.
*
* Coordinates are logical pixels in the same space as the Treeland capture
* region and the MainWindow overlay (typically virtual-desktop origin).
* Kept free of compositor / widget state so unit tests can exercise edge cases.
*/
namespace TreelandToolBarPlacement {

/** Default gap between capture rect and outside toolbar placement. */
inline constexpr int kDefaultYSpacing = 5;

/**
* Pick the screen that should own the toolbar for @p captureRegion.
* Prefers the screen containing the region center; falls back to the screen
* with the largest intersection area; finally @p fallback.
*/
QRect pickScreenGeometry(const QRect &captureRegion,
const QList<QRect> &screens,
const QRect &fallback);

/**
* Clamp a top-left toolbar position so the full toolbar rectangle stays inside
* @p screenGeom. If the toolbar is larger than the screen on an axis, pin that
* axis to the screen origin (best-effort visibility).
*/
QPoint clampToScreen(const QPoint &topLeft,
const QSize &toolBarSize,
const QRect &screenGeom);

/**
* Deterministic Treeland screenshot toolbar placement:
* - horizontal: right-align to the capture rect (legacy X11 behavior), then clamp;
* - vertical: prefer outside below with @p ySpacing when the full toolbar fits
* on the chosen screen; else outside above when it fits; else inside the
* capture (top + spacing) and clamp.
* Always returns a position where the full toolbar rect is inside @p screenGeom
* whenever toolBarSize fits on that screen.
*/
QPoint placeToolBar(const QRect &captureRegion,
const QSize &toolBarSize,
const QRect &screenGeom,
int ySpacing = kDefaultYSpacing);

} // namespace TreelandToolBarPlacement

#endif // TREELANDTOOLBARPLACEMENT_H
1 change: 1 addition & 0 deletions tests/ut_screen_shot_recorder/test_all_interfaces.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "utils/ut_configsettings.h"
//#include "utils/ut_desktopinfo.h"
#include "utils/ut_screengrabber.h"
#include "utils/ut_treelandtoolbarplacement.h"
#include "utils/ut_shortcut.h"
#include "utils/ut_tempfile.h"
#include "utils/ut_utils_other.h"
Expand Down
3 changes: 3 additions & 0 deletions tests/ut_screen_shot_recorder/ut_screen_shot_recorder.pro
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ HEADERS += test_all_interfaces.h \
#utils/ut_dbusutils.h \
#utils/ut_desktopinfo.h \
utils/ut_screengrabber.h \
utils/ut_treelandtoolbarplacement.h \
utils/ut_shortcut.h \
utils/ut_tempfile.h \
utils/ut_utils_other.h \
Expand All @@ -129,6 +130,7 @@ HEADERS += test_all_interfaces.h \
#../../src/utils/dbusutils.h \
#../../src/utils/desktopinfo.h \
../../src/utils/screengrabber.h \
../../src/utils/treelandtoolbarplacement.h \
../../src/utils/shortcut.h \
../../src/utils/tempfile.h \
../../src/utils/shapesutils.h \
Expand Down Expand Up @@ -336,6 +338,7 @@ SOURCES += main.cpp \
../../src/utils/borderprocessinterface.cpp \
#../../src/utils/desktopinfo.cpp \
../../src/utils/screengrabber.cpp \
../../src/utils/treelandtoolbarplacement.cpp \
../../src/utils/shortcut.cpp \
../../src/utils/tempfile.cpp \
../../src/utils/shapesutils.cpp \
Expand Down
Loading
Loading