Skip to content

Commit afae67c

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 2f70c9a commit afae67c

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 doesn'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(DrawPoint::MaxElementValue, DrawPoint::MaxElementValue);
2829
const DrawPoint IngameWindow::posCenter(DrawPoint::MaxElementValue - 1, DrawPoint::MaxElementValue);
@@ -32,7 +33,8 @@ const Extent IngameWindow::borderSize(1, 1);
3233
IngameWindow::IngameWindow(unsigned id, const DrawPoint& pos, const Extent& size, std::string title,
3334
glArchivItem_Bitmap* background, bool modal, CloseBehavior closeBehavior, Window* parent)
3435
: Window(parent, id, pos, size), title_(std::move(title)), background(background), lastMousePos(0, 0),
35-
isModal_(modal), closeme(false), isMinimized_(false), isMoving(false), closeBehavior_(closeBehavior)
36+
isModal_(modal), closeme(false), isPinned_(false), isMinimized_(false), isMoving(false),
37+
closeBehavior_(closeBehavior)
3638
{
3739
std::fill(buttonStates_.begin(), buttonStates_.end(), ButtonState::Up);
3840
contentOffset.x = LOADER.GetImageN("resource", 38)->getWidth(); // left border
@@ -211,17 +213,32 @@ void IngameWindow::MouseLeftUp(const MouseCoords& mc)
211213
buttonStates_[btn] = ButtonState::Up;
212214

213215
if((btn == IwButton::Close && closeBehavior_ == CloseBehavior::Custom) // no close button
214-
|| (btn == IwButton::Minimize && isModal_)) // modal windows cannot be minimized
216+
|| (isModal_ // modal windows cannot be pinned or minimized
217+
&& (btn == IwButton::Title || btn == IwButton::PinOrMinimize)))
215218
continue;
216219

217220
if(IsPointInRect(mc.GetPos(), GetButtonBounds(btn)))
218221
{
219222
switch(btn)
220223
{
221224
case IwButton::Close: Close(); break;
222-
case IwButton::Minimize:
223-
SetMinimized(!IsMinimized());
224-
LOADER.GetSoundN("sound", 113)->Play(255, false);
225+
case IwButton::Title:
226+
if(SETTINGS.interface.enableWindowPinning && mc.dbl_click)
227+
{
228+
SetMinimized(!IsMinimized());
229+
LOADER.GetSoundN("sound", 113)->Play(255, false);
230+
}
231+
break;
232+
case IwButton::PinOrMinimize:
233+
if(SETTINGS.interface.enableWindowPinning)
234+
{
235+
SetPinned(!IsPinned());
236+
LOADER.GetSoundN("sound", 111)->Play(255, false);
237+
} else
238+
{
239+
SetMinimized(!IsMinimized());
240+
LOADER.GetSoundN("sound", 113)->Play(255, false);
241+
}
225242
break;
226243
}
227244
}
@@ -294,11 +311,27 @@ void IngameWindow::Draw_()
294311
using ButtonStateResIds = helpers::EnumArray<unsigned, ButtonState>;
295312
constexpr ButtonStateResIds closeResIds = {47, 55, 51};
296313
constexpr ButtonStateResIds minimizeResIds = {48, 56, 52};
314+
constexpr ButtonStateResIds pinBaseResIds = {47, 47, 51};
315+
constexpr auto overlayIdsStart = 15u;
316+
constexpr ButtonStateResIds pinOverlayResIdsRel = {0, 1, 2};
317+
constexpr ButtonStateResIds unpinOverlayResIdsRel = {3, 4, 5};
297318
if(closeBehavior_ != CloseBehavior::Custom)
298-
LOADER.GetImageN("resource", closeResIds[buttonStates_[IwButton::Close]])->DrawFull(GetPos());
319+
LOADER.GetImageN("resource", closeResIds[buttonStates_[IwButton::Close]])
320+
->DrawFull(GetButtonBounds(IwButton::Close));
299321
if(!IsModal())
300-
LOADER.GetImageN("resource", minimizeResIds[buttonStates_[IwButton::Minimize]])
301-
->DrawFull(GetButtonBounds(IwButton::Minimize));
322+
{
323+
const auto buttonState = buttonStates_[IwButton::PinOrMinimize];
324+
const auto bounds = GetButtonBounds(IwButton::PinOrMinimize);
325+
if(SETTINGS.interface.enableWindowPinning)
326+
{
327+
LOADER.GetImageN("resource", pinBaseResIds[buttonState])->DrawFull(bounds);
328+
if(isPinned_)
329+
LOADER.GetImageN("io_new", unpinOverlayResIdsRel[buttonState] + overlayIdsStart)->DrawFull(bounds);
330+
else
331+
LOADER.GetImageN("io_new", pinOverlayResIdsRel[buttonState] + overlayIdsStart)->DrawFull(bounds);
332+
} else
333+
LOADER.GetImageN("resource", minimizeResIds[buttonState])->DrawFull(bounds);
334+
}
302335

303336
// The title bar
304337
unsigned titleIndex;
@@ -410,10 +443,15 @@ void IngameWindow::SaveOpenStatus(bool isOpen) const
410443
Rect IngameWindow::GetButtonBounds(IwButton btn) const
411444
{
412445
auto pos = GetPos();
446+
auto size = ButtonSize;
413447
switch(btn)
414448
{
415449
case IwButton::Close: break;
416-
case IwButton::Minimize: pos.x += GetSize().x - ButtonSize.x; break;
450+
case IwButton::Title:
451+
pos.x += TitleMargin;
452+
size.x = GetSize().x - TitleMargin * 2;
453+
break;
454+
case IwButton::PinOrMinimize: pos.x += GetSize().x - ButtonSize.x; break;
417455
}
418-
return Rect(pos, ButtonSize);
456+
return Rect(pos, size);
419457
}

libs/s25main/ingameWindows/IngameWindow.h

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

4041
class IngameWindow : public Window
@@ -86,6 +87,9 @@ class IngameWindow : public Window
8687
/// ist das Fenster minimiert?
8788
bool IsMinimized() const { return isMinimized_; }
8889

90+
void SetPinned(bool pinned = true) { isPinned_ = pinned; }
91+
bool IsPinned() const { return isPinned_; }
92+
8993
CloseBehavior getCloseBehavior() const { return closeBehavior_; }
9094

9195
/// Modal windows cannot be minimized, are always active and stay on top of non-modal ones
@@ -126,6 +130,7 @@ class IngameWindow : public Window
126130

127131
bool isModal_;
128132
bool closeme;
133+
bool isPinned_;
129134
bool isMinimized_;
130135
bool isMoving;
131136
CloseBehavior closeBehavior_;

0 commit comments

Comments
 (0)