Skip to content

Commit

Permalink
refactor: pull some complexity out of CaptureWidget constructor
Browse files Browse the repository at this point in the history
There is a fair bit going on in the constructor and in particular some parts
around screens is intermingled between different blocks of platform
specific #ifdefs, which makes it a bit hard to follow.

In this commit I'm pulling different parts of the logic out to their own
functions to make it so people can go about refactoring individual pieces of
logic more confidently. I've tried to to make too many changes beyond moving
code around (although it did all change indentation too, which makes a diff a
bit more difficult to follow.

From the new `initInitialScreenshot()` method I've pulled out the setting of
`topLeft` that I added previously, that's gone down into `positionWindow()`
where it belongs.

I've pulled `initWindowFlags()` out of window sizing and positioning code just
to avoid intermingling the #ifdefs.

In `positionWindow()` I've added in the setting of `topLeft` from the request
and left some TODO comments. Now that getting the screenshot and setting the
window position are separate it may make sense to do some sanity checking. I
would also like to figure out why the windows and macOS cases need to be
different and if we can change them to make editing a pin nicer there too.

For setting the button area I don't know what most of this code is doing. I
suspect some if it could be handled by the code in `ScreenGrabber()` which
looks at geometries already.

I moved the creation of the button widget and the call to get the relevant
areas into the existing `initButtons()` method, it just seemed appropriate.
  • Loading branch information
toofar committed Dec 28, 2023
1 parent 54b9175 commit 0ad59cc
Show file tree
Hide file tree
Showing 2 changed files with 128 additions and 90 deletions.
214 changes: 124 additions & 90 deletions src/widgets/capture/capturewidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,104 +104,17 @@ CaptureWidget::CaptureWidget(const CaptureRequest& req,
m_contrastUiColor = m_config.contrastUiColor();
setMouseTracking(true);
initContext(fullScreen, req);
// Top left of the whole set of screens
QPoint topLeft(0, 0);
if (fullScreen) {
// Grab Screenshot
bool ok = true;
QScreen* selectedScreen = req.initialCaptureScreen();
if (selectedScreen != nullptr) {
// Set the screenshot area, and gui window, to be over just one
// screen.
// TODO: refactor this widget to make it so passing
// fullscreen=false isn't required to do anything.
topLeft = selectedScreen->geometry().topLeft();
m_context.screenshot =
ScreenGrabber().grabScreen(selectedScreen, ok);
} else {
m_context.screenshot = ScreenGrabber().grabEntireDesktop(ok);
}
if (!ok) {
if (!initInitialScreenshot(req)) {
AbstractLogger::error() << tr("Unable to capture screen");
this->close();
}
m_context.origScreenshot = m_context.screenshot;

#if defined(Q_OS_WIN)
// Call cmake with -DFLAMESHOT_DEBUG_CAPTURE=ON to enable easier debugging
#if !defined(FLAMESHOT_DEBUG_CAPTURE)
setWindowFlags(Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint |
Qt::SubWindow // Hides the taskbar icon
);
#endif
initWindowFlags();

for (QScreen* const screen : QGuiApplication::screens()) {
QPoint topLeftScreen = screen->geometry().topLeft();

if (topLeftScreen.x() < topLeft.x()) {
topLeft.setX(topLeftScreen.x());
}
if (topLeftScreen.y() < topLeft.y()) {
topLeft.setY(topLeftScreen.y());
}
}
move(topLeft);
resize(pixmap().size());
#elif defined(Q_OS_MACOS)
// Emulate fullscreen mode
// setWindowFlags(Qt::WindowStaysOnTopHint |
// Qt::BypassWindowManagerHint |
// Qt::FramelessWindowHint |
// Qt::NoDropShadowWindowHint | Qt::ToolTip |
// Qt::Popup
// );
QScreen* currentScreen = QGuiAppCurrentScreen().currentScreen();
move(currentScreen->geometry().x(), currentScreen->geometry().y());
resize(currentScreen->size());
#else
// Call cmake with -DFLAMESHOT_DEBUG_CAPTURE=ON to enable easier debugging
#if !defined(FLAMESHOT_DEBUG_CAPTURE)
setWindowFlags(Qt::BypassWindowManagerHint | Qt::WindowStaysOnTopHint |
Qt::FramelessWindowHint | Qt::Tool);
move(topLeft);
resize(pixmap().size());
#endif
#endif
positionWindow(req);
}
QVector<QRect> areas;
if (m_context.fullscreen) {
QPoint topLeftOffset = QPoint(0, 0);
#if defined(Q_OS_WIN)
topLeftOffset = topLeft;
#endif

#if defined(Q_OS_MACOS)
// MacOS works just with one active display, so we need to append
// just one current display and keep multiple displays logic for
// other OS
QRect r;
QScreen* screen = QGuiAppCurrentScreen().currentScreen();
r = screen->geometry();
// all calculations are processed according to (0, 0) start
// point so we need to move current object to (0, 0)
r.moveTo(0, 0);
areas.append(r);
#else
for (QScreen* const screen : QGuiApplication::screens()) {
QRect r = screen->geometry();
r.moveTo(r.x() / screen->devicePixelRatio(),
r.y() / screen->devicePixelRatio());
r.moveTo(r.topLeft() - topLeftOffset);
areas.append(r);
}
#endif
} else {
areas.append(rect());
}

m_buttonHandler = new ButtonHandler(this);
m_buttonHandler->updateScreenRegions(areas);
m_buttonHandler->hide();

initButtons();
initSelection(); // button handler must be initialized before
Expand Down Expand Up @@ -294,8 +207,129 @@ CaptureWidget::~CaptureWidget()
}
}

/** @brief Get initial screenshot to serve as canvas for GUI window.
*
* Takes parameters from `req` and saves the pixmap from the screenshot to
* `m_context.screenshot` and `m_context.origScreenshot`.
*
* If `false` is returned we couldn't get a screenshot.
*/
bool CaptureWidget::initInitialScreenshot(const CaptureRequest& req)
{
bool ok = true;
QScreen* selectedScreen = req.initialCaptureScreen();
if (selectedScreen != nullptr) {
// Set the screenshot area to be over just one screen.
m_context.screenshot =
ScreenGrabber().grabScreen(selectedScreen, ok);
} else {
m_context.screenshot = ScreenGrabber().grabEntireDesktop(ok);
}
m_context.origScreenshot = m_context.screenshot;
return ok;
}

/** @brief Set Qt flags on this window to make it fullscreen, borderless etc.
*/
void CaptureWidget::initWindowFlags()
{
#if !defined(FLAMESHOT_DEBUG_CAPTURE)
#if defined(Q_OS_WIN)
setWindowFlags(Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint |
Qt::SubWindow // Hides the taskbar icon
);
#elif defined(Q_OS_MACOS)
// Emulate fullscreen mode
// setWindowFlags(Qt::WindowStaysOnTopHint |
// Qt::BypassWindowManagerHint |
// Qt::FramelessWindowHint |
// Qt::NoDropShadowWindowHint | Qt::ToolTip |
// Qt::Popup
// );
#else // Q_OS_LINUX || Q_OS_UNIX
setWindowFlags(Qt::BypassWindowManagerHint | Qt::WindowStaysOnTopHint |
Qt::FramelessWindowHint | Qt::Tool);
#endif
#endif // !FLAMESHOT_DEBUG_CAPTURE
}

/** @brief Set the starting size and position of this window.
*/
void CaptureWidget::positionWindow(const CaptureRequest& req)
{
// Top left of the whole set of screens
QPoint topLeft(0, 0);
QScreen* selectedScreen = req.initialCaptureScreen();
if (selectedScreen != nullptr) {
// Set the gui window to be over just one screen.
// TODO: verify initial selection is within this screen
// TODO: make the windows case not override this
topLeft = selectedScreen->geometry().topLeft();
}
#if defined(Q_OS_WIN)
for (QScreen* const screen : QGuiApplication::screens()) {
QPoint topLeftScreen = screen->geometry().topLeft();

if (topLeftScreen.x() < topLeft.x()) {
topLeft.setX(topLeftScreen.x());
}
if (topLeftScreen.y() < topLeft.y()) {
topLeft.setY(topLeftScreen.y());
}
}
move(topLeft);
resize(pixmap().size());
#elif defined(Q_OS_MACOS)
QScreen* currentScreen = QGuiAppCurrentScreen().currentScreen();
move(currentScreen->geometry().x(), currentScreen->geometry().y());
resize(currentScreen->size());
#else
move(topLeft);
resize(pixmap().size());
#endif
}

QVector<QRect> CaptureWidget::initButtonAreas()
{
QVector<QRect> areas;
if (m_context.fullscreen) {
QPoint topLeftOffset = QPoint(0, 0);
#if defined(Q_OS_WIN)
topLeftOffset = geometry().topLeft();
#endif

#if defined(Q_OS_MACOS)
// MacOS works just with one active display, so we need to append
// just one current display and keep multiple displays logic for
// other OS
QRect r;
QScreen* screen = QGuiAppCurrentScreen().currentScreen();
r = screen->geometry();
// all calculations are processed according to (0, 0) start
// point so we need to move current object to (0, 0)
r.moveTo(0, 0);
areas.append(r);
#else
for (QScreen* const screen : QGuiApplication::screens()) {
QRect r = screen->geometry();
r.moveTo(r.x() / screen->devicePixelRatio(),
r.y() / screen->devicePixelRatio());
r.moveTo(r.topLeft() - topLeftOffset);
areas.append(r);
}
#endif
} else {
areas.append(rect());
}
return areas;
}

void CaptureWidget::initButtons()
{
m_buttonHandler = new ButtonHandler(this);
m_buttonHandler->updateScreenRegions(initButtonAreas());
m_buttonHandler->hide();

auto allButtonTypes = CaptureToolButton::getIterableButtonTypes();
auto visibleButtonTypes = m_config.buttons();
if ((m_context.request.tasks() == CaptureRequest::NO_TASK) ||
Expand Down
4 changes: 4 additions & 0 deletions src/widgets/capture/capturewidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,10 @@ private slots:
void initShortcuts();
void initButtons();
void initHelpMessage();
bool initInitialScreenshot(const CaptureRequest& req);
void initWindowFlags();
void positionWindow(const CaptureRequest& req);
QVector<QRect> initButtonAreas();
void updateSizeIndicator();
void updateCursor();
void updateSelectionState();
Expand Down

0 comments on commit 0ad59cc

Please sign in to comment.