diff --git a/Library/ContextMenu.cpp b/Library/ContextMenu.cpp index 52ee96a88..940ba8dc0 100644 --- a/Library/ContextMenu.cpp +++ b/Library/ContextMenu.cpp @@ -377,6 +377,14 @@ HMENU ContextMenu::CreateSkinMenu(Skin* skin, int index, HMENU menu) { CheckMenuItem(settingsMenu, IDM_SKIN_FAVORITE, MF_BYCOMMAND | MF_CHECKED); } + + // Disable options if skin is selected + if (skin->IsSelected()) + { + EnableMenuItem(settingsMenu, IDM_SKIN_DRAGGABLE, MF_GRAYED); + EnableMenuItem(settingsMenu, IDM_SKIN_KEEPONSCREEN, MF_GRAYED); + EnableMenuItem(settingsMenu, IDM_SKIN_CLICKTHROUGH, MF_GRAYED); + } } // Add the name of the Skin to the menu diff --git a/Library/DialogManage.cpp b/Library/DialogManage.cpp index 96798ea1e..84578e9d1 100644 --- a/Library/DialogManage.cpp +++ b/Library/DialogManage.cpp @@ -131,6 +131,14 @@ void DialogManage::Open(const WCHAR* tabName, const WCHAR* param1, const WCHAR* } } +void DialogManage::UpdateSelectedSkinOptions(Skin* skin) +{ + if (c_Dialog && c_Dialog->m_TabSkins.IsInitialized()) + { + c_Dialog->m_TabSkins.UpdateSelected(skin); + } +} + void DialogManage::UpdateSkins(Skin* skin, bool deleted) { if (c_Dialog && c_Dialog->m_TabSkins.IsInitialized()) @@ -584,6 +592,21 @@ void DialogManage::TabSkins::Initialize() m_HandleCommands = true; } +void DialogManage::TabSkins::UpdateSelected(Skin* skin) +{ + if (m_SkinWindow && m_SkinWindow == skin) + { + bool selected = skin->IsSelected(); + + HWND item = GetControl(Id_DraggableCheckBox); + EnableWindow(item, selected ? FALSE : TRUE); + item = GetControl(Id_KeepOnScreenCheckBox); + EnableWindow(item, selected ? FALSE : TRUE); + item = GetControl(Id_ClickThroughCheckBox); + EnableWindow(item, selected ? FALSE : TRUE); + } +} + /* ** Updates metadata and settings when changed. ** diff --git a/Library/DialogManage.h b/Library/DialogManage.h index cda967f56..11cedf869 100644 --- a/Library/DialogManage.h +++ b/Library/DialogManage.h @@ -27,6 +27,8 @@ class DialogManage : public Dialog static void Open(int tab = 0); static void OpenSkin(Skin* skin); + static void UpdateSelectedSkinOptions(Skin* skin); + static void UpdateSkins(Skin* skin, bool deleted = false); static void UpdateLayouts(); @@ -81,6 +83,7 @@ class DialogManage : public Dialog void Create(HWND owner); virtual void Initialize(); + void UpdateSelected(Skin* skin); void Update(Skin* skin, bool deleted); static void SelectTreeItem(HWND tree, HTREEITEM item, LPCWSTR name); diff --git a/Library/Group.cpp b/Library/Group.cpp index 8eb746014..96ee52f01 100644 --- a/Library/Group.cpp +++ b/Library/Group.cpp @@ -27,6 +27,29 @@ void Group::InitializeGroup(const std::wstring& groups) } } +bool Group::AddToGroup(const std::wstring& group) +{ + if (!group.empty() && !BelongsToGroup(group)) + { + if (!m_OldGroups.empty()) + { + m_OldGroups.append(1, L'|'); + } + + m_OldGroups.append(group); + + std::vector vGroups = ConfigParser::Tokenize(group, L"|"); + for (auto iter = vGroups.begin(); iter != vGroups.end(); ++iter) + { + m_Groups.insert(m_Groups.end(), CreateGroup(*iter)); + } + + return true; + } + + return false; +} + bool Group::BelongsToGroup(const std::wstring& group) const { return (m_Groups.find(VerifyGroup(group)) != m_Groups.end()); diff --git a/Library/Group.h b/Library/Group.h index 8628e1037..ee366df5f 100644 --- a/Library/Group.h +++ b/Library/Group.h @@ -14,17 +14,18 @@ class __declspec(novtable) Group { public: + Group() {} virtual ~Group() {} Group(const Group& other) = delete; Group& operator=(Group other) = delete; - bool BelongsToGroup(const std::wstring& group) const; + void InitializeGroup(const std::wstring& groups); -protected: - Group() {} + const std::unordered_set& GetGroups() const { return m_Groups; } - void InitializeGroup(const std::wstring& groups); + bool AddToGroup(const std::wstring& group); + bool BelongsToGroup(const std::wstring& group) const; private: std::wstring& CreateGroup(std::wstring& str) const; diff --git a/Library/Meter.cpp b/Library/Meter.cpp index 877ea832b..bf0d13800 100644 --- a/Library/Meter.cpp +++ b/Library/Meter.cpp @@ -36,6 +36,7 @@ Meter::Meter(Skin* skin, const WCHAR* name) : Section(skin, name), m_ToolTipWidth(), m_ToolTipType(false), m_ToolTipHidden(skin->GetMeterToolTipHidden()), + m_ToolTipDisabled(false), m_ToolTipHandle(), m_Mouse(skin, this), m_HasMouseAction(false), @@ -646,7 +647,7 @@ void Meter::UpdateToolTip() SendMessage(hwndTT, TTM_SETTOOLINFO, 0, (LPARAM)&ti); SendMessage(hwndTT, TTM_SETMAXTIPWIDTH, 0, m_ToolTipWidth); - if (m_ToolTipHidden) + if (m_ToolTipHidden || m_ToolTipDisabled) { SendMessage(hwndTT, TTM_ACTIVATE, FALSE, 0); } diff --git a/Library/Meter.h b/Library/Meter.h index 2b6fb361c..e60f0873d 100644 --- a/Library/Meter.h +++ b/Library/Meter.h @@ -60,6 +60,8 @@ class __declspec(novtable) Meter : public Section void CreateToolTip(Skin* skin); void UpdateToolTip(); + void DisableToolTip() { m_ToolTipDisabled = true; UpdateToolTip(); } + void ResetToolTip() { m_ToolTipDisabled = false; UpdateToolTip(); } void Hide(); void Show(); @@ -128,7 +130,7 @@ class __declspec(novtable) Meter : public Section unsigned int m_ToolTipWidth; bool m_ToolTipType; bool m_ToolTipHidden; - + bool m_ToolTipDisabled; // Selected skins disable all tooltips HWND m_ToolTipHandle; Mouse m_Mouse; diff --git a/Library/Rainmeter.cpp b/Library/Rainmeter.cpp index 847aefcb2..826251c64 100644 --- a/Library/Rainmeter.cpp +++ b/Library/Rainmeter.cpp @@ -1331,6 +1331,8 @@ void Rainmeter::ReadGeneralSettings(const std::wstring& iniFile) m_DisableDragging = parser.ReadBool(L"Rainmeter", L"DisableDragging", false); m_DisableRDP = parser.ReadBool(L"Rainmeter", L"DisableRDP", false); + m_DefaultSelectedColor = parser.ReadColor(L"Rainmeter", L"SelectedColor", Color::MakeARGB(90, 255, 0, 0)); + m_SkinEditor = parser.ReadString(L"Rainmeter", L"ConfigEditor", L""); if (m_SkinEditor.empty()) { @@ -1535,6 +1537,7 @@ bool Rainmeter::LoadLayout(const std::wstring& name) PreserveSetting(backup, L"DisableVersionCheck"); PreserveSetting(backup, L"Language"); PreserveSetting(backup, L"NormalStayDesktop"); + PreserveSetting(backup, L"SelectedColor"); PreserveSetting(backup, L"TrayExecuteM", false); PreserveSetting(backup, L"TrayExecuteR", false); PreserveSetting(backup, L"TrayExecuteDM", false); diff --git a/Library/Rainmeter.h b/Library/Rainmeter.h index a5b90a244..693638b7d 100644 --- a/Library/Rainmeter.h +++ b/Library/Rainmeter.h @@ -171,6 +171,8 @@ class Rainmeter bool IsSkinAFavorite(const std::wstring& folder, const std::wstring& filename); void UpdateFavorites(const std::wstring& folder, const std::wstring& file, bool favorite); + Gdiplus::Color& GetDefaultSelectionColor() { return m_DefaultSelectedColor; } + friend class CommandHandler; friend class ContextMenu; friend class DialogManage; @@ -245,6 +247,8 @@ class Rainmeter std::wstring m_SkinEditor; + Gdiplus::Color m_DefaultSelectedColor; + CommandHandler m_CommandHandler; ContextMenu m_ContextMenu; SkinRegistry m_SkinRegistry; diff --git a/Library/Skin.cpp b/Library/Skin.cpp index 09b12657a..8393b4324 100644 --- a/Library/Skin.cpp +++ b/Library/Skin.cpp @@ -53,6 +53,7 @@ enum INTERVAL }; int Skin::c_InstanceCount = 0; +bool Skin::c_IsInSelectionMode = false; Skin::Skin(const std::wstring& folderPath, const std::wstring& file) : m_FolderPath(folderPath), m_FileName(file), m_Canvas(), @@ -111,6 +112,12 @@ Skin::Skin(const std::wstring& folderPath, const std::wstring& file) : m_FolderP m_BackgroundMode(BGMODE_IMAGE), m_SolidAngle(), m_SolidBevel(BEVELTYPE_NONE), + m_OldWindowDraggable(false), + m_OldKeepOnScreen(false), + m_OldClickThrough(false), + m_Selected(false), + m_SelectedColor(GetRainmeter().GetDefaultSelectionColor()), + m_DragGroup(), m_Blur(false), m_BlurMode(BLURMODE_NONE), m_BlurRegion(), @@ -305,7 +312,7 @@ void Skin::UnregisterMouseInput() rid.usUsagePage = 0x01; rid.usUsage = 0x02; // HID mouse rid.dwFlags = RIDEV_REMOVE; - rid.hwndTarget = m_Window; + rid.hwndTarget = NULL; RegisterRawInputDevices(&rid, 1, sizeof(rid)); m_MouseInputRegistered = false; } @@ -545,6 +552,89 @@ void Skin::MoveWindow(int x, int y) SavePositionIfAppropriate(); } +void Skin::MoveSelectedWindow(int dx, int dy) +{ + SetWindowPos( + m_Window, + nullptr, + m_ScreenX + dx, + m_ScreenY + dy, + 0, + 0, + SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE); + + SavePositionIfAppropriate(); +} + +void Skin::SelectSkinsGroup(std::unordered_set groups) +{ + for (const auto& group : groups) + { + if (m_DragGroup.BelongsToGroup(group)) + { + Select(); + return; + } + } +} + +void Skin::Select() +{ + m_Selected = true; + + // When a skin is selected, it is implied that the purpose is to + // move a skin(s) around the desktop, so temporarily set the following + // settings to allow for easy movement of the selected skin(s). + m_OldWindowDraggable = m_WindowDraggable; + SetWindowDraggable(true); + m_OldKeepOnScreen = m_KeepOnScreen; + SetKeepOnScreen(false); + m_OldClickThrough = m_ClickThrough; + SetClickThrough(false); + DialogManage::UpdateSelectedSkinOptions(this); + + // Disable each meter's tooltip + for (const auto& meter : m_Meters) meter->DisableToolTip(); + + Redraw(); +} + +void Skin::Deselect() +{ + m_Selected = false; + + // Reset the following options to their original state + SetWindowDraggable(m_OldWindowDraggable); + SetKeepOnScreen(m_OldKeepOnScreen); + SetClickThrough(m_OldClickThrough); + DialogManage::UpdateSelectedSkinOptions(this); + + // Reset each meter's tooltip + for (const auto& meter : m_Meters) meter->ResetToolTip(); + + Redraw(); +} + +void Skin::DeselectSkinsIfAppropriate(HWND hwnd) +{ + // Do not deselect any skins if CTRL+ALT is pressed + if (IsCtrlKeyDown() && IsAltKeyDown()) return; + + // If the window that gets focus is a Rainmeter skin that is + // selected, then do not de-select any skins + const auto skin = GetRainmeter().GetSkin(hwnd); + if (skin && skin->IsSelected()) return; + + for (const auto& skins : GetRainmeter().GetAllSkins()) + { + Skin* skin = skins.second; + if (skin->IsSelected()) + { + skin->Deselect(); + } + } +} + void Skin::ChangeZPos(ZPOSITION zPos, bool all) { HWND winPos = HWND_NOTOPMOST; @@ -1854,6 +1944,9 @@ void Skin::ReadOptions() m_SkinGroup = parser.ReadString(section, L"Group", L""); + const std::wstring dragGroup = parser.ReadString(section, L"DragGroup", L""); + m_DragGroup.InitializeGroup(dragGroup); + if (writeFlags != 0) { WriteOptions(writeFlags); @@ -2017,6 +2110,9 @@ bool Skin::ReadSkin() } InitializeGroup(m_SkinGroup); + const std::wstring dragGroup = m_Parser.ReadString(L"Rainmeter", L"DragGroup", L""); + m_DragGroup.AddToGroup(dragGroup); + static const RECT defMargins = {0}; m_BackgroundMargins = m_Parser.ReadRECT(L"Rainmeter", L"BackgroundMargins", defMargins); m_DragMargins = m_Parser.ReadRECT(L"Rainmeter", L"DragMargins", defMargins); @@ -2043,6 +2139,9 @@ bool Skin::ReadSkin() } } + auto& color = GetRainmeter().GetDefaultSelectionColor(); + m_SelectedColor = m_Parser.ReadColor(L"Rainmeter", L"SelectedColor", color.GetValue()); + m_Mouse.ReadOptions(m_Parser, L"Rainmeter"); m_OnRefreshAction = m_Parser.ReadString(L"Rainmeter", L"OnRefreshAction", L"", false); @@ -2574,6 +2673,13 @@ void Skin::Redraw() (*j)->Draw(m_Canvas); } } + + if (m_Selected) + { + Gdiplus::Rect rect(0, 0, m_WindowW, m_WindowH); + Gdiplus::SolidBrush brush(m_SelectedColor); + m_Canvas.FillRectangle(rect, brush); + } } UpdateWindow(m_TransparencyValue, true); @@ -3237,6 +3343,9 @@ LRESULT Skin::OnMouseMove(UINT uMsg, WPARAM wParam, LPARAM lParam) } } + // If the skin is selected, do not process any mouse 'move' actions + if (m_Selected) return 0; + if (!m_ClickThrough || keyDown) { POINT pos; @@ -3263,6 +3372,9 @@ LRESULT Skin::OnMouseMove(UINT uMsg, WPARAM wParam, LPARAM lParam) LRESULT Skin::OnMouseLeave(UINT uMsg, WPARAM wParam, LPARAM lParam) { + // If the skin is selected, do not process any mouse 'leave' actions + if (m_Selected) return 0; + POINT pos = System::GetCursorPosition(); HWND hWnd = WindowFromPoint(pos); if (!hWnd || (hWnd != m_Window && GetParent(hWnd) != m_Window)) // ignore tooltips @@ -3281,6 +3393,9 @@ LRESULT Skin::OnMouseLeave(UINT uMsg, WPARAM wParam, LPARAM lParam) LRESULT Skin::OnMouseScrollMove(UINT uMsg, WPARAM wParam, LPARAM lParam) { + // If the skin is selected, do not process mouse 'scroll' actions + if (m_Selected) return 0; + if (uMsg == WM_MOUSEWHEEL) // If sent through WM_INPUT, uMsg is WM_INPUT. { // Fix for Notepad++, which sends WM_MOUSEWHEEL to unfocused windows. @@ -3307,6 +3422,9 @@ LRESULT Skin::OnMouseScrollMove(UINT uMsg, WPARAM wParam, LPARAM lParam) LRESULT Skin::OnMouseHScrollMove(UINT uMsg, WPARAM wParam, LPARAM lParam) { + // If the skin is selected, do not process mouse 'horizontal scroll' actions + if (m_Selected) return 0; + POINT pos; pos.x = GET_X_LPARAM(lParam); pos.y = GET_Y_LPARAM(lParam); @@ -3367,7 +3485,10 @@ LRESULT Skin::OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam) break; case IDM_SKIN_KEEPONSCREEN: - SetKeepOnScreen(!m_KeepOnScreen); + if (!m_Selected) + { + SetKeepOnScreen(!m_KeepOnScreen); + } break; case IDM_SKIN_FAVORITE: @@ -3375,11 +3496,17 @@ LRESULT Skin::OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam) break; case IDM_SKIN_CLICKTHROUGH: - SetClickThrough(!m_ClickThrough); + if (!m_Selected) + { + SetClickThrough(!m_ClickThrough); + } break; case IDM_SKIN_DRAGGABLE: - SetWindowDraggable(!m_WindowDraggable); + if (!m_Selected) + { + SetWindowDraggable(!m_WindowDraggable); + } break; case IDM_SKIN_HIDEONMOUSE: @@ -3809,7 +3936,8 @@ LRESULT Skin::OnWindowPosChanging(UINT uMsg, WPARAM wParam, LPARAM lParam) // Snap to other windows for (auto iter = GetRainmeter().GetAllSkins().cbegin(); iter != GetRainmeter().GetAllSkins().cend(); ++iter) { - if ((*iter).second != this) + // Do not snap to |this| and to other selected skins + if ((*iter).second != this && !(*iter).second->IsSelected()) { SnapToWindow((*iter).second, wp); } @@ -3951,6 +4079,10 @@ LRESULT Skin::OnSettingChange(UINT uMsg, WPARAM wParam, LPARAM lParam) LRESULT Skin::OnLeftButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam) { + // If the skin is selected, do not process any 'left down' mouse actions, + // but run the DefWindowProc so that dragging works. + if (m_Selected) return DefWindowProc(m_Window, uMsg, wParam, lParam); + POINT pos; pos.x = GET_X_LPARAM(lParam); pos.y = GET_Y_LPARAM(lParam); @@ -3979,6 +4111,39 @@ LRESULT Skin::OnLeftButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam) LRESULT Skin::OnLeftButtonUp(UINT uMsg, WPARAM wParam, LPARAM lParam) { + // Select/Deselect the skin if CTRL+ALT is pressed when the + // left mouse button is depressed. (Draws an overlay over the skin.) + if (IsCtrlKeyDown() && IsAltKeyDown()) + { + if (!m_Selected) + { + Select(); // Select |this| skin + + // Select any skins that belong to any group |this| belongs to + const auto& groups = m_DragGroup.GetGroups(); + if (!groups.empty()) // Select all skins in group + { + for (const auto& skins : GetRainmeter().GetAllSkins()) + { + Skin* skin = skins.second; + if (skin != this) // Do not select |this| skin twice + { + skin->SelectSkinsGroup(groups); + } + } + } + } + else + { + Deselect(); + } + + return 0; + } + + // If the skin is selected, do not process 'left up' mouse actions. + if (m_Selected) return 0; // Make sure selection/deselection code is above this! + POINT pos; pos.x = GET_X_LPARAM(lParam); pos.y = GET_Y_LPARAM(lParam); @@ -3999,6 +4164,9 @@ LRESULT Skin::OnLeftButtonUp(UINT uMsg, WPARAM wParam, LPARAM lParam) LRESULT Skin::OnLeftButtonDoubleClick(UINT uMsg, WPARAM wParam, LPARAM lParam) { + // If the skin is selected, do not process 'left double click' mouse actions + if (m_Selected) return 0; + POINT pos; pos.x = GET_X_LPARAM(lParam); pos.y = GET_Y_LPARAM(lParam); @@ -4022,6 +4190,9 @@ LRESULT Skin::OnLeftButtonDoubleClick(UINT uMsg, WPARAM wParam, LPARAM lParam) LRESULT Skin::OnRightButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam) { + // If the skin is selected, do not process 'right down' mouse actions + if (m_Selected) return 0; + POINT pos; pos.x = GET_X_LPARAM(lParam); pos.y = GET_Y_LPARAM(lParam); @@ -4042,6 +4213,10 @@ LRESULT Skin::OnRightButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam) LRESULT Skin::OnRightButtonUp(UINT uMsg, WPARAM wParam, LPARAM lParam) { + // If the skin is selected, do not process 'right up' mouse actions, + // but run the DefWindowProc so the context menu works + if (m_Selected) return DefWindowProc(m_Window, uMsg, wParam, lParam); + POINT pos; pos.x = GET_X_LPARAM(lParam); pos.y = GET_Y_LPARAM(lParam); @@ -4061,6 +4236,9 @@ LRESULT Skin::OnRightButtonUp(UINT uMsg, WPARAM wParam, LPARAM lParam) LRESULT Skin::OnRightButtonDoubleClick(UINT uMsg, WPARAM wParam, LPARAM lParam) { + // If the skin is selected, do not process 'right double click' mouse actions + if (m_Selected) return 0; + POINT pos; pos.x = GET_X_LPARAM(lParam); pos.y = GET_Y_LPARAM(lParam); @@ -4084,6 +4262,9 @@ LRESULT Skin::OnRightButtonDoubleClick(UINT uMsg, WPARAM wParam, LPARAM lParam) LRESULT Skin::OnMiddleButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam) { + // If the skin is selected, do not process 'middle down' mouse actions + if (m_Selected) return 0; + POINT pos; pos.x = GET_X_LPARAM(lParam); pos.y = GET_Y_LPARAM(lParam); @@ -4104,6 +4285,9 @@ LRESULT Skin::OnMiddleButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam) LRESULT Skin::OnMiddleButtonUp(UINT uMsg, WPARAM wParam, LPARAM lParam) { + // If the skin is selected, do not process 'middle up' mouse actions + if (m_Selected) return 0; + POINT pos; pos.x = GET_X_LPARAM(lParam); pos.y = GET_Y_LPARAM(lParam); @@ -4124,6 +4308,9 @@ LRESULT Skin::OnMiddleButtonUp(UINT uMsg, WPARAM wParam, LPARAM lParam) LRESULT Skin::OnMiddleButtonDoubleClick(UINT uMsg, WPARAM wParam, LPARAM lParam) { + // If the skin is selected, do not process 'middle double click' mouse actions + if (m_Selected) return 0; + POINT pos; pos.x = GET_X_LPARAM(lParam); pos.y = GET_Y_LPARAM(lParam); @@ -4147,6 +4334,9 @@ LRESULT Skin::OnMiddleButtonDoubleClick(UINT uMsg, WPARAM wParam, LPARAM lParam) LRESULT Skin::OnXButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam) { + // If the skin is selected, do not process 'x button down' mouse actions + if (m_Selected) return 0; + POINT pos; pos.x = GET_X_LPARAM(lParam); pos.y = GET_Y_LPARAM(lParam); @@ -4174,6 +4364,9 @@ LRESULT Skin::OnXButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam) LRESULT Skin::OnXButtonUp(UINT uMsg, WPARAM wParam, LPARAM lParam) { + // If the skin is selected, do not process 'x button up' mouse actions + if (m_Selected) return 0; + POINT pos; pos.x = GET_X_LPARAM(lParam); pos.y = GET_Y_LPARAM(lParam); @@ -4201,6 +4394,9 @@ LRESULT Skin::OnXButtonUp(UINT uMsg, WPARAM wParam, LPARAM lParam) LRESULT Skin::OnXButtonDoubleClick(UINT uMsg, WPARAM wParam, LPARAM lParam) { + // If the skin is selected, do not process 'x button double click' mouse actions + if (m_Selected) return 0; + POINT pos; pos.x = GET_X_LPARAM(lParam); pos.y = GET_Y_LPARAM(lParam); @@ -4244,6 +4440,7 @@ LRESULT Skin::OnSetWindowFocus(UINT uMsg, WPARAM wParam, LPARAM lParam) { GetRainmeter().ExecuteCommand(m_OnUnfocusAction.c_str(), this); } + DeselectSkinsIfAppropriate((HWND)wParam); break; } @@ -4520,6 +4717,8 @@ LRESULT Skin::OnMove(UINT uMsg, WPARAM wParam, LPARAM lParam) // and in parent-client coordinates for child windows. // Store the new window position + int oldX = m_ScreenX; + int oldY = m_ScreenY; m_ScreenX = GET_X_LPARAM(lParam); m_ScreenY = GET_Y_LPARAM(lParam); @@ -4530,6 +4729,25 @@ LRESULT Skin::OnMove(UINT uMsg, WPARAM wParam, LPARAM lParam) ScreenToWindow(); } + if (!c_IsInSelectionMode && m_Selected) + { + const int newX = m_ScreenX - oldX; + const int newY = m_ScreenY - oldY; + + c_IsInSelectionMode = true; + + for (const auto& skins : GetRainmeter().GetAllSkins()) + { + Skin* skin = skins.second; + if (skin->IsSelected() && skin != this) + { + skin->MoveSelectedWindow(newX, newY); + } + } + + c_IsInSelectionMode = false; + } + return 0; } @@ -4556,6 +4774,41 @@ LRESULT Skin::OnPowerBroadcast(UINT uMsg, WPARAM wParam, LPARAM lParam) return 0; } +LRESULT Skin::OnKeyDown(UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + if (m_Selected) + { + int newX = 0; + int newY = 0; + int delta = IsCtrlKeyDown() ? SNAPDISTANCE : 1; + + switch (wParam) + { + case VK_LEFT: newX -= delta; break; + case VK_RIGHT: newX += delta; break; + case VK_UP: newY -= delta; break; + case VK_DOWN: newY += delta; break; + default: + return 0; + } + + c_IsInSelectionMode = true; + + for (const auto& skins : GetRainmeter().GetAllSkins()) + { + Skin* skin = skins.second; + if (skin->IsSelected()) + { + skin->MoveSelectedWindow(newX, newY); + } + } + + c_IsInSelectionMode = false; + } + + return 0; +} + /* ** The main window procedure for the meter window. ** @@ -4618,6 +4871,7 @@ LRESULT CALLBACK Skin::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPara MESSAGE(OnSetWindowFocus, WM_KILLFOCUS) MESSAGE(OnTimeChange, WM_TIMECHANGE) MESSAGE(OnPowerBroadcast, WM_POWERBROADCAST) + MESSAGE(OnKeyDown, WM_KEYDOWN) END_MESSAGEPROC } diff --git a/Library/Skin.h b/Library/Skin.h index 577a4af59..615c7c3e2 100644 --- a/Library/Skin.h +++ b/Library/Skin.h @@ -130,6 +130,12 @@ class Skin : public Group void SetHasMouseScrollAction() { m_HasMouseScrollAction = true; } void MoveWindow(int x, int y); + void MoveSelectedWindow(int dx, int dy); + bool IsSelected() { return m_Selected; } + void SelectSkinsGroup(std::unordered_set groups); + void Select(); + void Deselect(); + void ChangeZPos(ZPOSITION zPos, bool all = false); void ChangeSingleZPos(ZPOSITION zPos, bool all = false); void FadeWindow(int from, int to); @@ -245,6 +251,7 @@ class Skin : public Group LRESULT OnSetWindowFocus(UINT uMsg, WPARAM wParam, LPARAM lParam); LRESULT OnTimeChange(UINT uMsg, WPARAM wParam, LPARAM lParam); LRESULT OnPowerBroadcast(UINT uMsg, WPARAM wParam, LPARAM lParam); + LRESULT OnKeyDown(UINT uMsg, WPARAM wParam, LPARAM lParam); private: enum STATE @@ -312,6 +319,7 @@ class Skin : public Group void SetWindowPositionVariables(int x, int y); void SetWindowSizeVariables(int w, int h); void SetFavorite(bool favorite); + void DeselectSkinsIfAppropriate(HWND hwnd); void ShowBlur(); void HideBlur(); @@ -395,6 +403,15 @@ class Skin : public Group Gdiplus::REAL m_SolidAngle; BEVELTYPE m_SolidBevel; + bool m_OldWindowDraggable; + bool m_OldKeepOnScreen; + bool m_OldClickThrough; + + bool m_Selected; + Gdiplus::Color m_SelectedColor; + + Group m_DragGroup; + bool m_Blur; BLURMODE m_BlurMode; HRGN m_BlurRegion; @@ -429,6 +446,7 @@ class Skin : public Group bool m_Favorite; static int c_InstanceCount; + static bool c_IsInSelectionMode; }; #endif