user32: EC_USEFONTINFO behaviour depends on whether the font's charset is CJK or not.

Huw Davies huw at codeweavers.com
Thu Sep 1 05:35:10 CDT 2016


In the non-CJK case, the margins are half of the width returned by
GdiGetCharDimensions() but only if the client rect's width is above a
certain size.  There is an exception if the client rect is empty if
the font is sufficiently small.

In the CJK case, the margins are independent of the client rect size.

Signed-off-by: Huw Davies <huw at codeweavers.com>
---
 dlls/user32/edit.c       |  60 ++++++++++---------
 dlls/user32/tests/edit.c | 153 +++++++++++++++++++++++++++++++++++------------
 2 files changed, 147 insertions(+), 66 deletions(-)

diff --git a/dlls/user32/edit.c b/dlls/user32/edit.c
index 46fda41..f5cd3c4 100644
--- a/dlls/user32/edit.c
+++ b/dlls/user32/edit.c
@@ -2864,24 +2864,24 @@ static void EDIT_EM_SetLimitText(EDITSTATE *es, UINT limit)
  * action wParam despite what the docs say. EC_USEFONTINFO calculates the
  * margin according to the textmetrics of the current font.
  *
- * FIXME - With TrueType or vector fonts EC_USEFONTINFO currently sets one third
- * of the char's width as the margin, but this is not how Windows handles this.
- * For all other fonts Windows sets the margins to zero.
- *
- * FIXME - When EC_USEFONTINFO is used the margins only change if the
- * edit control is equal to or larger than a certain size.
- * Interestingly if one subtracts both the left and right margins from
- * this size one always seems to get an even number.  The extents of
- * the (four character) string "'**'" match this quite closely, so
- * we'll use this until we come up with a better idea.
+ * When EC_USEFONTINFO is used in the non_cjk case the margins only
+ * change if the edit control is equal to or larger than a certain
+ * size.  Though there is an exception for the empty client rect case
+ * with small font sizes.
  */
