aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/ui/syntax/syntaxedit.go
blob: 5598d7a86cbbc8b5e3a9cb68545c16c5358a36bc (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
/* SPDX-License-Identifier: MIT
 *
 * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
 */

package syntax

import (
	"errors"
	"strings"
	"syscall"
	"unsafe"

	"github.com/lxn/walk"
	"github.com/lxn/win"
)

// #include "syntaxedit.h"
import "C"

type SyntaxEdit struct {
	walk.WidgetBase
	textChangedPublisher walk.EventPublisher
	privateKeyPublisher  walk.StringEventPublisher
}

func (se *SyntaxEdit) LayoutFlags() walk.LayoutFlags {
	return walk.GrowableHorz | walk.GrowableVert | walk.GreedyHorz | walk.GreedyVert
}

func (se *SyntaxEdit) MinSizeHint() walk.Size {
	return walk.Size{20, 12}
}

func (se *SyntaxEdit) SizeHint() walk.Size {
	return walk.Size{200, 100}
}

func (se *SyntaxEdit) Text() string {
	textLength := se.SendMessage(win.WM_GETTEXTLENGTH, 0, 0)
	buf := make([]uint16, textLength+1)
	se.SendMessage(win.WM_GETTEXT, uintptr(textLength+1), uintptr(unsafe.Pointer(&buf[0])))
	return strings.Replace(syscall.UTF16ToString(buf), "\r\n", "\n", -1)
}

func (se *SyntaxEdit) SetText(text string) (err error) {
	if text == se.Text() {
		return nil
	}
	text = strings.Replace(text, "\n", "\r\n", -1)
	if win.TRUE != se.SendMessage(win.WM_SETTEXT, 0, uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(text)))) {
		err = errors.New("WM_SETTEXT failed")
	}
	se.textChangedPublisher.Publish()
	return
}

func (se *SyntaxEdit) TextChanged() *walk.Event {
	return se.textChangedPublisher.Event()
}

func (se *SyntaxEdit) PrivateKeyChanged() *walk.StringEvent {
	return se.privateKeyPublisher.Event()
}

func (se *SyntaxEdit) WndProc(hwnd win.HWND, msg uint32, wParam, lParam uintptr) uintptr {
	switch msg {
	case win.WM_NOTIFY, win.WM_COMMAND:
		switch win.HIWORD(uint32(wParam)) {
		case win.EN_CHANGE:
			se.textChangedPublisher.Publish()
		}
		// This is a horrible trick from MFC where we reflect the event back to the child.
		se.SendMessage(msg+C.WM_REFLECT, wParam, lParam)
	case C.SE_PRIVATE_KEY:
		if lParam == 0 {
			se.privateKeyPublisher.Publish("")
		} else {
			se.privateKeyPublisher.Publish(C.GoString((*C.char)(unsafe.Pointer(lParam))))
		}
	}
	return se.WidgetBase.WndProc(hwnd, msg, wParam, lParam)
}

func NewSyntaxEdit(parent walk.Container) (*SyntaxEdit, error) {
	C.register_syntax_edit()
	se := &SyntaxEdit{}
	err := walk.InitWidget(
		se,
		parent,
		"WgQuickSyntaxEdit",
		C.SYNTAXEDIT_STYLE,
		C.SYNTAXEDIT_EXTSTYLE,
	)
	if err != nil {
		return nil, err
	}

	se.GraphicsEffects().Add(walk.InteractionEffect)
	se.GraphicsEffects().Add(walk.FocusEffect)
	se.MustRegisterProperty("Text", walk.NewProperty(
		func() interface{} {
			return se.Text()
		},
		func(v interface{}) error {
			if s, ok := v.(string); ok {
				return se.SetText(s)
			} else {
				return se.SetText("")
			}
		},
		se.textChangedPublisher.Event()))

	return se, nil
}