Skip to content

Commit 9ac18b2

Browse files
Allow excluding windows from being closed via Esc
Add a "pin" function to in-game windows. The function replaces the shade (aka. minimize) button and the window can instead be shaded by double-clicking the title bar. Pinned windows are excluded from being closed by the window manager when hitting the Escape key.
1 parent 39768b8 commit 9ac18b2

3 files changed

Lines changed: 62 additions & 16 deletions

File tree

libs/s25main/WindowManager.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,11 +132,14 @@ void WindowManager::RelayKeyboardMessage(KeyboardMsgHandler msg, const KeyEvent&
132132
return; // No windows -> nothing to do
133133

134134
// ESC or ALT+W closes the active window
135-
if(ke.kt == KeyType::Escape || (ke.c == 'w' && ke.alt))
135+
const auto escape = (ke.kt == KeyType::Escape);
136+
if(escape || (ke.c == 'w' && ke.alt))
136137
{
137138
// Find one which isn't yet marked for closing so multiple ESC in between draw calls can close multiple windows
138-
const auto itActiveWnd =
139-
std::find_if(windows.rbegin(), windows.rend(), [](const auto& wnd) { return !wnd->ShouldBeClosed(); });
139+
// ESC won't close pinned windows
140+
const auto itActiveWnd = std::find_if(windows.rbegin(), windows.rend(), [escape](const auto& wnd) {
141+
return !wnd->ShouldBeClosed() && !(escape && wnd->IsPinned());
142+
});
140143
if(itActiveWnd != windows.rend() && (*itActiveWnd)->getCloseBehavior() != CloseBehavior::Custom)
141144
(*itActiveWnd)->Close();
142145
} else if(!CALL_MEMBER_FN(*windows.back(), msg)(ke)) // send to active window

libs/s25main/ingameWindows/IngameWindow.cpp

Lines changed: 49 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222

2323
namespace {
2424
constexpr Extent ButtonSize(16, 16);
25-
}
25+
constexpr unsigned TitleMargin = 32;
26+
} // namespace
2627

2728
const DrawPoint IngameWindow::posLastOrCenter(std::numeric_limits<DrawPoint::ElementType>::max(),
2829
std::numeric_limits<DrawPoint::ElementType>::max());
@@ -35,7 +36,8 @@ const Extent IngameWindow::borderSize(1, 1);
3536
IngameWindow::IngameWindow(unsigned id, const DrawPoint& pos, const Extent& size, std::string title,
3637
glArchivItem_Bitmap* background, bool modal, CloseBehavior closeBehavior, Window* parent)
3738
: Window(parent, id, pos, size), title_(std::move(title)), background(background), lastMousePos(0, 0),
38-
isModal_(modal), closeme(false), isMinimized_(false), isMoving(false), closeBehavior_(closeBehavior)
39+
isModal_(modal), closeme(false), isPinned_(false), isMinimized_(false), isMoving(false),
40+
closeBehavior_(closeBehavior)
3941
{
4042
std::fill(buttonStates_.begin(), buttonStates_.end(), ButtonState::Up);
4143
contentOffset.x = LOADER.GetImageN("resource", 38)->getWidth(); // left border
@@ -180,17 +182,32 @@ void IngameWindow::MouseLeftUp(const MouseCoords& mc)
180182
buttonStates_[btn] = ButtonState::Up;
181183

182184
if((btn == IwButton::Close && closeBehavior_ == CloseBehavior::Custom) // no close button
183-
|| (btn == IwButton::Minimize && isModal_)) // modal windows cannot be minimized
185+
|| (isModal_ // modal windows cannot be pinned or minimized
186+
&& (btn == IwButton::Title || btn == IwButton::PinOrMinimize)))
184187
continue;
185188

