Skip to content

Commit 514a14a

Browse files
committed
Viewer: exit full screen with Esc (#82)
1 parent a4fe245 commit 514a14a

7 files changed

Lines changed: 132 additions & 28 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ All notable changes to Q1View are documented here. Releases follow [semantic ver
77
## [Unreleased]
88

99
### Changed
10+
- Viewer: pressing `Esc` now exits full-screen mode and returns to the previous normal window state, even when focus is in the thumbnail drawer or another Viewer child control. `Return` still toggles full screen. (issue #82)
1011
- Viewer: the thumbnail drawer now always starts closed, so every launch looks like the classic Viewer and the drawer appears only when you open it (the `E` shortcut). Its width is still remembered, so it reopens at the size you last used; only the open/closed state is no longer persisted.
1112
- Viewer: when the thumbnail drawer has focus, `PgUp`/`PgDn` now step the selection to the previous/next file (matching the main view's file navigation) instead of scrolling by a page — in the gallery grid the page was the whole list, so they had simply duplicated `Home`/`End`. They move the selection only (arrow keys still browse folders; `Enter`/double-click still opens), while `Home`/`End` continue to jump to the first/last item. (issue #84)
1213

QVisionCore/QViewerShortcuts.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,14 @@ static const Q1ViewShortcutRow Q1VIEW_SHORTCUTS[] = {
4141
{ "Ctrl + O", "Open an image", Q1VIEW_FE_QT },
4242
{ "Mouse Wheel", "Zoom in or out; high zoom shows pixel values", Q1VIEW_FE_ALL },
4343
{ "Left Drag", "Pan the image", Q1VIEW_FE_QT },
44-
{ "Return", "Full screen", Q1VIEW_FE_ALL },
44+
{ "Return", "Enter or exit full screen", Q1VIEW_FE_ALL },
4545
{ "H", "Toggle hex pixel values", Q1VIEW_FE_ALL },
4646
{ "Y", "Toggle Y-only view", Q1VIEW_FE_ALL },
4747
{ "V", "Toggle RGB/source YUV pixel values", Q1VIEW_FE_ALL },
4848
{ "R", "Rotate 90 degrees clockwise", Q1VIEW_FE_ALL },
4949
{ "Page Up/Down", "Previous or next file", Q1VIEW_FE_ALL },
5050
{ "S", "Selection capture mode", Q1VIEW_FE_ALL },
51+
{ "Esc", "Exit full screen", Q1VIEW_FE_MFC },
5152
{ "Esc", "Clear selection", Q1VIEW_FE_QT },
5253
{ "Ctrl + C", "Capture view or selected region", Q1VIEW_FE_ALL },
5354
{ "Ctrl + V", "Paste image from clipboard", Q1VIEW_FE_ALL },

Viewer/MainFrm.cpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,19 @@ BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
501501
return TRUE;
502502
}
503503

504+
BOOL CMainFrame::PreTranslateMessage(MSG *pMsg)
505+
{
506+
if (pMsg != NULL && pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_ESCAPE) {
507+
CViewerView *pView = DYNAMIC_DOWNCAST(CViewerView, GetActiveView());
508+
if (pView != NULL && pView->IsFullScreen()) {
509+
pView->ToggleFullScreen();
510+
return TRUE;
511+
}
512+
}
513+
514+
return CFrameWnd::PreTranslateMessage(pMsg);
515+
}
516+
504517
void CMainFrame::OnUpdateFrameTitle(BOOL bAddToTitle)
505518
{
506519
if ((GetStyle() & FWS_ADDTOTITLE) == 0)
@@ -1175,7 +1188,11 @@ void CMainFrame::UpdateMagnication(float n, int wDst, int hDst)
11751188
CString str;
11761189
str.Format(_T("%dx%d (%.2fx)"), wDst, hDst, n);
11771190

1178-
GetMenu()->ModifyMenu(ID_MAGNIFY, MF_BYCOMMAND | MF_RIGHTJUSTIFY | MF_GRAYED, ID_MAGNIFY, str);
1191+
CMenu *pMenu = GetMenu();
1192+
if (pMenu == NULL)
1193+
return;
1194+
1195+
pMenu->ModifyMenu(ID_MAGNIFY, MF_BYCOMMAND | MF_RIGHTJUSTIFY | MF_GRAYED, ID_MAGNIFY, str);
11791196

11801197
// While the drawer slides or the divider is dragged, the image refits every
11811198
// frame; DrawMenuBar repaints the whole (non-buffered) menu bar each time, which

Viewer/MainFrm.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ class CMainFrame : public CFrameWnd
165165
// Overrides
166166
public:
167167
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
168+
virtual BOOL PreTranslateMessage(MSG *pMsg);
168169
virtual void OnUpdateFrameTitle(BOOL bAddToTitle);
169170
virtual BOOL OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext);
170171

Viewer/ViewerView.cpp

Lines changed: 104 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
#include "Q1ViewVersion.h"
2222

2323
#include <cmath>
24+
#include <dwmapi.h>
25+
26+
#pragma comment(lib, "dwmapi.lib")
2427

2528
#ifdef _DEBUG
2629
#define new DEBUG_NEW
@@ -38,6 +41,9 @@ const bool printPlaySpeed = false;
3841

3942
#define WM_VIEWER_PLAY_TIMER (WM_APP + 1)
4043

44+
static const UINT_PTR VIEWER_FULLSCREEN_DWM_TIMER = 0x5100;
45+
static const UINT VIEWER_FULLSCREEN_DWM_DELAY_MS = 250;
46+
4147
enum QMouseMenuID
4248
{
4349
QMouseMenuStart = 0x10000000,
@@ -72,6 +78,7 @@ BEGIN_MESSAGE_MAP(CViewerView, CView)
7278
ON_WM_DESTROY()
7379
ON_WM_CREATE()
7480
ON_WM_SIZE()
81+
ON_WM_TIMER()
7582
ON_WM_SETCURSOR()
7683
ON_WM_RBUTTONUP()
7784
ON_MESSAGE(WM_VIEWER_PLAY_TIMER, CViewerView::OnPlayTimer)
@@ -120,7 +127,7 @@ CViewerView::CViewerView()
120127
, mFullMode(false)
121128
, mShowCoord(false)
122129
, mShowBoxInfo(true)
123-
, mWasZoomed(false)
130+
, mHavePreFullPlacement(false)
124131
, mHexMode(false)
125132
, mShowSourceYuv(true)
126133
, mRgbHex(_T("%02X\n%02X\n%02X"))
@@ -129,6 +136,9 @@ CViewerView::CViewerView()
129136
, mNewSel(false)
130137
, mDeltaIdx(-1)
131138
{
139+
::ZeroMemory(&mPreFullPlacement, sizeof(mPreFullPlacement));
140+
mPreFullPlacement.length = sizeof(mPreFullPlacement);
141+
132142
LOGFONT lf;
133143

134144
::ZeroMemory(&lf, sizeof(lf));
@@ -262,11 +272,16 @@ void CViewerView::AdjustWindowSize()
262272

263273
void CViewerView::FitToWindow()
264274
{
265-
// The frame size is unchanged, so OnSize did not run; refresh the canvas
266-
// extent from the current client size in case Initialize() toggled the
267-
// per-frame progress bar for the newly loaded source.
275+
CRect rcClient;
276+
GetClientRect(&rcClient);
277+
mWClient = rcClient.Width();
278+
mHClient = rcClient.Height();
279+
280+
// Refresh the canvas extent from the actual client size. During frame style
281+
// changes OnSize can arrive after the first repaint, so do not rely on the
282+
// cached size when fitting a full-screen transition.
268283
mWCanvas = mWClient;
269-
mHCanvas = mHClient - mHProgress;
284+
mHCanvas = max(0, mHClient - mHProgress);
270285

271286
// Scale the freshly loaded image so the whole image is visible in the
272287
// current viewport, without touching the frame size. Mirroring common
@@ -1696,32 +1711,69 @@ void CViewerView::ToggleFullScreen()
16961711
{
16971712
// In this SDI app the view's parent is CMainFrame.
16981713
CMainFrame *pMainFrm = static_cast<CMainFrame *>(AfxGetMainWnd());
1699-
mFullMode = !mFullMode;
1700-
1701-
if (mFullMode) {
1702-
pMainFrm->SetMenu(NULL);
1703-
if (pMainFrm->IsZoomed()) {
1704-
pMainFrm->SendMessage(WM_SYSCOMMAND, SC_RESTORE,
1705-
(LPARAM)pMainFrm->GetSafeHwnd());
1706-
mWasZoomed = true;
1714+
if (pMainFrm == NULL)
1715+
return;
1716+
1717+
const bool entering = !mFullMode;
1718+
pMainFrm->SetRedraw(FALSE);
1719+
SetRedraw(FALSE);
1720+
::LockWindowUpdate(pMainFrm->GetSafeHwnd());
1721+
KillTimer(VIEWER_FULLSCREEN_DWM_TIMER);
1722+
BOOL disableDwmTransitions = TRUE;
1723+
::DwmSetWindowAttribute(pMainFrm->GetSafeHwnd(),
1724+
DWMWA_TRANSITIONS_FORCEDISABLED,
1725+
&disableDwmTransitions,
1726+
sizeof(disableDwmTransitions));
1727+
1728+
if (entering) {
1729+
mPreFullPlacement.length = sizeof(mPreFullPlacement);
1730+
mHavePreFullPlacement = pMainFrm->GetWindowPlacement(&mPreFullPlacement) != FALSE;
1731+
if (mPreFullPlacement.showCmd == SW_SHOWMINIMIZED)
1732+
mPreFullPlacement.showCmd = SW_SHOWNORMAL;
1733+
1734+
mFullMode = true;
1735+
1736+
MONITORINFO mi = {};
1737+
mi.cbSize = sizeof(mi);
1738+
HMONITOR hmon = ::MonitorFromWindow(pMainFrm->GetSafeHwnd(), MONITOR_DEFAULTTONEAREST);
1739+
if (::GetMonitorInfo(hmon, &mi)) {
1740+
CRect rc(mi.rcMonitor);
1741+
pMainFrm->SetMenu(NULL);
1742+
pMainFrm->ModifyStyle(
1743+
WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,
1744+
WS_POPUP, 0);
1745+
pMainFrm->SetWindowPos(&CWnd::wndTop, rc.left, rc.top,
1746+
rc.Width(), rc.Height(),
1747+
SWP_NOOWNERZORDER | SWP_FRAMECHANGED | SWP_NOREDRAW);
17071748
}
1708-
pMainFrm->ModifyStyle(WS_CAPTION | WS_SYSMENU, WS_POPUP, SWP_FRAMECHANGED);
1709-
pMainFrm->SendMessage(WM_SYSCOMMAND, SC_MAXIMIZE,
1710-
(LPARAM)pMainFrm->GetSafeHwnd());
17111749
} else {
1750+
mFullMode = false;
17121751
HMENU hMenu = ::LoadMenu(theApp.m_hInstance, MAKEINTRESOURCE(IDR_MAINFRAME));
17131752
::SetMenu(pMainFrm->GetSafeHwnd(), hMenu);
1714-
pMainFrm->ModifyStyle(WS_POPUP, WS_CAPTION | WS_SYSMENU, SWP_FRAMECHANGED);
1753+
pMainFrm->ModifyStyle(WS_POPUP,
1754+
WS_OVERLAPPED | WS_CAPTION | FWS_ADDTOTITLE |
1755+
WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SYSMENU | WS_THICKFRAME,
1756+
0);
17151757
pMainFrm->AddMainMenu();
17161758
GetDocument()->UpdateMenu();
1717-
pMainFrm->SendMessage(WM_SYSCOMMAND, SC_RESTORE,
1718-
(LPARAM)pMainFrm->GetSafeHwnd());
1719-
if (mWasZoomed) {
1720-
pMainFrm->SendMessage(WM_SYSCOMMAND, SC_MAXIMIZE,
1721-
(LPARAM)pMainFrm->GetSafeHwnd());
1722-
mWasZoomed = false;
1723-
}
1759+
if (mHavePreFullPlacement)
1760+
pMainFrm->SetWindowPlacement(&mPreFullPlacement);
1761+
else
1762+
pMainFrm->ShowWindow(SW_RESTORE);
1763+
pMainFrm->SetWindowPos(NULL, 0, 0, 0, 0,
1764+
SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
1765+
SWP_NOOWNERZORDER | SWP_FRAMECHANGED | SWP_NOREDRAW);
17241766
}
1767+
1768+
pMainFrm->RecalcLayout(TRUE);
1769+
FitToWindow();
1770+
pMainFrm->SetRedraw(TRUE);
1771+
SetRedraw(TRUE);
1772+
::LockWindowUpdate(NULL);
1773+
pMainFrm->SetForegroundWindow();
1774+
pMainFrm->RedrawWindow(NULL, NULL,
1775+
RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN | RDW_FRAME | RDW_ERASE);
1776+
SetTimer(VIEWER_FULLSCREEN_DWM_TIMER, VIEWER_FULLSCREEN_DWM_DELAY_MS, NULL);
17251777
}
17261778

17271779
void CViewerView::ToggleHelp()
@@ -2049,6 +2101,16 @@ void CViewerView::OnDestroy()
20492101
{
20502102
CView::OnDestroy();
20512103

2104+
KillTimer(VIEWER_FULLSCREEN_DWM_TIMER);
2105+
CMainFrame *pMainFrm = static_cast<CMainFrame *>(AfxGetMainWnd());
2106+
if (pMainFrm != NULL) {
2107+
BOOL disableDwmTransitions = FALSE;
2108+
::DwmSetWindowAttribute(pMainFrm->GetSafeHwnd(),
2109+
DWMWA_TRANSITIONS_FORCEDISABLED,
2110+
&disableDwmTransitions,
2111+
sizeof(disableDwmTransitions));
2112+
}
2113+
20522114
mBufferPool->disable();
20532115
mBufferQueue->destroy();
20542116
KillPlayTimer();
@@ -2090,6 +2152,24 @@ void CViewerView::OnSize(UINT nType, int cx, int cy)
20902152
mRcProgress.SetRect(0, mHCanvas, mWClient, mHClient);
20912153
}
20922154

2155+
void CViewerView::OnTimer(UINT_PTR nIDEvent)
2156+
{
2157+
if (nIDEvent == VIEWER_FULLSCREEN_DWM_TIMER) {
2158+
KillTimer(VIEWER_FULLSCREEN_DWM_TIMER);
2159+
CMainFrame *pMainFrm = static_cast<CMainFrame *>(AfxGetMainWnd());
2160+
if (pMainFrm != NULL) {
2161+
BOOL disableDwmTransitions = FALSE;
2162+
::DwmSetWindowAttribute(pMainFrm->GetSafeHwnd(),
2163+
DWMWA_TRANSITIONS_FORCEDISABLED,
2164+
&disableDwmTransitions,
2165+
sizeof(disableDwmTransitions));
2166+
}
2167+
return;
2168+
}
2169+
2170+
CView::OnTimer(nIDEvent);
2171+
}
2172+
20932173
void CViewerView::OnRButtonUp(UINT nFlags, CPoint point)
20942174
{
20952175
if ((!mRcVolume.IsRectEmpty() && mRcVolume.PtInRect(point)) ||

Viewer/ViewerView.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,8 @@ class CViewerView : public CView
166166
bool mFullMode;
167167
bool mShowCoord;
168168
bool mShowBoxInfo;
169-
bool mWasZoomed;
169+
bool mHavePreFullPlacement;
170+
WINDOWPLACEMENT mPreFullPlacement;
170171
bool mHexMode;
171172
bool mShowSourceYuv;
172173
const CString mRgbHex;
@@ -198,6 +199,7 @@ class CViewerView : public CView
198199
void ChangeZoom(short zDelta, CPoint &pt);
199200
cv::Mat CreateRoiMat(int x0, int y0, int w, int h);
200201
void ToggleFullScreen();
202+
bool IsFullScreen() const { return mFullMode; }
201203
void DrawPixelText(CDC *pDC, q1::GridInfo &gi);
202204
void DrawPixelValueMode(CDC *pDC);
203205
void ToggleHelp();
@@ -254,6 +256,7 @@ class CViewerView : public CView
254256
afx_msg void OnDestroy();
255257
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
256258
afx_msg void OnSize(UINT nType, int cx, int cy);
259+
afx_msg void OnTimer(UINT_PTR nIDEvent);
257260
afx_msg void OnRButtonUp(UINT nFlags, CPoint point);
258261
afx_msg LRESULT OnPlayTimer(WPARAM wParam, LPARAM lParam);
259262
};

docs/USER_GUIDE.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ Viewer includes a built-in control panel, opened with `?`.
4646
| Toggle thumbnail browser (drawer) | `E` |
4747
| Paste image from clipboard | `Ctrl+V` |
4848
| Zoom | Mouse wheel |
49-
| Full screen | `Return` |
49+
| Enter or exit full screen | `Return` |
50+
| Exit full screen | `Esc` |
5051
| Play or pause video | `Space` |
5152
| Previous or next frame | Left / Right |
5253
| First or last frame | Home / End |

0 commit comments

Comments
 (0)