#include "BaseUtil.h" #include "EditCtrl.h" #include "BitManip.h" #include "WinUtil.h" // TODO: // - expose EN_UPDATE // (http://msdn.microsoft.com/en-us/library/windows/desktop/bb761687(v=vs.85).aspx) // - add border and possibly other decorations by handling WM_NCCALCSIZE, WM_NCPAINT and // WM_NCHITTEST // etc., http://www.catch22.net/tuts/insert-buttons-edit-control // - include value we remember in WM_NCCALCSIZE in GetIdealSize() static LRESULT CALLBACK EditParentProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) { UNUSED(uIdSubclass); EditCtrl *w = (EditCtrl *)dwRefData; CrashIf(GetParent(w->hwnd) != (HWND)lp); if ((WM_CTLCOLOREDIT == msg) && (w->bgBrush != nullptr)) { HDC hdc = (HDC)wp; // SetBkColor(hdc, w->bgCol); SetBkMode(hdc, TRANSPARENT); if (w->txtCol != NO_COLOR) { SetTextColor(hdc, w->txtCol); } return (INT_PTR)w->bgBrush; } if (w->onTextChanged && (WM_COMMAND == msg) && (EN_CHANGE == HIWORD(wp))) { w->onTextChanged(w); return 0; } // TODO: handle WM_CTLCOLORSTATIC for read-only/disabled controls return DefSubclassProc(hwnd, msg, wp, lp); } #if 0 static bool HasWsBorder(HWND hwnd) { DWORD style = GetWindowStyle(hwnd); return bit::IsMaskSet<DWORD>(style, WS_BORDER); } #endif static LRESULT CALLBACK EditProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) { UNUSED(uIdSubclass); EditCtrl *w = (EditCtrl *)dwRefData; CrashIf(w->hwnd != (HWND)lp); if (w->preFilter) { bool discard = false; auto res = w->preFilter(hwnd, msg, wp, lp, discard); if (discard) { return res; } } if (WM_NCDESTROY == msg) { RemoveWindowSubclass(GetParent(w->hwnd), EditParentProc, 0); RemoveWindowSubclass(w->hwnd, EditProc, 0); return DefSubclassProc(hwnd, msg, wp, lp); } // Node: this is sent during creation, which is too early for us (we didn't // subclass the window yet) // currently, we force it with SetWindowPos(... SMP_FRAMECHANGED) if (WM_NCCALCSIZE == msg) { NCCALCSIZE_PARAMS *p = (NCCALCSIZE_PARAMS *)lp; RECT orig = p->rgrc[0]; LRESULT res = DefSubclassProc(hwnd, msg, wp, lp); RECT curr = p->rgrc[0]; w->ncDx = RectDx(orig) - RectDx(curr); w->ncDy = RectDy(orig) - RectDy(curr); return res; } return DefSubclassProc(hwnd, msg, wp, lp); } void SetFont(EditCtrl *w, HFONT f) { SetWindowFont(w->hwnd, f, TRUE); } void SetColors(EditCtrl *w, COLORREF txtCol, COLORREF bgCol) { DeleteObject(w->bgBrush); w->bgBrush = nullptr; if (txtCol != NO_CHANGE) { w->txtCol = txtCol; } if (bgCol != NO_CHANGE) { w->bgCol = bgCol; } if (w->bgCol != NO_COLOR) { w->bgBrush = CreateSolidBrush(bgCol); } InvalidateRect(w->hwnd, nullptr, FALSE); } void SetText(EditCtrl *w, const WCHAR *s) { SetWindowTextW(w->hwnd, s); } bool SetCueText(EditCtrl *w, const WCHAR *s) { CrashIf(!w->hwnd); return Edit_SetCueBannerText(w->hwnd, s) == TRUE; } // caller must free() the result WCHAR *GetTextW(EditCtrl *w) { return win::GetText(w->hwnd); } // caller must free() the result char *GetText(EditCtrl *w) { ScopedMem<WCHAR> su(GetTextW(w)); return str::conv::ToUtf8(su.Get()); } EditCtrl *AllocEditCtrl(HWND parent, RECT *initialPosition) { auto w = AllocStruct<EditCtrl>(); w->parent = parent; if (initialPosition) { w->initialPos = *initialPosition; } else { SetRect(&w->initialPos, 0, 0, 120, 28); } w->dwExStyle = 0; w->dwStyle = WS_CHILD | WS_VISIBLE | ES_LEFT | ES_AUTOHSCROLL; w->txtCol = NO_COLOR; w->bgCol = NO_COLOR; w->bgBrush = nullptr; return w; } bool CreateEditCtrl(EditCtrl *w) { // Note: has to remember this here because when I GetWindowStyle() later on, // WS_BORDER is not set, which is a mystery, because it is being drawn. // also, WS_BORDER seems to be painted in client areay w->hasBorder = bit::IsMaskSet<DWORD>(w->dwStyle, WS_BORDER); RECT rc = w->initialPos; w->hwnd = CreateWindowExW(w->dwExStyle, WC_EDIT, L"", w->dwStyle, rc.left, rc.top, RectDx(rc), RectDy(rc), w->parent, nullptr, GetModuleHandleW(nullptr), nullptr); if (!w->hwnd) { return false; } SetFont(w, GetDefaultGuiFont()); SetWindowSubclass(w->hwnd, EditProc, 0, (DWORD_PTR)w); SetWindowSubclass(GetParent(w->hwnd), EditParentProc, 0, (DWORD_PTR)w); return true; } void DeleteEditCtrl(EditCtrl *w) { if (!w) return; DeleteObject(w->bgBrush); free(w); } SIZE GetIdealSize(EditCtrl *w) { // force sending WM_NCCALCSIZE SetWindowPos(w->hwnd, nullptr, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOZORDER | SWP_NOSIZE | SWP_NOMOVE); WCHAR *txt = GetTextW(w); if (str::Len(txt) == 0) { free(txt); txt = str::Dup(L"Sample"); } SizeI s = TextSizeInHwnd(w->hwnd, txt); free(txt); SIZE res; res.cx = s.dx + w->ncDx; res.cy = s.dy + w->ncDy; if (w->hasBorder) { res.cx += 4; res.cy += 4; } return res; } void SetPos(EditCtrl *w, RECT *r) { MoveWindow(w->hwnd, r); }