186189
if(IsPointInRect(mc.GetPos(), GetButtonBounds(btn)))
187190
{
188191
switch(btn)
189192
{
190193
case IwButton::Close: Close(); break;
191-
case IwButton::Minimize:
192-
SetMinimized(!IsMinimized());
193-
LOADER.GetSoundN("sound", 113)->Play(255, false);
194+
case IwButton::Title:
195+
if(SETTINGS.interface.enableWindowPinning && mc.dbl_click)
196+
{
197+
SetMinimized(!IsMinimized());
198+
LOADER.GetSoundN("sound", 113)->Play(255, false);
199+
}
200+
break;
201+
case IwButton::PinOrMinimize:
202+
if(SETTINGS.interface.enableWindowPinning)
203+
{
204+
SetPinned(!IsPinned());
205+
LOADER.GetSoundN("sound", 111)->Play(255, false);
206+
} else
207+
{
208+
SetMinimized(!IsMinimized());
209+
LOADER.GetSoundN("sound", 113)->Play(255, false);
210+
}
194211
break;
195212
}
196213
}
@@ -263,11 +280,27 @@ void IngameWindow::Draw_()
263280
using ButtonStateResIds = helpers::EnumArray<unsigned, ButtonState>;
264281
constexpr ButtonStateResIds closeResIds = {47, 55, 51};
265282
constexpr ButtonStateResIds minimizeResIds = {48, 56, 52};
283+
constexpr ButtonStateResIds pinBaseResIds = {47, 47, 51};
284+
constexpr auto overlayIdsStart = 15u;
285+
constexpr ButtonStateResIds pinOverlayResIdsRel = {0, 1, 2};
286+
constexpr ButtonStateResIds unpinOverlayResIdsRel = {3, 4, 5};
266287
if(closeBehavior_ != CloseBehavior::Custom)
267-
LOADER.GetImageN("resource", closeResIds[buttonStates_[IwButton::Close]])->DrawFull(GetPos());
288+
LOADER.GetImageN("resource", closeResIds[buttonStates_[IwButton::Close]])
289+
->DrawFull(GetButtonBounds(IwButton::Close));
268290
if(!IsModal())
269-
LOADER.GetImageN("resource", minimizeResIds[buttonStates_[IwButton::Minimize]])
270-
->DrawFull(GetButtonBounds(IwButton::Minimize));
291+
{
292+
const auto buttonState = buttonStates_[IwButton::PinOrMinimize];
293+
const auto bounds = GetButtonBounds(IwButton::PinOrMinimize);
294+
if(SETTINGS.interface.enableWindowPinning)
295+
{
296+
LOADER.GetImageN("resource", pinBaseResIds[buttonState])->DrawFull(bounds);
297+
if(isPinned_)
298+
LOADER.GetImageN("io_new", unpinOverlayResIdsRel[buttonState] + overlayIdsStart)->DrawFull(bounds);
299+
else
300+
LOADER.GetImageN("io_new", pinOverlayResIdsRel[buttonState] + overlayIdsStart)->DrawFull(bounds);
301+
} else
302+
LOADER.GetImageN("resource", minimizeResIds[buttonState])->DrawFull(bounds);
303+
}
271304

272305
// The title bar
273306
unsigned titleIndex;
@@ -379,10 +412,15 @@ void IngameWindow::SaveOpenStatus(bool isOpen) const
379412
Rect IngameWindow::GetButtonBounds(IwButton btn) const
380413
{
381414
auto pos = GetPos();
415+
auto size = ButtonSize;
382416
switch(btn)
383417
{
384418
case IwButton::Close: break;
385-
case IwButton::Minimize: pos.x += GetSize().x - ButtonSize.x; break;
419+
case IwButton::Title:
420+
pos.x += TitleMargin;
421+
size.x = GetSize().x - TitleMargin * 2;
422+
break;
423+
case IwButton::PinOrMinimize: pos.x += GetSize().x - ButtonSize.x; break;
386424
}
387-
return Rect(pos, ButtonSize);
425+
return Rect(pos, size);
388426
}

libs/s25main/ingameWindows/IngameWindow.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,12 @@ enum CloseBehavior
2929
enum class IwButton
3030
{
3131
Close,
32-
Minimize
32+
Title, /// Pseudo-button to respond to double-clicks on the title bar
33+
PinOrMinimize
3334
};
3435
constexpr auto maxEnumValue(IwButton)
3536
{
36-
return IwButton::Minimize;
37+
return IwButton::PinOrMinimize;
3738
}
3839

3940
class IngameWindow : public Window
@@ -83,6 +84,9 @@ class IngameWindow : public Window
8384
/// ist das Fenster minimiert?
8485
bool IsMinimized() const { return isMinimized_; }
8586

87+
void SetPinned(bool pinned = true) { isPinned_ = pinned; }
88+
bool IsPinned() const { return isPinned_; }
89+
8690
CloseBehavior getCloseBehavior() const { return closeBehavior_; }
8791

8892
/// Modal windows cannot be minimized, are always active and stay on top of non-modal ones
@@ -123,6 +127,7 @@ class IngameWindow : public Window
123127

124128
bool isModal_;
125129
bool closeme;
130+
bool isPinned_;
126131
bool isMinimized_;
127132
bool isMoving;
128133
CloseBehavior closeBehavior_;

0 commit comments

Comments
 (0)