[PATCH] richedit: do not hide or show vertical scrollbar if app hides or shows it behind our back
Alex Villacís Lasso
alex at karlalex.palosanto.com
Tue May 13 00:32:07 CDT 2008
Some applications have never heard of ES_DISABLENOSCROLL and attempt to force scrollbars to be always shown (with ShowScrollBar() or similar) when otherwise richedit would hide them. If richedit attempts to wrestle control back, a recursive loop of requests can result if app overrides WM_SIZE behavior. So let the app have its way. Richedit should reclaim control when forced visibility matches default automatic behavior.
Tests to verify this behavior.
---
dlls/riched20/editor.c | 3 +-
dlls/riched20/editstr.h | 1 +
dlls/riched20/paint.c | 44 +++++++++--
dlls/riched20/tests/editor.c | 173 ++++++++++++++++++++++++++++++++++++++++++
4 files changed, 214 insertions(+), 7 deletions(-)
diff --git a/dlls/riched20/editor.c b/dlls/riched20/editor.c
index 81382b3..9f18d5f 100644
--- a/dlls/riched20/editor.c
+++ b/dlls/riched20/editor.c
@@ -1685,7 +1685,7 @@ ME_TextEditor *ME_MakeEditor(HWND hWnd) {
ed->cPasswordMask = 0;
ed->notified_cr.cpMin = ed->notified_cr.cpMax = 0;
-
+ ed->bVScrollEnabled = FALSE;
return ed;
}
@@ -2923,6 +2923,7 @@ static LRESULT RichEditWndProc_common(HWND hWnd, UINT msg, WPARAM wParam,
return MAKELONG( pt.x, pt.y );
}
case WM_CREATE:
+ editor->bVScrollEnabled = ((GetWindowLongW(hWnd, GWL_STYLE) & WS_VSCROLL) != 0);
if (GetWindowLongW(hWnd, GWL_STYLE) & WS_HSCROLL)
{ /* Squelch the default horizontal scrollbar it would make */
ShowScrollBar(editor->hWnd, SB_HORZ, FALSE);
diff --git a/dlls/riched20/editstr.h b/dlls/riched20/editstr.h
index 1e2c770..20aac0d 100644
--- a/dlls/riched20/editstr.h
+++ b/dlls/riched20/editstr.h
@@ -334,6 +334,7 @@ typedef struct tagME_TextEditor
/* Track previous notified selection */
CHARRANGE notified_cr;
+ BOOL bVScrollEnabled;
} ME_TextEditor;
typedef struct tagME_Context
diff --git a/dlls/riched20/paint.c b/dlls/riched20/paint.c
index df2eda8..7bb1a01 100644
--- a/dlls/riched20/paint.c
+++ b/dlls/riched20/paint.c
@@ -651,20 +651,34 @@ void ME_Scroll(ME_TextEditor *editor, int value, int type)
* be used at all. */
HWND hWnd;
- SCROLLINFO si;
+ SCROLLINFO si, si_prev;
+ BOOL bCurrentEnabled;
BOOL bScrollBarWasVisible,bScrollBarWillBeVisible;
+ BOOL bForcedVisible = FALSE, bForcedInvisible = FALSE;
if (ME_WrapMarkedParagraphs(editor))
FIXME("ME_UpdateScrollBar had to call ME_WrapMarkedParagraphs\n");
+ bCurrentEnabled = ((GetWindowLongW(editor->hWnd, GWL_STYLE) & WS_VSCROLL) != 0);
+ if (editor->bVScrollEnabled != bCurrentEnabled)
+ {
+ bForcedInvisible = !(GetWindowLongW(editor->hWnd, GWL_STYLE) & WS_VSCROLL);
+ bForcedVisible = !bForcedInvisible;
+ }
+
hWnd = editor->hWnd;
si.cbSize = sizeof(si);
bScrollBarWasVisible = ME_GetYScrollVisible(editor);
bScrollBarWillBeVisible = editor->nHeight > editor->sizeWindow.cy;
-
+ editor->bVScrollEnabled = bScrollBarWillBeVisible;
if (bScrollBarWasVisible != bScrollBarWillBeVisible)
{
- ShowScrollBar(hWnd, SB_VERT, bScrollBarWillBeVisible);
+ if ((bForcedVisible || bForcedInvisible) && (bForcedVisible != bScrollBarWillBeVisible))
+ ShowScrollBar(hWnd, SB_VERT, bForcedVisible);
+ else if (!(bForcedVisible || bForcedInvisible))
+ ShowScrollBar(hWnd, SB_VERT, bScrollBarWillBeVisible);
+ if (!bForcedVisible && !bForcedInvisible)
+ editor->bVScrollEnabled = ((GetWindowLongW(hWnd, GWL_STYLE) & WS_VSCROLL) != 0);
ME_MarkAllForWrapping(editor);
ME_WrapMarkedParagraphs(editor);
}
@@ -679,12 +693,30 @@ void ME_Scroll(ME_TextEditor *editor, int value, int type)
{
si.nMax = editor->nTotalLength;
si.nPage = editor->sizeWindow.cy;
+ if (bForcedVisible)
+ {
+ TRACE("Setting page to max to preserve scrollbar...\n");
+ si.nPage = si.nMax;
+ }
} else {
si.nMax = si.nPage = 0;
+ if (bForcedVisible)
+ {
+ TRACE("Setting page = max = 1 to preserve scrollbar...\n");
+ si.nPage = si.nMax = 1;
+ }
+ }
+
+ si_prev.cbSize = sizeof(si_prev);
+ si_prev.fMask = SIF_PAGE | SIF_RANGE;
+ GetScrollInfo(hWnd, SB_VERT, &si_prev);
+ if (!(si_prev.nMax == si.nMax && si_prev.nMin == si.nMin && si_prev.nMax == si.nMax))
+ {
+ TRACE("min=%d max=%d page=%d\n", si.nMin, si.nMax, si.nPage);
+ if (!bForcedVisible && !bForcedInvisible)
+ editor->bVScrollEnabled = (si.nMax > si.nPage);
+ SetScrollInfo(hWnd, SB_VERT, &si, TRUE);
}
-
- TRACE("min=%d max=%d page=%d\n", si.nMin, si.nMax, si.nPage);
- SetScrollInfo(hWnd, SB_VERT, &si, TRUE);
}
int ME_GetYScrollPos(ME_TextEditor *editor)
diff --git a/dlls/riched20/tests/editor.c b/dlls/riched20/tests/editor.c
index 5dabe62..c17cc40 100644
--- a/dlls/riched20/tests/editor.c
+++ b/dlls/riched20/tests/editor.c
@@ -2055,6 +2055,178 @@ static void test_EM_SCROLL(void)
DestroyWindow(hwndRichEdit);
}
+static void test_scrollbar_visibility(void)
+{
+ HWND hwndRichEdit = new_richedit(NULL);
+ const char * text="a\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n";
+ SCROLLBARINFO sbi;
+
+ /* These tests show that richedit should temporarily refrain from automatically
+ hiding or showing its scrollbars (vertical at least) when an explicit request
+ is made via ShowScrollBar() or similar, outside of standard richedit logic.
+ Some applications depend on forced showing (when otherwise richedit would
+ hide the vertical scrollbar) and are thrown on an endless recursive loop
+ if richedit auto-hides the scrollbar again. Apparently they never heard of
+ the ES_DISABLENOSCROLL style... */
+
+ /* Test default scrollbar visibility behavior */
+ memset(&sbi, 0, sizeof(sbi));
+ sbi.cbSize = sizeof(sbi);
+ GetScrollBarInfo(hwndRichEdit, OBJID_VSCROLL, &sbi);
+ trace("sbi.rgstate[0] == 0x%08x\n", sbi.rgstate[0]);
+ ok (((sbi.rgstate[0] & STATE_SYSTEM_INVISIBLE) != 0),
+ "Vertical scrollbar is visible, should be invisible.\n");
+
+ SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
+ memset(&sbi, 0, sizeof(sbi));
+ sbi.cbSize = sizeof(sbi);
+ GetScrollBarInfo(hwndRichEdit, OBJID_VSCROLL, &sbi);
+ trace("sbi.rgstate[0] == 0x%08x\n", sbi.rgstate[0]);
+ ok (((sbi.rgstate[0] & STATE_SYSTEM_INVISIBLE) != 0),
+ "Vertical scrollbar is visible, should be invisible.\n");
+
+ SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
+ memset(&sbi, 0, sizeof(sbi));
+ sbi.cbSize = sizeof(sbi);
+ GetScrollBarInfo(hwndRichEdit, OBJID_VSCROLL, &sbi);
+ trace("sbi.rgstate[0] == 0x%08x\n", sbi.rgstate[0]);
+ ok (((sbi.rgstate[0] & STATE_SYSTEM_INVISIBLE) == 0),
+ "Vertical scrollbar is invisible, should be visible.\n");
+
+ DestroyWindow(hwndRichEdit);
+
+ /* Test behavior with explicit visibility request */
+ hwndRichEdit = new_richedit(NULL);
+
+ /* Previously failed because builtin incorrectly re-hides scrollbar forced visible */
+ trace("explicitly requesting visible scrollbar\n");
+ ShowScrollBar(hwndRichEdit, SB_VERT, TRUE);
+ memset(&sbi, 0, sizeof(sbi));
+ sbi.cbSize = sizeof(sbi);
+ GetScrollBarInfo(hwndRichEdit, OBJID_VSCROLL, &sbi);
+ trace("sbi.rgstate[0] == 0x%08x\n", sbi.rgstate[0]);
+ ok (((sbi.rgstate[0] & STATE_SYSTEM_INVISIBLE) == 0),
+ "Vertical scrollbar is invisible, should be visible.\n");
+
+ /* Ditto, see above */
+ SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
+ memset(&sbi, 0, sizeof(sbi));
+ sbi.cbSize = sizeof(sbi);
+ GetScrollBarInfo(hwndRichEdit, OBJID_VSCROLL, &sbi);
+ trace("sbi.rgstate[0] == 0x%08x\n", sbi.rgstate[0]);
+ ok (((sbi.rgstate[0] & STATE_SYSTEM_INVISIBLE) == 0),
+ "Vertical scrollbar is invisible, should be visible.\n");
+
+ /* Ditto, see above */
+ SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
+ memset(&sbi, 0, sizeof(sbi));
+ sbi.cbSize = sizeof(sbi);
+ GetScrollBarInfo(hwndRichEdit, OBJID_VSCROLL, &sbi);
+ trace("sbi.rgstate[0] == 0x%08x\n", sbi.rgstate[0]);
+ ok (((sbi.rgstate[0] & STATE_SYSTEM_INVISIBLE) == 0),
+ "Vertical scrollbar is invisible, should be visible.\n");
+
+ /* Ditto, see above */
+ SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a\na");
+ memset(&sbi, 0, sizeof(sbi));
+ sbi.cbSize = sizeof(sbi);
+ GetScrollBarInfo(hwndRichEdit, OBJID_VSCROLL, &sbi);
+ trace("sbi.rgstate[0] == 0x%08x\n", sbi.rgstate[0]);
+ ok (((sbi.rgstate[0] & STATE_SYSTEM_INVISIBLE) == 0),
+ "Vertical scrollbar is invisible, should be visible.\n");
+
+ /* Ditto, see above */
+ SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
+ memset(&sbi, 0, sizeof(sbi));
+ sbi.cbSize = sizeof(sbi);
+ GetScrollBarInfo(hwndRichEdit, OBJID_VSCROLL, &sbi);
+ trace("sbi.rgstate[0] == 0x%08x\n", sbi.rgstate[0]);
+ ok (((sbi.rgstate[0] & STATE_SYSTEM_INVISIBLE) == 0),
+ "Vertical scrollbar is invisible, should be visible.\n");
+
+ SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
+ SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
+ memset(&sbi, 0, sizeof(sbi));
+ sbi.cbSize = sizeof(sbi);
+ GetScrollBarInfo(hwndRichEdit, OBJID_VSCROLL, &sbi);
+ trace("sbi.rgstate[0] == 0x%08x\n", sbi.rgstate[0]);
+ ok (((sbi.rgstate[0] & STATE_SYSTEM_INVISIBLE) != 0),
+ "Vertical scrollbar is visible, should be invisible.\n");
+
+ DestroyWindow(hwndRichEdit);
+
+ hwndRichEdit = new_richedit(NULL);
+
+ trace("explicitly requesting invisible scrollbar\n");
+ ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
+ memset(&sbi, 0, sizeof(sbi));
+ sbi.cbSize = sizeof(sbi);
+ GetScrollBarInfo(hwndRichEdit, OBJID_VSCROLL, &sbi);
+ trace("sbi.rgstate[0] == 0x%08x\n", sbi.rgstate[0]);
+ ok (((sbi.rgstate[0] & STATE_SYSTEM_INVISIBLE) != 0),
+ "Vertical scrollbar is visible, should be invisible.\n");
+
+ SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
+ memset(&sbi, 0, sizeof(sbi));
+ sbi.cbSize = sizeof(sbi);
+ GetScrollBarInfo(hwndRichEdit, OBJID_VSCROLL, &sbi);
+ trace("sbi.rgstate[0] == 0x%08x\n", sbi.rgstate[0]);
+ ok (((sbi.rgstate[0] & STATE_SYSTEM_INVISIBLE) != 0),
+ "Vertical scrollbar is visible, should be invisible.\n");
+
+ SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)"a");
+ memset(&sbi, 0, sizeof(sbi));
+ sbi.cbSize = sizeof(sbi);
+ GetScrollBarInfo(hwndRichEdit, OBJID_VSCROLL, &sbi);
+ trace("sbi.rgstate[0] == 0x%08x\n", sbi.rgstate[0]);
+ ok (((sbi.rgstate[0] & STATE_SYSTEM_INVISIBLE) != 0),
+ "Vertical scrollbar is visible, should be invisible.\n");
+
+ SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
+ memset(&sbi, 0, sizeof(sbi));
+ sbi.cbSize = sizeof(sbi);
+ GetScrollBarInfo(hwndRichEdit, OBJID_VSCROLL, &sbi);
+ trace("sbi.rgstate[0] == 0x%08x\n", sbi.rgstate[0]);
+ ok (((sbi.rgstate[0] & STATE_SYSTEM_INVISIBLE) != 0),
+ "Vertical scrollbar is visible, should be invisible.\n");
+
+ SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
+ memset(&sbi, 0, sizeof(sbi));
+ sbi.cbSize = sizeof(sbi);
+ GetScrollBarInfo(hwndRichEdit, OBJID_VSCROLL, &sbi);
+ trace("sbi.rgstate[0] == 0x%08x\n", sbi.rgstate[0]);
+ ok (((sbi.rgstate[0] & STATE_SYSTEM_INVISIBLE) == 0),
+ "Vertical scrollbar is invisible, should be visible.\n");
+
+ /* Previously, builtin incorrectly re-shows explicitly hidden scrollbar */
+ trace("explicitly requesting invisible scrollbar\n");
+ ShowScrollBar(hwndRichEdit, SB_VERT, FALSE);
+ memset(&sbi, 0, sizeof(sbi));
+ sbi.cbSize = sizeof(sbi);
+ GetScrollBarInfo(hwndRichEdit, OBJID_VSCROLL, &sbi);
+ trace("sbi.rgstate[0] == 0x%08x\n", sbi.rgstate[0]);
+ ok (((sbi.rgstate[0] & STATE_SYSTEM_INVISIBLE) != 0),
+ "Vertical scrollbar is visible, should be invisible.\n");
+
+ SendMessage(hwndRichEdit, WM_SETTEXT, 0, 0);
+ memset(&sbi, 0, sizeof(sbi));
+ sbi.cbSize = sizeof(sbi);
+ GetScrollBarInfo(hwndRichEdit, OBJID_VSCROLL, &sbi);
+ trace("sbi.rgstate[0] == 0x%08x\n", sbi.rgstate[0]);
+ ok (((sbi.rgstate[0] & STATE_SYSTEM_INVISIBLE) != 0),
+ "Vertical scrollbar is visible, should be invisible.\n");
+
+ SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)text);
+ memset(&sbi, 0, sizeof(sbi));
+ sbi.cbSize = sizeof(sbi);
+ GetScrollBarInfo(hwndRichEdit, OBJID_VSCROLL, &sbi);
+ trace("sbi.rgstate[0] == 0x%08x\n", sbi.rgstate[0]);
+ ok (((sbi.rgstate[0] & STATE_SYSTEM_INVISIBLE) == 0),
+ "Vertical scrollbar is invisible, should be visible.\n");
+
+ DestroyWindow(hwndRichEdit);
+}
+
static void test_EM_SETUNDOLIMIT(void)
{
/* cases we test for:
@@ -4168,6 +4340,7 @@ START_TEST( editor )
test_EM_GETLINE();
test_EM_SCROLLCARET();
test_EM_SCROLL();
+ test_scrollbar_visibility();
test_WM_SETTEXT();
test_EM_LINELENGTH();
test_EM_SETCHARFORMAT();
--
1.5.4.1
--------------010303010100050309090304--
More information about the wine-patches
mailing list