Skip to content

Commit 8fd3974

Browse files
committed
Add RichEdit widget and basic demo
1 parent aae4668 commit 8fd3974

File tree

8 files changed

+138
-2
lines changed

8 files changed

+138
-2
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ https://docs.microsoft.com/zh-cn/windows/win32/controls/window-controls
171171

172172
- [x] Spin Control
173173

174-
- [ ] Rich Edit
174+
- [x] Rich Edit
175175

176176
- [ ] DateTimePicker
177177
- [ ] Month Calendar

examples/basic/main.go

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ func bindWidgets(dlg *wingui.Dialog) {
7171
spin, _ := wingui.BindNewSpinControl(IDC_SPIN1, dlg)
7272
spinLabel, _ := wingui.BindNewStatic(IDC_SPIN_LABEL, dlg)
7373
spin.SetBuddy(spinEdit.Handle())
74-
spin.SetRange32(0, 100)
74+
spin.SetRange32(-1000, 1000)
7575
spin.SetPos32(0)
7676
updateSpinLabel := func() {
7777
spinLabel.SetText(fmt.Sprintf("Spin: %d", spin.GetPos32()))
@@ -332,6 +332,32 @@ func bindWidgets(dlg *wingui.Dialog) {
332332
updateLv2Status()
333333
}
334334
tabcontrol.AddDialogPage("Page2", tab2)
335+
336+
// RichEdit demo page
337+
richPage, err := wingui.NewDialog(IDD_DIALOG_RICH, tabcontrol.Handle(), nil)
338+
if err != nil {
339+
log.Println("rich page error", err)
340+
return
341+
}
342+
rich, _ := wingui.BindNewRichEdit(IDC_RICHEDIT1, richPage)
343+
richLabel, _ := wingui.BindNewStatic(IDC_RICH_LABEL_LEN, richPage)
344+
updateRichLabel := func() {
345+
richLabel.SetText(fmt.Sprintf("Len: %d", rich.TextLength()))
346+
}
347+
updateRichLabel()
348+
rich.OnChanged = updateRichLabel
349+
350+
appendBtn, _ := wingui.BindNewButton(IDC_RICH_APPEND, richPage)
351+
appendBtn.OnClicked = func() {
352+
rich.AppendText(time.Now().Format("15:04:05") + " hello rich edit\r\n")
353+
updateRichLabel()
354+
}
355+
clearBtn, _ := wingui.BindNewButton(IDC_RICH_CLEAR, richPage)
356+
clearBtn.OnClicked = func() {
357+
rich.SetText("")
358+
updateRichLabel()
359+
}
360+
tabcontrol.AddDialogPage("RichEdit", richPage)
335361
tabcontrol.Select(0)
336362
// other
337363

examples/basic/resource.h.go

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/basic/ui.syso

274 Bytes
Binary file not shown.

examples/basic/ui/resource.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#define IDB_BITMAP1 110
99
#define IDB_BITMAP2 111
1010
#define IDD_DIALOG_TAB1 113
11+
#define IDD_DIALOG_RICH 114
1112
#define IDB_OK 40000
1213
#define IDB_CANCEL 40001
1314
#define IDC_TAB_LIST1 40001
@@ -33,3 +34,7 @@
3334
#define IDC_SPIN_LABEL 40020
3435
#define IDC_SPIN_SET42 40021
3536
#define IDC_SPIN_RANGE 40022
37+
#define IDC_RICHEDIT1 40023
38+
#define IDC_RICH_APPEND 40024
39+
#define IDC_RICH_CLEAR 40025
40+
#define IDC_RICH_LABEL_LEN 40026

examples/basic/ui/ui.rc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,18 @@ FONT 8, "Ms Shell Dlg"
8282
LTEXT "Count: 0", IDC_TAB_LABEL_COUNT, 7, 76, 141, 9, SS_LEFT, WS_EX_LEFT
8383
}
8484

85+
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
86+
IDD_DIALOG_RICH DIALOG 0, 0, 153, 86
87+
STYLE DS_3DLOOK | DS_CENTER | DS_SHELLFONT | WS_VISIBLE | WS_CHILDWINDOW | WS_SYSMENU
88+
EXSTYLE WS_EX_CLIENTEDGE
89+
FONT 8, "Ms Shell Dlg"
90+
{
91+
CONTROL "", IDC_RICHEDIT1, "RICHEDIT50W", WS_TABSTOP | WS_BORDER | ES_MULTILINE | ES_AUTOVSCROLL | ES_WANTRETURN | WS_VSCROLL, 7, 7, 141, 50, WS_EX_LEFT
92+
PUSHBUTTON "Append", IDC_RICH_APPEND, 7, 60, 55, 14, 0, WS_EX_LEFT
93+
PUSHBUTTON "Clear", IDC_RICH_CLEAR, 66, 60, 55, 14, 0, WS_EX_LEFT
94+
LTEXT "Len: 0", IDC_RICH_LABEL_LEN, 7, 76, 141, 9, SS_LEFT, WS_EX_LEFT
95+
}
96+
8597

8698

8799
//

richedit.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package wingui
2+
3+
import (
4+
"syscall"
5+
"unsafe"
6+
7+
"github.com/lxn/win"
8+
)
9+
10+
// RichEdit is a wrapper for the Win32 Rich Edit control.
11+
// The class is provided by Msftedit.dll (RICHEDIT50W) or Riched20.dll (RichEdit20W).
12+
type RichEdit struct {
13+
WindowBase
14+
OnChanged func()
15+
}
16+
17+
func (re *RichEdit) WndProc(msg uint32, wParam, lParam uintptr) uintptr {
18+
switch msg {
19+
case win.WM_COMMAND:
20+
switch win.HIWORD(uint32(wParam)) {
21+
case win.EN_CHANGE:
22+
if re.OnChanged != nil && lParam == uintptr(re.hwnd) {
23+
re.OnChanged()
24+
}
25+
}
26+
}
27+
return re.AsWindowBase().WndProc(msg, wParam, lParam)
28+
}
29+
30+
// TextSelection returns the current selection range (character index).
31+
func (re *RichEdit) TextSelection() (start, end int) {
32+
re.SendMessage(win.EM_GETSEL, uintptr(unsafe.Pointer(&start)), uintptr(unsafe.Pointer(&end)))
33+
return
34+
}
35+
36+
// SetTextSelection sets the current selection range (character index).
37+
func (re *RichEdit) SetTextSelection(start, end int) {
38+
re.SendMessage(win.EM_SETSEL, uintptr(start), uintptr(end))
39+
}
40+
41+
// ReplaceSelectedText replaces the current selection with text.
42+
func (re *RichEdit) ReplaceSelectedText(text string, canUndo bool) {
43+
re.SendMessage(win.EM_REPLACESEL,
44+
uintptr(win.BoolToBOOL(canUndo)),
45+
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(text))),
46+
)
47+
}
48+
49+
// AppendText appends text to the end of the control.
50+
func (re *RichEdit) AppendText(value string) {
51+
s, e := re.TextSelection()
52+
l := re.TextLength()
53+
re.SetTextSelection(l, l)
54+
re.ReplaceSelectedText(value, false)
55+
re.SetTextSelection(s, e)
56+
}
57+
58+
// TextLength returns current text length.
59+
func (re *RichEdit) TextLength() int {
60+
return int(re.SendMessage(win.WM_GETTEXTLENGTH, 0, 0))
61+
}
62+
63+
// SetReadOnly toggles read-only mode.
64+
func (re *RichEdit) SetReadOnly(readOnly bool) {
65+
re.SendMessage(win.EM_SETREADONLY, uintptr(win.BoolToBOOL(readOnly)), 0)
66+
}
67+
68+
// NewRichEdit creates a new RichEdit, need bind to Dialog before use.
69+
func NewRichEdit(idd uintptr) *RichEdit {
70+
return &RichEdit{WindowBase: WindowBase{idd: idd}}
71+
}
72+
73+
// BindNewRichEdit creates a new RichEdit and bind to target dlg.
74+
func BindNewRichEdit(idd uintptr, dlg *Dialog) (*RichEdit, error) {
75+
re := NewRichEdit(idd)
76+
err := dlg.BindWidgets(re)
77+
return re, err
78+
}
79+

wingui.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ package wingui
4848

4949
import (
5050
"github.com/lxn/win"
51+
"golang.org/x/sys/windows"
5152
"log"
5253
"os"
5354
"runtime"
@@ -67,6 +68,9 @@ func init() {
6768
DwICC: win.ICC_WIN95_CLASSES,
6869
}
6970
win.InitCommonControlsEx(&icc)
71+
// Ensure Rich Edit classes are registered (RICHEDIT50W / RichEdit20W).
72+
_ = windows.NewLazySystemDLL("Msftedit.dll").Load()
73+
_ = windows.NewLazySystemDLL("Riched20.dll").Load()
7074
}
7175

7276
// InitHInstance init hInstance,used by Dialog APIs.

0 commit comments

Comments
 (0)