mirror of
https://github.com/microsoft/PowerToys
synced 2025-09-05 17:05:12 +00:00
239 lines
7.8 KiB
C++
239 lines
7.8 KiB
C++
#include "pch.h"
|
|
#include "WindowDrag.h"
|
|
|
|
#include <FancyZonesLib/FancyZonesData/AppZoneHistory.h>
|
|
#include <FancyZonesLib/FancyZonesWindowProcessing.h>
|
|
#include <FancyZonesLib/FancyZonesWindowProperties.h>
|
|
#include <FancyZonesLib/NotificationUtil.h>
|
|
#include <FancyZonesLib/Settings.h>
|
|
#include <FancyZonesLib/WindowUtils.h>
|
|
#include <FancyZonesLib/WorkArea.h>
|
|
|
|
#include <FancyZonesLib/trace.h>
|
|
|
|
#include <common/utils/elevation.h>
|
|
|
|
WindowDrag::WindowDrag(HWND window, const std::unordered_map<HMONITOR, std::unique_ptr<WorkArea>>& activeWorkAreas) :
|
|
m_window(window),
|
|
m_activeWorkAreas(activeWorkAreas),
|
|
m_currentWorkArea(nullptr),
|
|
m_snappingMode(false)
|
|
{
|
|
m_windowProperties.hasNoVisibleOwner = !FancyZonesWindowUtils::HasVisibleOwner(m_window);
|
|
m_windowProperties.isStandardWindow = FancyZonesWindowUtils::IsStandardWindow(m_window) &&
|
|
(!FancyZonesWindowUtils::IsPopupWindow(m_window) || FancyZonesSettings::settings().allowSnapPopupWindows);
|
|
}
|
|
|
|
WindowDrag::~WindowDrag()
|
|
{
|
|
ResetWindowTransparency();
|
|
}
|
|
|
|
std::unique_ptr<WindowDrag> WindowDrag::Create(HWND window, const std::unordered_map<HMONITOR, std::unique_ptr<WorkArea>>& activeWorkAreas)
|
|
{
|
|
if (!FancyZonesWindowProcessing::IsProcessable(window) ||
|
|
!FancyZonesWindowUtils::IsCandidateForZoning(window) ||
|
|
FancyZonesWindowUtils::IsCursorTypeIndicatingSizeEvent())
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
if (!is_process_elevated() && FancyZonesWindowUtils::IsProcessOfWindowElevated(window))
|
|
{
|
|
// Notifies user if unable to drag elevated window
|
|
FancyZonesNotifications::WarnIfElevationIsRequired();
|
|
return nullptr;
|
|
}
|
|
|
|
return std::unique_ptr<WindowDrag>(new WindowDrag(window, activeWorkAreas));
|
|
}
|
|
|
|
bool WindowDrag::MoveSizeStart(HMONITOR monitor, bool isSnapping)
|
|
{
|
|
auto iter = m_activeWorkAreas.find(monitor);
|
|
if (iter == end(m_activeWorkAreas))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
m_currentWorkArea = iter->second.get();
|
|
|
|
SwitchSnappingMode(isSnapping);
|
|
|
|
if (m_currentWorkArea)
|
|
{
|
|
m_currentWorkArea->UnsnapWindow(m_window);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WindowDrag::MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen, bool isSnapping, bool isSelectManyZonesState)
|
|
{
|
|
auto iter = m_activeWorkAreas.find(monitor);
|
|
if (isSnapping && iter != m_activeWorkAreas.end())
|
|
{
|
|
// The drag has moved to a different monitor.
|
|
// Change work area
|
|
if (iter->second.get() != m_currentWorkArea)
|
|
{
|
|
m_highlightedZones.Reset();
|
|
|
|
if (m_currentWorkArea)
|
|
{
|
|
if (!FancyZonesSettings::settings().showZonesOnAllMonitors)
|
|
{
|
|
m_currentWorkArea->HideZonesOverlay();
|
|
}
|
|
else
|
|
{
|
|
m_currentWorkArea->ShowZonesOverlay({}, m_window);
|
|
}
|
|
}
|
|
|
|
m_currentWorkArea = iter->second.get();
|
|
}
|
|
|
|
if (m_currentWorkArea)
|
|
{
|
|
POINT ptClient = ptScreen;
|
|
MapWindowPoints(nullptr, m_currentWorkArea->GetWorkAreaWindow(), &ptClient, 1);
|
|
const bool redraw = m_highlightedZones.Update(m_currentWorkArea->GetLayout().get(), ptClient, isSelectManyZonesState);
|
|
if (redraw)
|
|
{
|
|
m_currentWorkArea->ShowZonesOverlay(m_highlightedZones.Zones(), m_window);
|
|
}
|
|
}
|
|
}
|
|
|
|
SwitchSnappingMode(isSnapping);
|
|
}
|
|
|
|
void WindowDrag::MoveSizeEnd()
|
|
{
|
|
if (m_snappingMode)
|
|
{
|
|
const bool hasNoVisibleOwner = !FancyZonesWindowUtils::HasVisibleOwner(m_window);
|
|
const bool isStandardWindow = FancyZonesWindowUtils::IsStandardWindow(m_window);
|
|
|
|
if ((isStandardWindow == false && hasNoVisibleOwner == true &&
|
|
m_windowProperties.isStandardWindow == true && m_windowProperties.hasNoVisibleOwner == true) ||
|
|
FancyZonesWindowUtils::IsWindowMaximized(m_window))
|
|
{
|
|
// Abort the zoning, this is a Chromium based tab that is merged back with an existing window
|
|
// or if the window is maximized by Windows when the cursor hits the screen top border
|
|
}
|
|
else if (m_currentWorkArea)
|
|
{
|
|
m_currentWorkArea->MoveWindowIntoZoneByIndexSet(m_window, m_highlightedZones.Zones());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FancyZonesWindowUtils::ResetRoundCornersPreference(m_window);
|
|
if (FancyZonesSettings::settings().restoreSize)
|
|
{
|
|
if (FancyZonesWindowUtils::IsCursorTypeIndicatingSizeEvent())
|
|
{
|
|
::RemoveProp(m_window, ZonedWindowProperties::PropertyRestoreSizeID);
|
|
}
|
|
else if (!FancyZonesWindowUtils::IsWindowMaximized(m_window))
|
|
{
|
|
FancyZonesWindowUtils::RestoreWindowSize(m_window);
|
|
}
|
|
}
|
|
}
|
|
|
|
SwitchSnappingMode(false);
|
|
}
|
|
|
|
void WindowDrag::SwitchSnappingMode(bool isSnapping)
|
|
{
|
|
if (!m_snappingMode && isSnapping) // turn on
|
|
{
|
|
SetWindowTransparency();
|
|
|
|
if (FancyZonesSettings::settings().showZonesOnAllMonitors)
|
|
{
|
|
for (const auto& [_, workArea] : m_activeWorkAreas)
|
|
{
|
|
if (workArea && workArea.get() != m_currentWorkArea)
|
|
{
|
|
workArea->ShowZonesOverlay({}, m_window);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (m_currentWorkArea)
|
|
{
|
|
Trace::WorkArea::MoveOrResizeStarted(m_currentWorkArea->GetLayout().get(), m_currentWorkArea->GetLayoutWindows().get());
|
|
}
|
|
}
|
|
else if (m_snappingMode && !isSnapping) // turn off
|
|
{
|
|
ResetWindowTransparency();
|
|
m_highlightedZones.Reset();
|
|
|
|
// Hide all layouts (regardless of settings)
|
|
for (auto& [_, workArea] : m_activeWorkAreas)
|
|
{
|
|
if (workArea)
|
|
{
|
|
workArea->HideZonesOverlay();
|
|
}
|
|
}
|
|
|
|
if (m_currentWorkArea)
|
|
{
|
|
Trace::WorkArea::MoveOrResizeEnd(m_currentWorkArea->GetLayout().get(), m_currentWorkArea->GetLayoutWindows().get());
|
|
}
|
|
}
|
|
|
|
m_snappingMode = isSnapping;
|
|
}
|
|
|
|
void WindowDrag::SetWindowTransparency()
|
|
{
|
|
if (FancyZonesSettings::settings().makeDraggedWindowTransparent)
|
|
{
|
|
m_windowProperties.exstyle = GetWindowLong(m_window, GWL_EXSTYLE);
|
|
|
|
SetWindowLong(m_window, GWL_EXSTYLE, m_windowProperties.exstyle | WS_EX_LAYERED);
|
|
|
|
if (!GetLayeredWindowAttributes(m_window, &m_windowProperties.crKey, &m_windowProperties.alpha, &m_windowProperties.dwFlags))
|
|
{
|
|
Logger::error(L"Window transparency: GetLayeredWindowAttributes failed, {}", get_last_error_or_default(GetLastError()));
|
|
return;
|
|
}
|
|
|
|
if (!SetLayeredWindowAttributes(m_window, 0, (255 * 50) / 100, LWA_ALPHA))
|
|
{
|
|
Logger::error(L"Window transparency: SetLayeredWindowAttributes failed, {}", get_last_error_or_default(GetLastError()));
|
|
return;
|
|
}
|
|
|
|
m_windowProperties.transparencySet = true;
|
|
}
|
|
}
|
|
|
|
void WindowDrag::ResetWindowTransparency()
|
|
{
|
|
if (FancyZonesSettings::settings().makeDraggedWindowTransparent && m_windowProperties.transparencySet)
|
|
{
|
|
bool reset = true;
|
|
if (!SetLayeredWindowAttributes(m_window, m_windowProperties.crKey, m_windowProperties.alpha, m_windowProperties.dwFlags))
|
|
{
|
|
Logger::error(L"Window transparency: SetLayeredWindowAttributes failed, {}", get_last_error_or_default(GetLastError()));
|
|
reset = false;
|
|
}
|
|
|
|
if (SetWindowLong(m_window, GWL_EXSTYLE, m_windowProperties.exstyle) == 0)
|
|
{
|
|
Logger::error(L"Window transparency: SetWindowLong failed, {}", get_last_error_or_default(GetLastError()));
|
|
reset = false;
|
|
}
|
|
|
|
m_windowProperties.transparencySet = !reset;
|
|
}
|
|
}
|