-static int calc_min_set_margin_size(HDC dc, INT left, INT right)
+static BOOL is_cjk(UINT charset)
 {
-    static const WCHAR magic_string[] = {'\'','*','*','\'', 0};
-    SIZE sz;
-
-    GetTextExtentPointW(dc, magic_string, sizeof(magic_string)/sizeof(WCHAR) - 1, &sz);
-    return sz.cx + left + right;
+    switch(charset)
+    {
+    case SHIFTJIS_CHARSET:
+    case HANGUL_CHARSET:
+    case GB2312_CHARSET:
+    case CHINESEBIG5_CHARSET:
+        return TRUE;
+    }
+    /* HANGUL_CHARSET is strange, though treated as CJK by Win 8, it is
+     * not by other versions including Win 10. */
+    return FALSE;
 }
 
 static void EDIT_EM_SetMargins(EDITSTATE *es, INT action,
@@ -2895,19 +2895,25 @@ static void EDIT_EM_SetMargins(EDITSTATE *es, INT action,
         if (es->font && (left == EC_USEFONTINFO || right == EC_USEFONTINFO)) {
             HDC dc = GetDC(es->hwndSelf);
             HFONT old_font = SelectObject(dc, es->font);
-            GetTextMetricsW(dc, &tm);
+            LONG width = GdiGetCharDimensions(dc, &tm, NULL);
+            RECT rc;
+
             /* The default margins are only non zero for TrueType or Vector fonts */
             if (tm.tmPitchAndFamily & ( TMPF_VECTOR | TMPF_TRUETYPE )) {
-                int min_size;
-                RECT rc;
-                /* This must be calculated more exactly! But how? */
-                default_left_margin = tm.tmAveCharWidth / 2;
-                default_right_margin = tm.tmAveCharWidth / 2;
-                min_size = calc_min_set_margin_size(dc, default_left_margin, default_right_margin);
-                GetClientRect(es->hwndSelf, &rc);
-                if (!IsRectEmpty(&rc) && (rc.right - rc.left < min_size)) {
-                    default_left_margin = es->left_margin;
-                    default_right_margin = es->right_margin;
+                if (!is_cjk(tm.tmCharSet)) {
+                    default_left_margin = width / 2;
+                    default_right_margin = width / 2;
+
+                    GetClientRect(es->hwndSelf, &rc);
+                    if (rc.right - rc.left < (width / 2 + width) * 2 &&
+                        (width >= 28 || !IsRectEmpty(&rc)) ) {
+                        default_left_margin = es->left_margin;
+                        default_right_margin = es->right_margin;
+                    }
+                } else {
+                    /* FIXME: figure out the CJK values. They are not affected by the client rect. */
+                    default_left_margin = width / 2;
+                    default_right_margin = width / 2;
                 }
             }
             SelectObject(dc, old_font);
diff --git a/dlls/user32/tests/edit.c b/dlls/user32/tests/edit.c
index e725590..b9aa1c9 100644
--- a/dlls/user32/tests/edit.c
+++ b/dlls/user32/tests/edit.c
@@ -1421,14 +1421,118 @@ static void test_edit_control_scroll(void)
     DestroyWindow (hwEdit);
 }
 
+static void test_margins_usefontinfo(UINT charset)
+{
+    HWND hwnd;
+    HDC hdc;
+    SIZE size;
+    BOOL cjk = FALSE;
+    LOGFONTA lf;
+    HFONT hfont;
+    RECT rect;
+    INT margins, threshold, expect, empty_expect, small_expect;
+
+    memset(&lf, 0, sizeof(lf));
+    lf.lfHeight = -11;
+    lf.lfWeight = FW_NORMAL;
+    lf.lfCharSet = charset;
+    strcpy(lf.lfFaceName, "Tahoma");
+
+    hfont = CreateFontIndirectA(&lf);
+    ok(hfont != NULL, "got %p\n", hfont);
+
+    /* Big window rectangle */
+    hwnd = CreateWindowExA(0, "Edit", "A", WS_POPUP, 0, 0, 5000, 1000, NULL, NULL, NULL, NULL);
+    ok(hwnd != NULL, "got %p\n", hwnd);
+    GetClientRect(hwnd, &rect);
+    ok(!IsRectEmpty(&rect), "got rect %s\n", wine_dbgstr_rect(&rect));
+
+    hdc = GetDC(hwnd);
+    hfont = SelectObject(hdc, hfont);
+    size.cx = GdiGetCharDimensions( hdc, NULL, &size.cy );
+    expect = MAKELONG(size.cx / 2, size.cx / 2);
+    small_expect = 0;
+    empty_expect = size.cx >= 28 ? small_expect : expect;
+
+    charset = GetTextCharset(hdc);
+    switch (charset)
+    {
+    case SHIFTJIS_CHARSET:
+    case HANGUL_CHARSET:
+    case GB2312_CHARSET:
+    case CHINESEBIG5_CHARSET:
+        cjk = TRUE;
+    }
+
+    hfont = SelectObject(hdc, hfont);
+    ReleaseDC(hwnd, hdc);
+
+    margins = SendMessageA(hwnd, EM_GETMARGINS, 0, 0);
+    ok(margins == 0, "got %x\n", margins);
+    SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, MAKELPARAM(TRUE, 0));
+    margins = SendMessageA(hwnd, EM_GETMARGINS, 0, 0);
+    if (!cjk)
+        ok(margins == expect, "%d: got %d, %d\n", charset, HIWORD(margins), LOWORD(margins));
+    else
+    {
+        ok(HIWORD(margins) > 0 && LOWORD(margins) > 0, "%d: got %d, %d\n", charset, HIWORD(margins), LOWORD(margins));
+        expect = empty_expect = small_expect = margins;
+    }
+    DestroyWindow(hwnd);
+
+    threshold = (size.cx / 2 + size.cx) * 2;
+
+    /* Size below which non-cjk margins are zero */
+    hwnd = CreateWindowExA(0, "Edit", "A", WS_POPUP, 0, 0, threshold - 1, 100, NULL, NULL, NULL, NULL);
+    ok(hwnd != NULL, "got %p\n", hwnd);
+    GetClientRect(hwnd, &rect);
+    ok(!IsRectEmpty(&rect), "got rect %s\n", wine_dbgstr_rect(&rect));
+
+    margins = SendMessageA(hwnd, EM_GETMARGINS, 0, 0);
+    ok(margins == 0, "got %x\n", margins);
+
+    SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, MAKELPARAM(TRUE, 0));
+    margins = SendMessageA(hwnd, EM_GETMARGINS, 0, 0);
+    ok(margins == small_expect, "%d: got %d, %d\n", charset, HIWORD(margins), LOWORD(margins));
+    DestroyWindow(hwnd);
+
+    /* Size at which non-cjk margins become non-zero */
+    hwnd = CreateWindowExA(0, "Edit", "A", WS_POPUP, 0, 0, threshold, 100, NULL, NULL, NULL, NULL);
+    ok(hwnd != NULL, "got %p\n", hwnd);
+    GetClientRect(hwnd, &rect);
+    ok(!IsRectEmpty(&rect), "got rect %s\n", wine_dbgstr_rect(&rect));
+
+    margins = SendMessageA(hwnd, EM_GETMARGINS, 0, 0);
+    ok(margins == 0, "got %x\n", margins);
+
+    SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, MAKELPARAM(TRUE, 0));
+    margins = SendMessageA(hwnd, EM_GETMARGINS, 0, 0);
+    ok(margins == expect, "%d: got %d, %d\n", charset, HIWORD(margins), LOWORD(margins));
+    DestroyWindow(hwnd);
+
+    /* Empty rect */
+    hwnd = CreateWindowExA(0, "Edit", "A", WS_POPUP, 0, 0, 0, 0, NULL, NULL, NULL, NULL);
+    ok(hwnd != NULL, "got %p\n", hwnd);
+    GetClientRect(hwnd, &rect);
+    ok(IsRectEmpty(&rect), "got rect %s\n", wine_dbgstr_rect(&rect));
+
+    margins = SendMessageA(hwnd, EM_GETMARGINS, 0, 0);
+    ok(margins == 0, "got %x\n", margins);
+
+    SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, MAKELPARAM(TRUE, 0));
+    margins = SendMessageA(hwnd, EM_GETMARGINS, 0, 0);
+    ok(margins == empty_expect, "%d: got %d, %d\n", charset, HIWORD(margins), LOWORD(margins));
+    DestroyWindow(hwnd);
+
+    DeleteObject(hfont);
+}
+
 static void test_margins(void)
 {
     HWND hwEdit;
     RECT old_rect, new_rect;
     INT old_right_margin;
     DWORD old_margins, new_margins;
-    LOGFONTA lf;
-    HFONT hfont;
 
     hwEdit = create_editcontrol(WS_BORDER | ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0);
 
@@ -1487,45 +1591,16 @@ static void test_margins(void)
 
     DestroyWindow (hwEdit);
 
-    memset(&lf, 0, sizeof(lf));
-    lf.lfHeight = -11;
-    lf.lfWeight = FW_NORMAL;
-    lf.lfCharSet = DEFAULT_CHARSET;
-    strcpy(lf.lfFaceName, "Tahoma");
-
-    hfont = CreateFontIndirectA(&lf);
-    ok(hfont != NULL, "got %p\n", hfont);
-
-    /* Empty window rectangle */
-    hwEdit = CreateWindowExA(0, "Edit", "A", WS_POPUP, 0, 0, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, NULL, NULL);
-    ok(hwEdit != NULL, "got %p\n", hwEdit);
-    GetClientRect(hwEdit, &old_rect);
-    ok(IsRectEmpty(&old_rect), "got rect %s\n", wine_dbgstr_rect(&old_rect));
-
-    old_margins = SendMessageA(hwEdit, EM_GETMARGINS, 0, 0);
-    ok(old_margins == 0, "got %x\n", old_margins);
-
-    SendMessageA(hwEdit, WM_SETFONT, (WPARAM)hfont, MAKELPARAM(TRUE, 0));
-    old_margins = SendMessageA(hwEdit, EM_GETMARGINS, 0, 0);
-    ok(HIWORD(old_margins) > 0 && LOWORD(old_margins) > 0, "got %d, %d\n", HIWORD(old_margins), LOWORD(old_margins));
-
-    DestroyWindow(hwEdit);
-
-    /* Size is not enough to display a text, but not empty */
-    hwEdit = CreateWindowExA(0, "Edit", "A", WS_POPUP, 0, 0, 2, 2, NULL, NULL, NULL, NULL);
-    ok(hwEdit != NULL, "got %p\n", hwEdit);
-    GetClientRect(hwEdit, &old_rect);
-    ok(!IsRectEmpty(&old_rect), "got rect %s\n", wine_dbgstr_rect(&old_rect));
+    test_margins_usefontinfo(ANSI_CHARSET);
+    test_margins_usefontinfo(EASTEUROPE_CHARSET);
 
-    old_margins = SendMessageA(hwEdit, EM_GETMARGINS, 0, 0);
-    ok(old_margins == 0, "got %x\n", old_margins);
+    test_margins_usefontinfo(SHIFTJIS_CHARSET);
+    test_margins_usefontinfo(HANGUL_CHARSET);
+    test_margins_usefontinfo(CHINESEBIG5_CHARSET);
+    /* Don't test JOHAB_CHARSET.  Treated as CJK by Win 8,
+       but not by < Win 8 and Win 10. */
 
-    SendMessageA(hwEdit, WM_SETFONT, (WPARAM)hfont, MAKELPARAM(TRUE, 0));
-    old_margins = SendMessageA(hwEdit, EM_GETMARGINS, 0, 0);
-    ok(old_margins == 0, "got %d, %d\n", HIWORD(old_margins), LOWORD(old_margins));
-
-    DeleteObject(hfont);
-    DestroyWindow(hwEdit);
+    test_margins_usefontinfo(DEFAULT_CHARSET);
 }
 
 static INT CALLBACK find_font_proc(const LOGFONTA *elf, const TEXTMETRICA *ntm, DWORD type, LPARAM lParam)
-- 
2.8.2




More information about the wine-patches mailing list