├── .gitignore ├── LICENSE ├── README.md ├── event ├── event.go └── type.go ├── go.mod └── tk ├── action.go ├── basewidget.go ├── basewidget_test.go ├── button.go ├── button_test.go ├── canvas.go ├── canvas_test.go ├── checkbox.go ├── checkbox_test.go ├── clipboard.go ├── combobox.go ├── combobox_test.go ├── command.go ├── dialog.go ├── entry.go ├── entry_test.go ├── error.go ├── event.go ├── font.go ├── font_test.go ├── frame.go ├── frame_test.go ├── grid.go ├── gridlayout.go ├── ids.go ├── image.go ├── interp ├── cbytes_go16_unix.go ├── cbytes_unix.go ├── interp.go ├── interp_test.go ├── interp_unix.go ├── interp_windows.go ├── syncmap.go ├── syncmap_18.go └── zinterp_windows.go ├── label.go ├── label_test.go ├── labelframe.go ├── labelframe_test.go ├── layout.go ├── listbox.go ├── listbox_test.go ├── menu.go ├── menu_test.go ├── menubutton.go ├── menubutton_test.go ├── misc.go ├── notebook.go ├── notebook_test.go ├── pack.go ├── packlayout.go ├── paned.go ├── paned_test.go ├── place.go ├── placeframe.go ├── progressbar.go ├── progressbar_test.go ├── radiobutton.go ├── radiobutton_group.go ├── radiobutton_test.go ├── rsrc_windows_386.syso ├── rsrc_windows_amd64.syso ├── scale.go ├── scale_test.go ├── scrollbar.go ├── scrollbar_test.go ├── scrolllayout.go ├── separator.go ├── separator_test.go ├── spinbox.go ├── spinbox_test.go ├── text.go ├── text_test.go ├── theme.go ├── theme_ttk.go ├── tk.go ├── tk.manifest ├── tk_rsrc.go ├── tk_test.go ├── treeitem.go ├── treeview.go ├── treeview_test.go ├── utils.go ├── widget.go ├── widget_attr.go ├── widget_meta.go ├── widget_type.go ├── window.go ├── window_darwin.go ├── window_linux.go └── window_windows.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.dll 4 | *.so 5 | *.dylib 6 | 7 | # macOS 8 | .DS_Store 9 | 10 | # Test binary, build with `go test -c` 11 | *.test 12 | 13 | # Output of the go coverage tool, specifically when used with LiteIDE 14 | *.out 15 | 16 | # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 17 | .glide/ 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # atk 2 | Another Golang Tcl/Tk binding GUI ToolKit 3 | 4 | go get github.com/visualfc/atk 5 | 6 | Go docs: https://pkg.go.dev/github.com/visualfc/atk/tk 7 | 8 | ### Install Tcl/Tk 9 | 10 | http://www.tcl-lang.org 11 | 12 | 13 | * MacOS X 14 | 15 | https://www.activestate.com/activetcl/downloads 16 | 17 | * Windows 18 | 19 | https://www.activestate.com/activetcl/downloads 20 | 21 | https://github.com/visualfc/tcltk_mingw 22 | 23 | * Ubuntu 24 | 25 | $ sudo apt install tk-dev 26 | 27 | * CentOS 28 | 29 | $ sudo yum install tk-devel 30 | 31 | ### Demo 32 | 33 | https://github.com/visualfc/atk_demo 34 | 35 | ### Sample 36 | ```go 37 | package main 38 | 39 | import ( 40 | "github.com/visualfc/atk/tk" 41 | ) 42 | 43 | type Window struct { 44 | *tk.Window 45 | } 46 | 47 | func NewWindow() *Window { 48 | mw := &Window{tk.RootWindow()} 49 | lbl := tk.NewLabel(mw, "Hello ATK") 50 | btn := tk.NewButton(mw, "Quit") 51 | btn.OnCommand(func() { 52 | tk.Quit() 53 | }) 54 | tk.NewVPackLayout(mw).AddWidgets(lbl, tk.NewLayoutSpacer(mw, 0, true), btn) 55 | mw.ResizeN(300, 200) 56 | return mw 57 | } 58 | 59 | func main() { 60 | tk.MainLoop(func() { 61 | mw := NewWindow() 62 | mw.SetTitle("ATK Sample") 63 | mw.Center(nil) 64 | mw.ShowNormal() 65 | }) 66 | } 67 | ``` 68 | -------------------------------------------------------------------------------- /event/type.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package event 4 | 5 | const ( 6 | TypeKeyPress = 2 7 | TypeKeyRelease = 3 8 | TypeButtonPress = 4 9 | TypeButtonRelease = 5 10 | TypeMotionNotify = 6 11 | TypeEnterNotify = 7 12 | TypeLeaveNotify = 8 13 | TypeFocusIn = 9 14 | TypeFocusOut = 10 15 | TypeKeymapNotify = 11 16 | TypeExpose = 12 17 | TypeGraphicsExpose = 13 18 | TypeNoExpose = 14 19 | TypeVisibilityNotify = 15 20 | TypeCreateNotify = 16 21 | TypeDestroyNotify = 17 22 | TypeUnmapNotify = 18 23 | TypeMapNotify = 19 24 | TypeMapRequest = 20 25 | TypeReparentNotify = 21 26 | TypeConfigureNotify = 22 27 | TypeConfigureRequest = 23 28 | TypeGravityNotify = 24 29 | TypeResizeRequest = 25 30 | TypeCirculateNotify = 26 31 | TypeCirculateRequest = 27 32 | TypePropertyNotify = 28 33 | TypeSelectionClear = 29 34 | TypeSelectionRequest = 30 35 | TypeSelectionNotify = 31 36 | TypeColormapNotify = 32 37 | TypeClientMessage = 33 38 | TypeMappingNotify = 34 39 | LASTEvent = 35 40 | ) 41 | 42 | const ( 43 | TypeVirtualEvent = (TypeMappingNotify + 1) 44 | TypeActivateNotify = (TypeMappingNotify + 2) 45 | TypeDeactivateNotify = (TypeMappingNotify + 3) 46 | TypeMouseWheelEvent = (TypeMappingNotify + 4) 47 | TK_LASTEVENT = (TypeMappingNotify + 5) 48 | ) 49 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/visualfc/atk 2 | 3 | go 1.14 4 | -------------------------------------------------------------------------------- /tk/action.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import ( 6 | "fmt" 7 | ) 8 | 9 | type Action struct { 10 | actid string 11 | label string 12 | checkid string 13 | groupid string 14 | radioid string 15 | command *Command 16 | data interface{} 17 | } 18 | 19 | func (a *Action) String() string { 20 | if a.actid == "" { 21 | return "Separator" 22 | } else if a.groupid != "" { 23 | return fmt.Sprintf("RadioAction{%v}", a.label) 24 | } else if a.checkid != "" { 25 | return fmt.Sprintf("CheckAction{%v}", a.label) 26 | } else { 27 | return fmt.Sprintf("Action{%v}", a.label) 28 | } 29 | } 30 | 31 | func (a *Action) IsSeparator() bool { 32 | return a.actid == "" 33 | } 34 | 35 | func (a *Action) IsRadioAction() bool { 36 | return a.groupid != "" 37 | } 38 | 39 | func (a *Action) IsCheckAction() bool { 40 | return a.checkid != "" 41 | } 42 | 43 | func (a *Action) SetChecked(b bool) { 44 | if a.groupid != "" { 45 | eval(fmt.Sprintf("set %v {%v}", a.groupid, a.radioid)) 46 | } else if a.checkid != "" { 47 | eval(fmt.Sprintf("set %v {%v}", a.checkid, boolToInt(b))) 48 | } 49 | } 50 | 51 | func (a *Action) IsChecked() bool { 52 | if a.groupid != "" { 53 | r, _ := evalAsString(fmt.Sprintf("set %v", a.groupid)) 54 | return r == a.radioid 55 | } else if a.checkid != "" { 56 | b, _ := evalAsBool(fmt.Sprintf("set %v", a.checkid)) 57 | return b 58 | } 59 | return false 60 | } 61 | 62 | func (a *Action) Label() string { 63 | return a.label 64 | } 65 | 66 | func (a *Action) SetData(data interface{}) { 67 | a.data = data 68 | } 69 | 70 | func (a *Action) Data() interface{} { 71 | return a.data 72 | } 73 | 74 | func (a *Action) Invoke() { 75 | a.command.Invoke() 76 | } 77 | 78 | func (a *Action) OnCommand(fn func()) error { 79 | if fn == nil { 80 | return ErrInvalid 81 | } 82 | a.command.Bind(fn) 83 | return nil 84 | } 85 | 86 | func NewAction(label string) *Action { 87 | act := &Action{} 88 | act.label = label 89 | act.actid = makeActionId() 90 | act.command = &Command{} 91 | mainInterp.CreateAction(act.actid, func([]string) { 92 | act.command.Invoke() 93 | }) 94 | return act 95 | } 96 | 97 | func NewActionEx(label string, cmd func()) *Action { 98 | act := NewAction(label) 99 | act.OnCommand(cmd) 100 | return act 101 | } 102 | 103 | func NewCheckAction(label string) *Action { 104 | act := NewAction(label) 105 | id := makeNamedId("atk_checkaction") 106 | evalSetValue(id, "0") 107 | act.checkid = id 108 | return act 109 | } 110 | 111 | func NewCheckActionEx(label string, cmd func()) *Action { 112 | act := NewCheckAction(label) 113 | act.OnCommand(cmd) 114 | return act 115 | } 116 | 117 | func NewRadioAction(group *ActionGroup, label string) *Action { 118 | return group.AddNewRadioAction(label) 119 | } 120 | 121 | func NewSeparatorAction() *Action { 122 | action := &Action{} 123 | return action 124 | } 125 | 126 | type ActionGroup struct { 127 | groupid string 128 | actions []*Action 129 | fnRadioCommand func() 130 | } 131 | 132 | func NewActionGroup() *ActionGroup { 133 | id := makeNamedId("atk_actiongroup") 134 | evalSetValue(id, "") 135 | return &ActionGroup{id, nil, nil} 136 | } 137 | 138 | func (a *ActionGroup) findAction(act *Action) bool { 139 | for _, v := range a.actions { 140 | if v == act { 141 | return true 142 | } 143 | } 144 | return false 145 | } 146 | 147 | func (a *ActionGroup) radioCommand() { 148 | if a.fnRadioCommand != nil { 149 | a.fnRadioCommand() 150 | } 151 | } 152 | 153 | func (a *ActionGroup) AddRadioAction(act *Action) error { 154 | if a.findAction(act) { 155 | return ErrExist 156 | } 157 | act.groupid = a.groupid 158 | act.radioid = makeNamedId("atk_radioaction") 159 | act.OnCommand(a.radioCommand) 160 | a.actions = append(a.actions, act) 161 | return nil 162 | } 163 | 164 | func (a *ActionGroup) AddNewRadioAction(label string) *Action { 165 | act := NewAction(label) 166 | a.AddRadioAction(act) 167 | return act 168 | } 169 | 170 | func (a *ActionGroup) OnCommand(fn func()) { 171 | a.fnRadioCommand = fn 172 | } 173 | 174 | func (a *ActionGroup) checkedValue() string { 175 | r, _ := evalAsStringEx(fmt.Sprintf("set %v", a.groupid), false) 176 | return r 177 | } 178 | 179 | func (a *ActionGroup) SetCheckedIndex(index int) error { 180 | if index < 0 || index > len(a.actions) { 181 | return ErrInvalid 182 | } 183 | a.actions[index].SetChecked(true) 184 | return nil 185 | } 186 | 187 | func (a *ActionGroup) SetCheckedAction(act *Action) error { 188 | if act == nil { 189 | return ErrInvalid 190 | } 191 | for _, v := range a.actions { 192 | if v == act { 193 | act.SetChecked(true) 194 | return nil 195 | } 196 | } 197 | return ErrNotExist 198 | } 199 | 200 | func (a *ActionGroup) CheckedActionIndex() int { 201 | s := a.checkedValue() 202 | if s == "" { 203 | return -1 204 | } 205 | for n, act := range a.actions { 206 | if act.radioid == s { 207 | return n 208 | } 209 | } 210 | return -1 211 | } 212 | 213 | func (a *ActionGroup) CheckedAction() *Action { 214 | s := a.checkedValue() 215 | if s == "" { 216 | return nil 217 | } 218 | for _, act := range a.actions { 219 | if act.radioid == s { 220 | return act 221 | } 222 | } 223 | return nil 224 | } 225 | 226 | func (a *ActionGroup) Actions() []*Action { 227 | return a.actions 228 | } 229 | -------------------------------------------------------------------------------- /tk/basewidget.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import ( 6 | "fmt" 7 | "strings" 8 | ) 9 | 10 | var _ Widget = &BaseWidget{} 11 | 12 | type BaseWidget struct { 13 | id string 14 | info *WidgetInfo 15 | } 16 | 17 | func (w *BaseWidget) String() string { 18 | iw := globalWidgetMap[w.id] 19 | if iw != nil { 20 | return fmt.Sprintf("%v{%v}", iw.TypeName(), w.id) 21 | } else { 22 | return fmt.Sprintf("Invalid{%v}", w.id) 23 | } 24 | } 25 | 26 | func (w *BaseWidget) Id() string { 27 | return w.id 28 | } 29 | 30 | func (w *BaseWidget) Info() *WidgetInfo { 31 | return w.info 32 | } 33 | 34 | func (w *BaseWidget) Type() WidgetType { 35 | if w.info != nil { 36 | return w.info.Type 37 | } 38 | return WidgetTypeNone 39 | } 40 | 41 | func (w *BaseWidget) TypeName() string { 42 | if w.info != nil { 43 | return w.info.TypeName 44 | } 45 | return "Invalid" 46 | } 47 | 48 | func (w *BaseWidget) Parent() Widget { 49 | return ParentOfWidget(w) 50 | } 51 | 52 | func (w *BaseWidget) Children() []Widget { 53 | return ChildrenOfWidget(w) 54 | } 55 | 56 | func (w *BaseWidget) IsValid() bool { 57 | return IsValidWidget(w) 58 | } 59 | 60 | func (w *BaseWidget) Destroy() error { 61 | return DestroyWidget(w) 62 | } 63 | 64 | func (w *BaseWidget) DestroyChildren() error { 65 | if !IsValidWidget(w) { 66 | return ErrInvalid 67 | } 68 | for _, child := range w.Children() { 69 | DestroyWidget(child) 70 | } 71 | return nil 72 | } 73 | 74 | func (w *BaseWidget) NativeAttribute(key string) string { 75 | if !IsValidWidget(w) { 76 | return "" 77 | } 78 | if !w.info.MetaClass.HasAttribute(key) { 79 | return "" 80 | } 81 | r, _ := evalAsString(fmt.Sprintf("%v cget -%v", w.id, key)) 82 | return r 83 | } 84 | 85 | func (w *BaseWidget) NativeAttributes(keys ...string) (attributes []NativeAttr) { 86 | if !IsValidWidget(w) { 87 | return nil 88 | } 89 | if keys == nil { 90 | for _, key := range w.info.MetaClass.Attributes { 91 | r, _ := evalAsString(fmt.Sprintf("%v cget -%v", w.id, key)) 92 | attributes = append(attributes, NativeAttr{key, r}) 93 | } 94 | } else { 95 | for _, key := range keys { 96 | if w.info.MetaClass.HasAttribute(key) { 97 | r, _ := evalAsString(fmt.Sprintf("%v cget -%v", w.id, key)) 98 | attributes = append(attributes, NativeAttr{key, r}) 99 | } 100 | } 101 | } 102 | return 103 | } 104 | 105 | func (w *BaseWidget) SetNativeAttribute(key string, value string) error { 106 | return w.SetNativeAttributes([]NativeAttr{NativeAttr{key, value}}...) 107 | } 108 | 109 | func (w *BaseWidget) SetNativeAttributes(attributes ...NativeAttr) error { 110 | if !IsValidWidget(w) { 111 | return ErrInvalid 112 | } 113 | var attrList []string 114 | for _, attr := range attributes { 115 | if !w.info.MetaClass.HasAttribute(attr.Key) { 116 | continue 117 | } 118 | pname := "atk_tmp_" + attr.Key 119 | setObjText(pname, attr.Value) 120 | attrList = append(attrList, fmt.Sprintf("-%v $%v", attr.Key, pname)) 121 | } 122 | if len(attrList) > 0 { 123 | return eval(fmt.Sprintf("%v configure %v", w.id, strings.Join(attrList, " "))) 124 | } 125 | return nil 126 | } 127 | 128 | func (w *BaseWidget) SetAttributes(attributes ...*WidgetAttr) error { 129 | if !IsValidWidget(w) { 130 | return ErrInvalid 131 | } 132 | extra := buildWidgetAttributeScript(w.info.MetaClass, w.info.IsTtk, attributes) 133 | if len(extra) > 0 { 134 | return eval(fmt.Sprintf("%v configure %v", w.id, extra)) 135 | } 136 | return nil 137 | } 138 | 139 | func (w *BaseWidget) BindEvent(event string, fn func(e *Event)) error { 140 | return BindEvent(w.id, event, fn) 141 | } 142 | 143 | func (w *BaseWidget) BindKeyEvent(fn func(e *KeyEvent)) error { 144 | return BindKeyEventEx(w.id, fn, nil) 145 | } 146 | 147 | func (w *BaseWidget) BindKeyEventEx(fnPress func(e *KeyEvent), fnRelease func(e *KeyEvent)) error { 148 | return BindKeyEventEx(w.id, fnPress, fnRelease) 149 | } 150 | 151 | func (w *BaseWidget) BindInfo() []string { 152 | return BindInfo(w.id) 153 | } 154 | 155 | func (w *BaseWidget) ClearBind(event string) error { 156 | return ClearBindEvent(w.id, event) 157 | } 158 | 159 | func (w *BaseWidget) Lower(below Widget) error { 160 | script := fmt.Sprintf("lower %v", w.id) 161 | if IsValidWidget(below) { 162 | script += " " + below.Id() 163 | } 164 | return eval(script) 165 | } 166 | 167 | func (w *BaseWidget) Raise(above Widget) error { 168 | script := fmt.Sprintf("raise %v", w.id) 169 | if IsValidWidget(above) { 170 | script += " " + above.Id() 171 | } 172 | return eval(script) 173 | } 174 | 175 | func (w *BaseWidget) SetGrab() error { 176 | return eval(fmt.Sprintf("grab set %v", w.id)) 177 | } 178 | 179 | func (w *BaseWidget) ReleaseGrab() error { 180 | return eval(fmt.Sprintf("grab release %v", w.id)) 181 | } 182 | 183 | func (w *BaseWidget) IsGrab() bool { 184 | id, err := evalAsString("grab current") 185 | if err != nil || id == "" { 186 | return false 187 | } 188 | return w.id == id 189 | } 190 | 191 | func (w *BaseWidget) SetFocus() error { 192 | return eval(fmt.Sprintf("focus %v", w.id)) 193 | } 194 | 195 | func (w *BaseWidget) IsFocus() bool { 196 | id, err := evalAsString("focus") 197 | if err != nil || id == "" { 198 | return false 199 | } 200 | return w.id == id 201 | } 202 | 203 | func (w *BaseWidget) FocusNextWidget() Widget { 204 | id, err := evalAsString("tk_focusNext " + w.id) 205 | if err != nil || id == "" { 206 | return nil 207 | } 208 | return FindWidget(id) 209 | } 210 | 211 | func (w *BaseWidget) FocusPrevWidget() Widget { 212 | id, err := evalAsString("tk_focusPrev " + w.id) 213 | if err != nil || id == "" { 214 | return nil 215 | } 216 | return FindWidget(id) 217 | } 218 | 219 | func SetFocusFollowsMouse() error { 220 | return eval("tk_focusFollowsMouse") 221 | } 222 | 223 | func FocusWidget() Widget { 224 | id, err := evalAsString("focus") 225 | if err != nil || id == "" { 226 | return nil 227 | } 228 | return FindWidget(id) 229 | } 230 | 231 | func GrabWidget() Widget { 232 | id, err := evalAsString("grab current") 233 | if err != nil || id == "" { 234 | return nil 235 | } 236 | return FindWidget(id) 237 | } 238 | -------------------------------------------------------------------------------- /tk/basewidget_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import ( 6 | "strings" 7 | "testing" 8 | ) 9 | 10 | func init() { 11 | registerTest("TestWidgetId", testWidgetId) 12 | registerTest("TestWidgetParent", testWidgetParent) 13 | } 14 | 15 | type TestWidget struct { 16 | BaseWidget 17 | } 18 | 19 | func (w *TestWidget) Type() WidgetType { 20 | return WidgetTypeLast + 1 21 | } 22 | 23 | func (w *TestWidget) Attach(id string) error { 24 | w.id = id 25 | w.info = &WidgetInfo{WidgetTypeCanvas, "TestWidget", false, nil} 26 | RegisterWidget(w) 27 | return nil 28 | } 29 | 30 | func NewTestWidget(parent Widget, id string) *TestWidget { 31 | w := &TestWidget{} 32 | w.Attach(makeNamedWidgetId(parent, id)) 33 | return w 34 | } 35 | 36 | func testWidgetId(t *testing.T) { 37 | var id string 38 | parent := NewTestWidget(nil, "base") 39 | id = makeNamedWidgetId(nil, "atk_wtest") 40 | if !strings.HasPrefix(id, ".atk_wtest") { 41 | t.Fatal(id) 42 | } 43 | id = makeNamedWidgetId(parent, "atk_wchild") 44 | if !strings.HasPrefix(id, ".base1.atk_wchild1") { 45 | t.Fatal(id) 46 | } 47 | id = makeNamedWidgetId(nil, "idtest") 48 | if id != ".idtest1" { 49 | t.Fatal(id) 50 | } 51 | id = makeNamedWidgetId(parent, "idtest") 52 | if id != ".base1.idtest1" { 53 | t.Fatal(id) 54 | } 55 | id = makeNamedWidgetId(parent, "idtest") 56 | if id != ".base1.idtest2" { 57 | t.Fatal(id) 58 | } 59 | DestroyWidget(parent) 60 | } 61 | 62 | func findOfList(w Widget, list []Widget) bool { 63 | for _, v := range list { 64 | if v == w { 65 | return true 66 | } 67 | } 68 | return false 69 | } 70 | 71 | func testWidgetParent(t *testing.T) { 72 | a1 := NewTestWidget(nil, "a1") 73 | a2 := NewTestWidget(nil, "a2") 74 | defer a1.Destroy() 75 | defer a2.Destroy() 76 | a1_b1 := NewTestWidget(a1, "b1") 77 | a1_b1_c1 := NewTestWidget(a1_b1, "c1") 78 | a1_b1_c2 := NewTestWidget(a1_b1, "c2") 79 | a1_b1_c3 := NewTestWidget(a1_b1, "c3") 80 | a2_b1 := NewTestWidget(a2, "b1") 81 | a2_b1_c1 := NewTestWidget(a2_b1, "c1") 82 | a2_b1_c2 := NewTestWidget(a2_b1, "c2") 83 | a2_b1_c3 := NewTestWidget(a2_b1, "c3") 84 | 85 | if p := ParentOfWidget(a1); p != RootWindow() { 86 | t.Fatal("ParentWidget", p) 87 | } 88 | if p := ParentOfWidget(a1_b1); p != a1 { 89 | t.Fatal("ParentWidget", p) 90 | } 91 | if p := ParentOfWidget(a1_b1_c1); p != a1_b1 { 92 | t.Fatal("ParentWidget", p) 93 | } 94 | list := ChildrenOfWidget(rootWindow) 95 | if !findOfList(a1, list) || !findOfList(a2, list) { 96 | t.Fatal("ChildrenOfWidget", list) 97 | } 98 | list = ChildrenOfWidget(a1_b1) 99 | if len(list) != 3 || !findOfList(a1_b1_c1, list) || !findOfList(a1_b1_c2, list) || !findOfList(a1_b1_c3, list) { 100 | t.Fatal("ChildrenOfWidget", list) 101 | } 102 | DestroyWidget(a1_b1_c3) 103 | list = ChildrenOfWidget(a1_b1) 104 | if len(list) != 2 { 105 | t.Fatal("DestroyWidget", list) 106 | } 107 | DestroyWidget(a1) 108 | list = ChildrenOfWidget(rootWindow) 109 | if findOfList(a1, list) { 110 | t.Fatal("DestroyWidget", list) 111 | } 112 | if IsValidWidget(a1_b1_c1) { 113 | t.Fatal("IsValidWidget", a1_b1_c1) 114 | } 115 | 116 | list = a2_b1.Children() 117 | if len(list) != 3 { 118 | t.Fatal("Children", list) 119 | } 120 | a2_b1_c3.Destroy() 121 | if a2_b1_c1.Parent() != a2_b1 || a2_b1_c2.Parent() != a2_b1 { 122 | t.Fatal("Destroy") 123 | } 124 | a2_b1.DestroyChildren() 125 | if a2_b1_c2.IsValid() { 126 | t.Fatal("DestroyChildren", a2_b1_c2) 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /tk/button.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import "fmt" 6 | 7 | // button 8 | type Button struct { 9 | BaseWidget 10 | command *Command 11 | } 12 | 13 | func NewButton(parent Widget, text string, attributes ...*WidgetAttr) *Button { 14 | theme := checkInitUseTheme(attributes) 15 | iid := makeNamedWidgetId(parent, "atk_button") 16 | attributes = append(attributes, &WidgetAttr{"text", text}) 17 | info := CreateWidgetInfo(iid, WidgetTypeButton, theme, attributes) 18 | if info == nil { 19 | return nil 20 | } 21 | w := &Button{} 22 | w.id = iid 23 | w.info = info 24 | RegisterWidget(w) 25 | return w 26 | } 27 | 28 | func (w *Button) Attach(id string) error { 29 | info, err := CheckWidgetInfo(id, WidgetTypeButton) 30 | if err != nil { 31 | return err 32 | } 33 | w.id = id 34 | w.info = info 35 | RegisterWidget(w) 36 | return nil 37 | } 38 | 39 | func (w *Button) SetText(text string) error { 40 | setObjText("atk_tmp_text", text) 41 | return eval(fmt.Sprintf("%v configure -text $atk_tmp_text", w.id)) 42 | } 43 | 44 | func (w *Button) Text() string { 45 | r, _ := evalAsString(fmt.Sprintf("%v cget -text", w.id)) 46 | return r 47 | } 48 | 49 | func (w *Button) SetWidth(width int) error { 50 | return eval(fmt.Sprintf("%v configure -width {%v}", w.id, width)) 51 | } 52 | 53 | func (w *Button) Width() int { 54 | r, _ := evalAsInt(fmt.Sprintf("%v cget -width", w.id)) 55 | return r 56 | } 57 | 58 | func (w *Button) SetImage(image *Image) error { 59 | if image == nil { 60 | return ErrInvalid 61 | } 62 | return eval(fmt.Sprintf("%v configure -image {%v}", w.id, image.Id())) 63 | } 64 | 65 | func (w *Button) Image() *Image { 66 | r, err := evalAsString(fmt.Sprintf("%v cget -image", w.id)) 67 | return parserImageResult(r, err) 68 | } 69 | 70 | func (w *Button) SetCompound(compound Compound) error { 71 | return eval(fmt.Sprintf("%v configure -compound {%v}", w.id, compound)) 72 | } 73 | 74 | func (w *Button) Compound() Compound { 75 | r, err := evalAsString(fmt.Sprintf("%v cget -compound", w.id)) 76 | return parserCompoundResult(r, err) 77 | } 78 | 79 | func (w *Button) SetPaddingN(padx int, pady int) error { 80 | if w.info.IsTtk { 81 | return eval(fmt.Sprintf("%v configure -padding {%v %v}", w.id, padx, pady)) 82 | } 83 | return eval(fmt.Sprintf("%v configure -padx {%v} -pady {%v}", w.id, padx, pady)) 84 | } 85 | 86 | func (w *Button) PaddingN() (int, int) { 87 | var r string 88 | var err error 89 | if w.info.IsTtk { 90 | r, err = evalAsString(fmt.Sprintf("%v cget -padding", w.id)) 91 | } else { 92 | r1, _ := evalAsString(fmt.Sprintf("%v cget -padx", w.id)) 93 | r2, _ := evalAsString(fmt.Sprintf("%v cget -pady", w.id)) 94 | r = r1 + " " + r2 95 | } 96 | return parserPaddingResult(r, err) 97 | } 98 | 99 | func (w *Button) SetPadding(pad Pad) error { 100 | return w.SetPaddingN(pad.X, pad.Y) 101 | } 102 | 103 | func (w *Button) Padding() Pad { 104 | x, y := w.PaddingN() 105 | return Pad{x, y} 106 | } 107 | 108 | func (w *Button) SetState(state State) error { 109 | return eval(fmt.Sprintf("%v configure -state {%v}", w.id, state)) 110 | } 111 | 112 | func (w *Button) State() State { 113 | r, err := evalAsString(fmt.Sprintf("%v cget -state", w.id)) 114 | return parserStateResult(r, err) 115 | } 116 | 117 | func (w *Button) SetTakeFocus(takefocus bool) error { 118 | return eval(fmt.Sprintf("%v configure -takefocus {%v}", w.id, boolToInt(takefocus))) 119 | } 120 | 121 | func (w *Button) IsTakeFocus() bool { 122 | r, _ := evalAsBool(fmt.Sprintf("%v cget -takefocus", w.id)) 123 | return r 124 | } 125 | 126 | func (w *Button) OnCommand(fn func()) error { 127 | if fn == nil { 128 | return ErrInvalid 129 | } 130 | if w.command == nil { 131 | w.command = &Command{} 132 | bindCommand(w.id, "command", w.command.Invoke) 133 | } 134 | w.command.Bind(fn) 135 | return nil 136 | } 137 | 138 | func (w *Button) Invoke() { 139 | eval(fmt.Sprintf("%v invoke", w.id)) 140 | } 141 | 142 | func ButtonAttrText(text string) *WidgetAttr { 143 | return &WidgetAttr{"text", text} 144 | } 145 | 146 | func ButtonAttrWidth(width int) *WidgetAttr { 147 | return &WidgetAttr{"width", width} 148 | } 149 | 150 | func ButtonAttrImage(image *Image) *WidgetAttr { 151 | if image == nil { 152 | return nil 153 | } 154 | return &WidgetAttr{"image", image.Id()} 155 | } 156 | 157 | func ButtonAttrCompound(compound Compound) *WidgetAttr { 158 | return &WidgetAttr{"compound", compound} 159 | } 160 | 161 | func ButtonAttrPadding(padding Pad) *WidgetAttr { 162 | return &WidgetAttr{"padding", padding} 163 | } 164 | 165 | func ButtonAttrState(state State) *WidgetAttr { 166 | return &WidgetAttr{"state", state} 167 | } 168 | 169 | func ButtonAttrTakeFocus(takefocus bool) *WidgetAttr { 170 | return &WidgetAttr{"takefocus", boolToInt(takefocus)} 171 | } 172 | -------------------------------------------------------------------------------- /tk/button_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import "testing" 6 | 7 | func init() { 8 | registerTest("Button", testButton) 9 | } 10 | 11 | func testButton(t *testing.T) { 12 | w := NewButton(nil, "test", ButtonAttrText("text"), ButtonAttrWidth(20), ButtonAttrCompound(CompoundNone), ButtonAttrState(StateNormal), ButtonAttrTakeFocus(true)) 13 | defer w.Destroy() 14 | 15 | w.SetText("text") 16 | if v := w.Text(); v != "text" { 17 | t.Fatal("Text", "text", v) 18 | } 19 | 20 | w.SetWidth(20) 21 | if v := w.Width(); v != 20 { 22 | t.Fatal("Width", 20, v) 23 | } 24 | 25 | w.SetCompound(CompoundNone) 26 | if v := w.Compound(); v != CompoundNone { 27 | t.Fatal("Compound", CompoundNone, v) 28 | } 29 | 30 | w.SetState(StateNormal) 31 | if v := w.State(); v != StateNormal { 32 | t.Fatal("State", StateNormal, v) 33 | } 34 | 35 | w.SetTakeFocus(true) 36 | if v := w.IsTakeFocus(); v != true { 37 | t.Fatal("IsTakeFocus", true, v) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /tk/canvas_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import "testing" 6 | 7 | func init() { 8 | registerTest("Canvas", testCanvas) 9 | } 10 | 11 | func testCanvas(t *testing.T) { 12 | w := NewCanvas(nil, CanvasAttrBackground("blue"), CanvasAttrBorderWidth(20), CanvasAttrHighlightBackground("blue"), CanvasAttrHighlightColor("blue"), CanvasAttrHighlightthickness(20), CanvasAttrInsertBackground("blue"), CanvasAttrInsertBorderWidth(20), CanvasAttrInsertOffTime(20), CanvasAttrInsertOnTime(20), CanvasAttrInsertWidth(20), CanvasAttrReliefStyle(1), CanvasAttrSelectBackground("blue"), CanvasAttrSelectborderwidth(20), CanvasAttrSelectforeground("blue"), CanvasAttrTakeFocus(true), CanvasAttrCloseEnough(0.5), CanvasAttrConfine(true), CanvasAttrWidth(20), CanvasAttrHeight(20), CanvasAttrState(StateNormal), CanvasAttrXScrollIncrement(20), CanvasAttrYScrollIncrement(20)) 13 | defer w.Destroy() 14 | 15 | w.SetBackground("blue") 16 | if v := w.Background(); v != "blue" { 17 | t.Fatal("Background", "blue", v) 18 | } 19 | 20 | w.SetBorderWidth(20) 21 | if v := w.BorderWidth(); v != 20 { 22 | t.Fatal("BorderWidth", 20, v) 23 | } 24 | 25 | w.SetHighlightBackground("blue") 26 | if v := w.HighlightBackground(); v != "blue" { 27 | t.Fatal("HighlightBackground", "blue", v) 28 | } 29 | 30 | w.SetHighlightColor("blue") 31 | if v := w.HighlightColor(); v != "blue" { 32 | t.Fatal("HighlightColor", "blue", v) 33 | } 34 | 35 | w.SetHighlightthickness(20) 36 | if v := w.Highlightthickness(); v != 20 { 37 | t.Fatal("Highlightthickness", 20, v) 38 | } 39 | 40 | w.SetInsertBackground("blue") 41 | if v := w.InsertBackground(); v != "blue" { 42 | t.Fatal("InsertBackground", "blue", v) 43 | } 44 | 45 | w.SetInsertBorderWidth(20) 46 | if v := w.InsertBorderWidth(); v != 20 { 47 | t.Fatal("InsertBorderWidth", 20, v) 48 | } 49 | 50 | w.SetInsertOffTime(20) 51 | if v := w.InsertOffTime(); v != 20 { 52 | t.Fatal("InsertOffTime", 20, v) 53 | } 54 | 55 | w.SetInsertOnTime(20) 56 | if v := w.InsertOnTime(); v != 20 { 57 | t.Fatal("InsertOnTime", 20, v) 58 | } 59 | 60 | w.SetInsertWidth(20) 61 | if v := w.InsertWidth(); v != 20 { 62 | t.Fatal("InsertWidth", 20, v) 63 | } 64 | 65 | w.SetReliefStyle(1) 66 | if v := w.ReliefStyle(); v != 1 { 67 | t.Fatal("ReliefStyle", 1, v) 68 | } 69 | 70 | w.SetSelectBackground("blue") 71 | if v := w.SelectBackground(); v != "blue" { 72 | t.Fatal("SelectBackground", "blue", v) 73 | } 74 | 75 | w.SetSelectborderwidth(20) 76 | if v := w.Selectborderwidth(); v != 20 { 77 | t.Fatal("Selectborderwidth", 20, v) 78 | } 79 | 80 | w.SetSelectforeground("blue") 81 | if v := w.Selectforeground(); v != "blue" { 82 | t.Fatal("Selectforeground", "blue", v) 83 | } 84 | 85 | w.SetTakeFocus(true) 86 | if v := w.IsTakeFocus(); v != true { 87 | t.Fatal("IsTakeFocus", true, v) 88 | } 89 | 90 | w.SetCloseEnough(0.5) 91 | if v := w.CloseEnough(); v != 0.5 { 92 | t.Fatal("CloseEnough", 0.5, v) 93 | } 94 | 95 | w.SetConfine(true) 96 | if v := w.IsConfine(); v != true { 97 | t.Fatal("IsConfine", true, v) 98 | } 99 | 100 | w.SetWidth(20) 101 | if v := w.Width(); v != 20 { 102 | t.Fatal("Width", 20, v) 103 | } 104 | 105 | w.SetHeight(20) 106 | if v := w.Height(); v != 20 { 107 | t.Fatal("Height", 20, v) 108 | } 109 | 110 | w.SetState(StateNormal) 111 | if v := w.State(); v != StateNormal { 112 | t.Fatal("State", StateNormal, v) 113 | } 114 | 115 | w.SetXScrollIncrement(20) 116 | if v := w.XScrollIncrement(); v != 20 { 117 | t.Fatal("XScrollIncrement", 20, v) 118 | } 119 | 120 | w.SetYScrollIncrement(20) 121 | if v := w.YScrollIncrement(); v != 20 { 122 | t.Fatal("YScrollIncrement", 20, v) 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /tk/checkbox.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import "fmt" 6 | 7 | // check button 8 | type CheckButton struct { 9 | BaseWidget 10 | command *Command 11 | } 12 | 13 | func NewCheckButton(parent Widget, text string, attributes ...*WidgetAttr) *CheckButton { 14 | theme := checkInitUseTheme(attributes) 15 | iid := makeNamedWidgetId(parent, "atk_checkbutton") 16 | attributes = append(attributes, &WidgetAttr{"text", text}) 17 | attributes = append(attributes, &WidgetAttr{"variable", variableId(iid)}) 18 | info := CreateWidgetInfo(iid, WidgetTypeCheckButton, theme, attributes) 19 | if info == nil { 20 | return nil 21 | } 22 | w := &CheckButton{} 23 | w.id = iid 24 | w.info = info 25 | evalSetValue(variableId(iid), "") 26 | RegisterWidget(w) 27 | return w 28 | } 29 | 30 | func (w *CheckButton) Attach(id string) error { 31 | info, err := CheckWidgetInfo(id, WidgetTypeCheckButton) 32 | if err != nil { 33 | return err 34 | } 35 | w.id = id 36 | w.info = info 37 | RegisterWidget(w) 38 | return nil 39 | } 40 | 41 | func (w *CheckButton) SetText(text string) error { 42 | setObjText("atk_tmp_text", text) 43 | return eval(fmt.Sprintf("%v configure -text $atk_tmp_text", w.id)) 44 | } 45 | 46 | func (w *CheckButton) Text() string { 47 | r, _ := evalAsString(fmt.Sprintf("%v cget -text", w.id)) 48 | return r 49 | } 50 | 51 | func (w *CheckButton) SetWidth(width int) error { 52 | return eval(fmt.Sprintf("%v configure -width {%v}", w.id, width)) 53 | } 54 | 55 | func (w *CheckButton) Width() int { 56 | r, _ := evalAsInt(fmt.Sprintf("%v cget -width", w.id)) 57 | return r 58 | } 59 | 60 | func (w *CheckButton) SetImage(image *Image) error { 61 | if image == nil { 62 | return ErrInvalid 63 | } 64 | return eval(fmt.Sprintf("%v configure -image {%v}", w.id, image.Id())) 65 | } 66 | 67 | func (w *CheckButton) Image() *Image { 68 | r, err := evalAsString(fmt.Sprintf("%v cget -image", w.id)) 69 | return parserImageResult(r, err) 70 | } 71 | 72 | func (w *CheckButton) SetCompound(compound Compound) error { 73 | return eval(fmt.Sprintf("%v configure -compound {%v}", w.id, compound)) 74 | } 75 | 76 | func (w *CheckButton) Compound() Compound { 77 | r, err := evalAsString(fmt.Sprintf("%v cget -compound", w.id)) 78 | return parserCompoundResult(r, err) 79 | } 80 | 81 | func (w *CheckButton) SetPaddingN(padx int, pady int) error { 82 | if w.info.IsTtk { 83 | return eval(fmt.Sprintf("%v configure -padding {%v %v}", w.id, padx, pady)) 84 | } 85 | return eval(fmt.Sprintf("%v configure -padx {%v} -pady {%v}", w.id, padx, pady)) 86 | } 87 | 88 | func (w *CheckButton) PaddingN() (int, int) { 89 | var r string 90 | var err error 91 | if w.info.IsTtk { 92 | r, err = evalAsString(fmt.Sprintf("%v cget -padding", w.id)) 93 | } else { 94 | r1, _ := evalAsString(fmt.Sprintf("%v cget -padx", w.id)) 95 | r2, _ := evalAsString(fmt.Sprintf("%v cget -pady", w.id)) 96 | r = r1 + " " + r2 97 | } 98 | return parserPaddingResult(r, err) 99 | } 100 | 101 | func (w *CheckButton) SetPadding(pad Pad) error { 102 | return w.SetPaddingN(pad.X, pad.Y) 103 | } 104 | 105 | func (w *CheckButton) Padding() Pad { 106 | x, y := w.PaddingN() 107 | return Pad{x, y} 108 | } 109 | 110 | func (w *CheckButton) SetState(state State) error { 111 | return eval(fmt.Sprintf("%v configure -state {%v}", w.id, state)) 112 | } 113 | 114 | func (w *CheckButton) State() State { 115 | r, err := evalAsString(fmt.Sprintf("%v cget -state", w.id)) 116 | return parserStateResult(r, err) 117 | } 118 | 119 | func (w *CheckButton) SetTakeFocus(takefocus bool) error { 120 | return eval(fmt.Sprintf("%v configure -takefocus {%v}", w.id, boolToInt(takefocus))) 121 | } 122 | 123 | func (w *CheckButton) IsTakeFocus() bool { 124 | r, _ := evalAsBool(fmt.Sprintf("%v cget -takefocus", w.id)) 125 | return r 126 | } 127 | 128 | func (w *CheckButton) SetChecked(check bool) error { 129 | return eval(fmt.Sprintf("set %v {%v}", variableId(w.id), boolToInt(check))) 130 | } 131 | 132 | func (w *CheckButton) IsChecked() bool { 133 | r, _ := evalAsBool(fmt.Sprintf("set %v", variableId(w.id))) 134 | return r 135 | } 136 | 137 | func (w *CheckButton) OnCommand(fn func()) error { 138 | if fn == nil { 139 | return ErrInvalid 140 | } 141 | if w.command == nil { 142 | w.command = &Command{} 143 | bindCommand(w.id, "command", w.command.Invoke) 144 | } 145 | w.command.Bind(fn) 146 | return nil 147 | } 148 | 149 | func (w *CheckButton) Invoke() { 150 | eval(fmt.Sprintf("%v invoke", w.id)) 151 | } 152 | 153 | func CheckButtonAttrText(text string) *WidgetAttr { 154 | return &WidgetAttr{"text", text} 155 | } 156 | 157 | func CheckButtonAttrWidth(width int) *WidgetAttr { 158 | return &WidgetAttr{"width", width} 159 | } 160 | 161 | func CheckButtonAttrImage(image *Image) *WidgetAttr { 162 | if image == nil { 163 | return nil 164 | } 165 | return &WidgetAttr{"image", image.Id()} 166 | } 167 | 168 | func CheckButtonAttrCompound(compound Compound) *WidgetAttr { 169 | return &WidgetAttr{"compound", compound} 170 | } 171 | 172 | func CheckButtonAttrPadding(padding Pad) *WidgetAttr { 173 | return &WidgetAttr{"padding", padding} 174 | } 175 | 176 | func CheckButtonAttrState(state State) *WidgetAttr { 177 | return &WidgetAttr{"state", state} 178 | } 179 | 180 | func CheckButtonAttrTakeFocus(takefocus bool) *WidgetAttr { 181 | return &WidgetAttr{"takefocus", boolToInt(takefocus)} 182 | } 183 | -------------------------------------------------------------------------------- /tk/checkbox_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import "testing" 6 | 7 | func init() { 8 | registerTest("CheckButton", testCheckButton) 9 | } 10 | 11 | func testCheckButton(t *testing.T) { 12 | w := NewCheckButton(nil, "test", CheckButtonAttrText("text"), CheckButtonAttrWidth(20), CheckButtonAttrCompound(CompoundNone), CheckButtonAttrState(StateNormal), CheckButtonAttrTakeFocus(true)) 13 | defer w.Destroy() 14 | 15 | w.SetText("text") 16 | if v := w.Text(); v != "text" { 17 | t.Fatal("Text", "text", v) 18 | } 19 | 20 | w.SetWidth(20) 21 | if v := w.Width(); v != 20 { 22 | t.Fatal("Width", 20, v) 23 | } 24 | 25 | w.SetCompound(CompoundNone) 26 | if v := w.Compound(); v != CompoundNone { 27 | t.Fatal("Compound", CompoundNone, v) 28 | } 29 | 30 | w.SetState(StateNormal) 31 | if v := w.State(); v != StateNormal { 32 | t.Fatal("State", StateNormal, v) 33 | } 34 | 35 | w.SetTakeFocus(true) 36 | if v := w.IsTakeFocus(); v != true { 37 | t.Fatal("IsTakeFocus", true, v) 38 | } 39 | 40 | w.SetChecked(true) 41 | if v := w.IsChecked(); v != true { 42 | t.Fatal("IsChecked", true, v) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /tk/clipboard.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import "fmt" 6 | 7 | func ClearClipboard() error { 8 | return eval("clipboard clear") 9 | } 10 | 11 | func AppendToClipboard(text string) error { 12 | pname := "atk_tmp_clip" 13 | setObjText(pname, text) 14 | return eval(fmt.Sprintf("clipboard append $%v", pname)) 15 | } 16 | 17 | func GetClipboardText() string { 18 | text, _ := evalAsString("clipboard get -type UTF8_STRING") 19 | return text 20 | } 21 | -------------------------------------------------------------------------------- /tk/combobox.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import "fmt" 6 | 7 | // combbox 8 | type ComboBox struct { 9 | BaseWidget 10 | } 11 | 12 | func NewComboBox(parent Widget, attributes ...*WidgetAttr) *ComboBox { 13 | theme := checkInitUseTheme(attributes) 14 | iid := makeNamedWidgetId(parent, "atk_combobox") 15 | attributes = append(attributes, &WidgetAttr{"textvariable", variableId(iid)}) 16 | info := CreateWidgetInfo(iid, WidgetTypeComboBox, theme, attributes) 17 | if info == nil { 18 | return nil 19 | } 20 | w := &ComboBox{} 21 | w.id = iid 22 | w.info = info 23 | evalSetValue(variableId(iid), "") 24 | RegisterWidget(w) 25 | return w 26 | } 27 | 28 | func (w *ComboBox) Attach(id string) error { 29 | info, err := CheckWidgetInfo(id, WidgetTypeComboBox) 30 | if err != nil { 31 | return err 32 | } 33 | w.id = id 34 | w.info = info 35 | RegisterWidget(w) 36 | return nil 37 | } 38 | 39 | func (w *ComboBox) SetFont(font Font) error { 40 | if font == nil { 41 | return ErrInvalid 42 | } 43 | return eval(fmt.Sprintf("%v configure -font {%v}", w.id, font.Id())) 44 | } 45 | 46 | func (w *ComboBox) Font() Font { 47 | r, err := evalAsString(fmt.Sprintf("%v cget -font", w.id)) 48 | return parserFontResult(r, err) 49 | } 50 | 51 | func (w *ComboBox) SetBackground(color string) error { 52 | setObjText("atk_tmp_text", color) 53 | return eval(fmt.Sprintf("%v configure -background $atk_tmp_text", w.id)) 54 | } 55 | 56 | func (w *ComboBox) Background() string { 57 | r, _ := evalAsString(fmt.Sprintf("%v cget -background", w.id)) 58 | return r 59 | } 60 | 61 | func (w *ComboBox) SetForground(color string) error { 62 | setObjText("atk_tmp_text", color) 63 | return eval(fmt.Sprintf("%v configure -foreground $atk_tmp_text", w.id)) 64 | } 65 | 66 | func (w *ComboBox) Forground() string { 67 | r, _ := evalAsString(fmt.Sprintf("%v cget -foreground", w.id)) 68 | return r 69 | } 70 | 71 | func (w *ComboBox) SetJustify(justify Justify) error { 72 | return eval(fmt.Sprintf("%v configure -justify {%v}", w.id, justify)) 73 | } 74 | 75 | func (w *ComboBox) Justify() Justify { 76 | r, err := evalAsString(fmt.Sprintf("%v cget -justify", w.id)) 77 | return parserJustifyResult(r, err) 78 | } 79 | 80 | func (w *ComboBox) SetWidth(width int) error { 81 | return eval(fmt.Sprintf("%v configure -width {%v}", w.id, width)) 82 | } 83 | 84 | func (w *ComboBox) Width() int { 85 | r, _ := evalAsInt(fmt.Sprintf("%v cget -width", w.id)) 86 | return r 87 | } 88 | 89 | func (w *ComboBox) SetHeight(height int) error { 90 | return eval(fmt.Sprintf("%v configure -height {%v}", w.id, height)) 91 | } 92 | 93 | func (w *ComboBox) Height() int { 94 | r, _ := evalAsInt(fmt.Sprintf("%v cget -height", w.id)) 95 | return r 96 | } 97 | 98 | func (w *ComboBox) SetEcho(echo string) error { 99 | setObjText("atk_tmp_text", echo) 100 | return eval(fmt.Sprintf("%v configure -show $atk_tmp_text", w.id)) 101 | } 102 | 103 | func (w *ComboBox) Echo() string { 104 | r, _ := evalAsString(fmt.Sprintf("%v cget -show", w.id)) 105 | return r 106 | } 107 | 108 | func (w *ComboBox) SetState(state State) error { 109 | return eval(fmt.Sprintf("%v configure -state {%v}", w.id, state)) 110 | } 111 | 112 | func (w *ComboBox) State() State { 113 | r, err := evalAsString(fmt.Sprintf("%v cget -state", w.id)) 114 | return parserStateResult(r, err) 115 | } 116 | 117 | func (w *ComboBox) SetTakeFocus(takefocus bool) error { 118 | return eval(fmt.Sprintf("%v configure -takefocus {%v}", w.id, boolToInt(takefocus))) 119 | } 120 | 121 | func (w *ComboBox) IsTakeFocus() bool { 122 | r, _ := evalAsBool(fmt.Sprintf("%v cget -takefocus", w.id)) 123 | return r 124 | } 125 | 126 | func (w *ComboBox) SetValues(values []string) error { 127 | setObjTextList("atk_tmp_textlist", values) 128 | return eval(fmt.Sprintf("%v configure -values $atk_tmp_textlist", w.id)) 129 | } 130 | 131 | func (w *ComboBox) Values() []string { 132 | r, _ := evalAsStringList(fmt.Sprintf("%v cget -values", w.id)) 133 | return r 134 | } 135 | 136 | func (w *ComboBox) OnSelected(fn func()) error { 137 | if fn == nil { 138 | return ErrInvalid 139 | } 140 | w.BindEvent("<>", func(e *Event) { 141 | fn() 142 | }) 143 | return nil 144 | } 145 | 146 | func (w *ComboBox) OnEditReturn(fn func()) error { 147 | if fn == nil { 148 | return ErrInvalid 149 | } 150 | w.BindEvent("", func(e *Event) { 151 | fn() 152 | }) 153 | return nil 154 | } 155 | 156 | func (w *ComboBox) Entry() *Entry { 157 | return &Entry{w.BaseWidget, nil} 158 | } 159 | 160 | func (w *ComboBox) SetCurrentText(text string) *ComboBox { 161 | setObjText("atk_tmp_text", text) 162 | eval(fmt.Sprintf("%v set $atk_tmp_text", w.id)) 163 | return w 164 | } 165 | 166 | func (w *ComboBox) CurrentText() string { 167 | r, _ := evalAsString(fmt.Sprintf("%v get", w.id)) 168 | return r 169 | } 170 | 171 | func (w *ComboBox) SetCurrentIndex(index int) *ComboBox { 172 | eval(fmt.Sprintf("%v current {%v}", w.id, index)) 173 | return w 174 | } 175 | 176 | func (w *ComboBox) CurrentIndex() int { 177 | r, _ := evalAsInt(fmt.Sprintf("%v current", w.id)) 178 | return r 179 | } 180 | 181 | func ComboBoxAttrFont(font Font) *WidgetAttr { 182 | if font == nil { 183 | return nil 184 | } 185 | return &WidgetAttr{"font", font.Id()} 186 | } 187 | 188 | func ComboBoxAttrBackground(color string) *WidgetAttr { 189 | return &WidgetAttr{"background", color} 190 | } 191 | 192 | func ComboBoxAttrForground(color string) *WidgetAttr { 193 | return &WidgetAttr{"foreground", color} 194 | } 195 | 196 | func ComboBoxAttrJustify(justify Justify) *WidgetAttr { 197 | return &WidgetAttr{"justify", justify} 198 | } 199 | 200 | func ComboBoxAttrWidth(width int) *WidgetAttr { 201 | return &WidgetAttr{"width", width} 202 | } 203 | 204 | func ComboBoxAttrHeight(height int) *WidgetAttr { 205 | return &WidgetAttr{"height", height} 206 | } 207 | 208 | func ComboBoxAttrEcho(echo string) *WidgetAttr { 209 | return &WidgetAttr{"show", echo} 210 | } 211 | 212 | func ComboBoxAttrState(state State) *WidgetAttr { 213 | return &WidgetAttr{"state", state} 214 | } 215 | 216 | func ComboBoxAttrTakeFocus(takefocus bool) *WidgetAttr { 217 | return &WidgetAttr{"takefocus", boolToInt(takefocus)} 218 | } 219 | 220 | func ComboBoxAttrValues(values []string) *WidgetAttr { 221 | return &WidgetAttr{"values", values} 222 | } 223 | -------------------------------------------------------------------------------- /tk/combobox_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import "testing" 6 | 7 | func init() { 8 | registerTest("ComboBox", testComboBox) 9 | } 10 | 11 | func testComboBox(t *testing.T) { 12 | w := NewComboBox(nil, ComboBoxAttrBackground("blue"), ComboBoxAttrForground("blue"), ComboBoxAttrJustify(1), ComboBoxAttrWidth(20), ComboBoxAttrHeight(20), ComboBoxAttrEcho("*"), ComboBoxAttrState(StateNormal), ComboBoxAttrTakeFocus(true)) 13 | defer w.Destroy() 14 | 15 | w.SetBackground("blue") 16 | if v := w.Background(); v != "blue" { 17 | t.Fatal("Background", "blue", v) 18 | } 19 | 20 | w.SetForground("blue") 21 | if v := w.Forground(); v != "blue" { 22 | t.Fatal("Forground", "blue", v) 23 | } 24 | 25 | w.SetJustify(1) 26 | if v := w.Justify(); v != 1 { 27 | t.Fatal("Justify", 1, v) 28 | } 29 | 30 | w.SetWidth(20) 31 | if v := w.Width(); v != 20 { 32 | t.Fatal("Width", 20, v) 33 | } 34 | 35 | w.SetHeight(20) 36 | if v := w.Height(); v != 20 { 37 | t.Fatal("Height", 20, v) 38 | } 39 | 40 | w.SetEcho("*") 41 | if v := w.Echo(); v != "*" { 42 | t.Fatal("Echo", "*", v) 43 | } 44 | 45 | w.SetState(StateNormal) 46 | if v := w.State(); v != StateNormal { 47 | t.Fatal("State", StateNormal, v) 48 | } 49 | 50 | w.SetTakeFocus(true) 51 | if v := w.IsTakeFocus(); v != true { 52 | t.Fatal("IsTakeFocus", true, v) 53 | } 54 | 55 | w.SetValues([]string{"100", "$ok", "{300"}) 56 | if v := w.Values(); v[0] != "100" || v[1] != "$ok" || v[2] != "{300" { 57 | t.Fatal("values", v) 58 | } 59 | 60 | w.SetCurrentText("$ok hello}") 61 | if v := w.CurrentText(); v != "$ok hello}" { 62 | t.Fatal("CurrentText", v) 63 | } 64 | 65 | w.SetCurrentIndex(2) 66 | if v := w.CurrentIndex(); v != 2 { 67 | t.Fatal("CurrentIndex", v) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /tk/command.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import ( 6 | "fmt" 7 | "sync" 8 | ) 9 | 10 | type Command struct { 11 | sync.RWMutex 12 | cmds []func() 13 | } 14 | 15 | func (c *Command) Bind(fn func()) { 16 | if fn == nil { 17 | return 18 | } 19 | c.Lock() 20 | c.cmds = append(c.cmds, fn) 21 | c.Unlock() 22 | } 23 | 24 | func (c *Command) Clear() { 25 | c.Lock() 26 | c.cmds = nil 27 | c.Unlock() 28 | } 29 | 30 | func (c *Command) Invoke() { 31 | c.RLock() 32 | for _, cmd := range c.cmds { 33 | if cmd != nil { 34 | cmd() 35 | } 36 | } 37 | c.RUnlock() 38 | } 39 | 40 | type CommandEx struct { 41 | sync.RWMutex 42 | cmds []func([]string) error 43 | } 44 | 45 | func (c *CommandEx) Bind(fn func([]string) error) { 46 | if fn == nil { 47 | return 48 | } 49 | c.Lock() 50 | c.cmds = append(c.cmds, fn) 51 | c.Unlock() 52 | } 53 | 54 | func (c *CommandEx) Clear() { 55 | c.Lock() 56 | c.cmds = nil 57 | c.Unlock() 58 | } 59 | 60 | func (c *CommandEx) Invoke(args []string) { 61 | c.RLock() 62 | for _, cmd := range c.cmds { 63 | if cmd != nil { 64 | cmd(args) 65 | } 66 | } 67 | c.RUnlock() 68 | } 69 | 70 | func bindCommand(id string, command string, fn func()) error { 71 | actName := makeActionId() 72 | err := eval(fmt.Sprintf("%v configure -%v {%v}", id, command, actName)) 73 | if err != nil { 74 | return err 75 | } 76 | mainInterp.CreateAction(actName, func(args []string) { 77 | fn() 78 | }) 79 | return nil 80 | } 81 | 82 | func bindCommandEx(id string, command string, fn func([]string)) error { 83 | actName := makeActionId() 84 | err := eval(fmt.Sprintf("%v configure -%v {%v}", id, command, actName)) 85 | if err != nil { 86 | return err 87 | } 88 | mainInterp.CreateAction(actName, func(args []string) { 89 | fn(args) 90 | }) 91 | return nil 92 | } 93 | -------------------------------------------------------------------------------- /tk/dialog.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import ( 6 | "fmt" 7 | ) 8 | 9 | //tk_chooseColor — pops up a dialog box for the user to select a color. 10 | func ChooseColor(parent Widget, title string, initcolor string) (string, error) { 11 | script := fmt.Sprintf("tk_chooseColor") 12 | if parent != nil { 13 | script += " -parent " + parent.Id() 14 | } 15 | if initcolor != "" { 16 | script += " -initialcolor " + initcolor 17 | } 18 | if title != "" { 19 | setObjText("atk_tmp_title", title) 20 | script += " -title $atk_tmp_title" 21 | } 22 | return evalAsString(script) 23 | } 24 | 25 | //tk_chooseDirectory — pops up a dialog box for the user to select a directory. 26 | func ChooseDirectory(parent Widget, title string, initialdir string, mustexist bool) (string, error) { 27 | script := fmt.Sprintf("tk_chooseDirectory") 28 | if parent != nil { 29 | script += " -parent " + parent.Id() 30 | } 31 | if initialdir != "" { 32 | setObjText("atk_tmp_initialdir", initialdir) 33 | script += " -initialdir $atk_tmp_initialdir" 34 | } 35 | if mustexist { 36 | script += " -mustexist true" 37 | } 38 | if title != "" { 39 | setObjText("atk_tmp_title", title) 40 | script += " -title $atk_tmp_title" 41 | } 42 | return evalAsString(script) 43 | } 44 | 45 | type FileType struct { 46 | Info string 47 | Ext string 48 | } 49 | 50 | func (v FileType) String() string { 51 | return fmt.Sprintf("{%v} {%v}", v.Info, v.Ext) 52 | } 53 | 54 | //tk_getOpenFile, tk_getSaveFile — pop up a dialog box for the user to select a file to open or save. 55 | func GetOpenFile(parent Widget, title string, filetypes []FileType, initialdir string, initialfile string) (string, error) { 56 | script := fmt.Sprintf("tk_getOpenFile") 57 | if parent != nil { 58 | script += " -parent " + parent.Id() 59 | } 60 | if filetypes != nil { 61 | var info []string 62 | for _, v := range filetypes { 63 | info = append(info, v.String()) 64 | } 65 | setObjTextList("atk_tmp_filetypes", info) 66 | script += " -filetypes $atk_tmp_filetypes" 67 | } 68 | if initialdir != "" { 69 | setObjText("atk_tmp_initialdir", initialdir) 70 | script += " -initialdir $atk_tmp_initialdir" 71 | } 72 | if initialfile != "" { 73 | setObjText("atk_tmp_initialfile", initialfile) 74 | script += " -initialfile $atk_tmp_initialfile" 75 | } 76 | if title != "" { 77 | setObjText("atk_tmp_title", title) 78 | script += " -title $atk_tmp_title" 79 | } 80 | return evalAsString(script) 81 | } 82 | 83 | func GetOpenMultipleFile(parent Widget, title string, filetypes []FileType, initialdir string, initialfile string) ([]string, error) { 84 | script := fmt.Sprintf("tk_getOpenFile") 85 | if parent != nil { 86 | script += " -parent " + parent.Id() 87 | } 88 | if filetypes != nil { 89 | var info []string 90 | for _, v := range filetypes { 91 | info = append(info, v.String()) 92 | } 93 | setObjTextList("atk_tmp_filetypes", info) 94 | script += " -filetypes $atk_tmp_filetypes" 95 | } 96 | if initialdir != "" { 97 | setObjText("atk_tmp_initialdir", initialdir) 98 | script += " -initialdir $atk_tmp_initialdir" 99 | } 100 | if initialfile != "" { 101 | setObjText("atk_tmp_initialfile", initialfile) 102 | script += " -initialfile $atk_tmp_initialfile" 103 | } 104 | if title != "" { 105 | setObjText("atk_tmp_title", title) 106 | script += " -title $atk_tmp_title" 107 | } 108 | script += " -multiple true" 109 | return evalAsStringList(script) 110 | } 111 | 112 | //tk_getOpenFile, tk_getSaveFile — pop up a dialog box for the user to select a file to open or save. 113 | func GetSaveFile(parent Widget, title string, confirmoverwrite bool, defaultextension string, filetypes []FileType, initialdir string, initialfile string) (string, error) { 114 | script := fmt.Sprintf("tk_getSaveFile") 115 | if parent != nil { 116 | script += " -parent " + parent.Id() 117 | } 118 | if mainInterp.SupportTk86() { 119 | script += " -confirmoverwrite " + fmt.Sprint(confirmoverwrite) 120 | } 121 | if defaultextension != "" { 122 | setObjText("atk_tmp_defaultextension", defaultextension) 123 | script += " -defaultextension $atk_tmp_defaultextension" 124 | } 125 | if filetypes != nil { 126 | var info []string 127 | for _, v := range filetypes { 128 | info = append(info, v.String()) 129 | } 130 | setObjTextList("atk_tmp_filetypes", info) 131 | script += " -filetypes $atk_tmp_filetypes" 132 | } 133 | if initialdir != "" { 134 | setObjText("atk_tmp_initialdir", initialdir) 135 | script += " -initialdir $atk_tmp_initialdir" 136 | } 137 | if initialfile != "" { 138 | setObjText("atk_tmp_initialfile", initialfile) 139 | script += " -initialfile $atk_tmp_initialfile" 140 | } 141 | if title != "" { 142 | setObjText("atk_tmp_title", title) 143 | script += " -title $atk_tmp_title" 144 | } 145 | return evalAsString(script) 146 | } 147 | 148 | type MessageBoxIcon int 149 | 150 | const ( 151 | MessageBoxIconNone MessageBoxIcon = iota 152 | MessageBoxIconError 153 | MessageBoxIconInfo 154 | MessageBoxIconQuestion 155 | MessageBoxIconWarning 156 | ) 157 | 158 | var ( 159 | messageBoxIconName = []string{"", "error", "info", "question", "warning"} 160 | ) 161 | 162 | func (v MessageBoxIcon) String() string { 163 | if v >= 0 && int(v) < len(messageBoxIconName) { 164 | return messageBoxIconName[v] 165 | } 166 | return "" 167 | } 168 | 169 | type MessageBoxType int 170 | 171 | const ( 172 | MessageBoxTypeOk MessageBoxType = iota 173 | MessageBoxTypeOkCancel 174 | MessageBoxTypeAbortRetryIgnore 175 | MessageBoxTypeRetryCancel 176 | MessageBoxTypeYesNo 177 | MessageBoxTypeYesNoCancel 178 | ) 179 | 180 | var ( 181 | messageBoxTypeName = []string{"ok", "okcancel", "abortretryignore", "retrycancel", "yesno", "yesnocancel"} 182 | ) 183 | 184 | func (v MessageBoxType) String() string { 185 | if v >= 0 && int(v) < len(messageBoxTypeName) { 186 | return messageBoxTypeName[v] 187 | } 188 | return "" 189 | } 190 | 191 | //tk_messageBox — pops up a message window and waits for user response. 192 | func MessageBox(parent Widget, title string, message string, detail string, defaultbutton string, icon MessageBoxIcon, typ MessageBoxType) (string, error) { 193 | script := fmt.Sprintf("tk_messageBox") 194 | if parent != nil { 195 | script += " -parent " + parent.Id() 196 | } 197 | if defaultbutton != "" { 198 | setObjText("atk_tmp_defaultbutton", defaultbutton) 199 | script += " -default $atk_tmp_defaultbutton" 200 | } 201 | if message != "" { 202 | setObjText("atk_tmp_message", message) 203 | script += " -message $atk_tmp_message" 204 | } 205 | sicon := icon.String() 206 | if sicon != "" { 207 | script += " -icon " + sicon 208 | } 209 | styp := typ.String() 210 | if styp != "" { 211 | script += " -type " + styp 212 | } 213 | if detail != "" { 214 | setObjText("atk_tmp_detail", detail) 215 | script += " -detail $atk_tmp_detail" 216 | } 217 | if title != "" { 218 | setObjText("atk_tmp_title", title) 219 | script += " -title $atk_tmp_title" 220 | } 221 | return evalAsString(script) 222 | } 223 | -------------------------------------------------------------------------------- /tk/entry_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import "testing" 6 | 7 | func init() { 8 | registerTest("Entry", testEntry) 9 | } 10 | 11 | func testEntry(t *testing.T) { 12 | w := NewEntry(nil, EntryAttrForeground("blue"), EntryAttrBackground("blue"), EntryAttrWidth(20), EntryAttrJustify(1), EntryAttrShow("text"), EntryAttrState(StateNormal), EntryAttrTakeFocus(true), EntryAttrExportSelection(true)) 13 | defer w.Destroy() 14 | 15 | w.SetForeground("blue") 16 | if v := w.Foreground(); v != "blue" { 17 | t.Fatal("Foreground", "blue", v) 18 | } 19 | 20 | w.SetBackground("blue") 21 | if v := w.Background(); v != "blue" { 22 | t.Fatal("Background", "blue", v) 23 | } 24 | 25 | w.SetWidth(20) 26 | if v := w.Width(); v != 20 { 27 | t.Fatal("Width", 20, v) 28 | } 29 | 30 | w.SetJustify(1) 31 | if v := w.Justify(); v != 1 { 32 | t.Fatal("Justify", 1, v) 33 | } 34 | 35 | w.SetShow("text") 36 | if v := w.Show(); v != "text" { 37 | t.Fatal("Show", "text", v) 38 | } 39 | 40 | w.SetState(StateNormal) 41 | if v := w.State(); v != StateNormal { 42 | t.Fatal("State", StateNormal, v) 43 | } 44 | 45 | w.SetTakeFocus(true) 46 | if v := w.IsTakeFocus(); v != true { 47 | t.Fatal("IsTakeFocus", true, v) 48 | } 49 | 50 | w.SetExportSelection(true) 51 | if v := w.IsExportSelection(); v != true { 52 | t.Fatal("IsExportSelection", true, v) 53 | } 54 | 55 | w.SetText("text") 56 | if v := w.Text(); v != "text" { 57 | t.Fatal("Text", "text", v) 58 | } 59 | 60 | w.SetState(StateNormal) 61 | w.SetShow("") 62 | 63 | w.SetText("abc中文") 64 | if w.TextLength() != 5 { 65 | t.Fatal("TextLength", w.TextLength()) 66 | } 67 | if w.SetCursorPosition(1).CursorPosition() != 1 { 68 | t.Fatal("CursorPostion") 69 | } 70 | if w.HasSelectedText() { 71 | t.Fatal("HasSelectedText") 72 | } 73 | w.SelectAll() 74 | if !w.HasSelectedText() { 75 | t.Fatal("HasSelectedText") 76 | } 77 | if w.SelectedText() != w.Text() { 78 | t.Fatal("SelectAll", w.SelectionStart(), w.SelectionEnd()) 79 | } 80 | w.ClearSelection() 81 | if w.HasSelectedText() { 82 | t.Fatal("ClearSelection") 83 | } 84 | w.SetSelection(2, 4) 85 | if w.SelectionStart() != 2 || w.SelectionEnd() != 4 { 86 | t.Fatal("SetSelection") 87 | } 88 | Update() 89 | if w.SelectedText() != "c中" { 90 | t.Fatal("SelectedText", w.SelectedText()) 91 | } 92 | w.DeleteRange(2, 4) 93 | if w.HasSelectedText() { 94 | t.Fatal("DeleteRange", w.Text(), w.SelectedText()) 95 | } 96 | w.Insert(2, "中") 97 | w.Insert(3, "c-") 98 | w.Delete(3) 99 | if w.Text() != "ab中-文" { 100 | t.Fatal("Text", w.Text()) 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /tk/error.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import "errors" 6 | 7 | var ( 8 | ErrInvalid = errors.New("invalid argument") 9 | ErrExist = errors.New("already exists") 10 | ErrNotExist = errors.New("does not exist") 11 | ErrClosed = errors.New("already closed") 12 | ErrUnsupport = errors.New("unsupport") 13 | ) 14 | -------------------------------------------------------------------------------- /tk/font.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import ( 6 | "fmt" 7 | "strings" 8 | ) 9 | 10 | type Font interface { 11 | Id() string 12 | IsValid() bool 13 | String() string 14 | Description() string 15 | Family() string 16 | Size() int 17 | IsBold() bool 18 | IsItalic() bool 19 | IsUnderline() bool 20 | IsOverstrike() bool 21 | } 22 | 23 | type FontAttr struct { 24 | key string 25 | value interface{} 26 | } 27 | 28 | func FontAttrBold() *FontAttr { 29 | return &FontAttr{"weight", "bold"} 30 | } 31 | 32 | func FontAttrItalic() *FontAttr { 33 | return &FontAttr{"slant", "italic"} 34 | } 35 | 36 | func FontAttrUnderline() *FontAttr { 37 | return &FontAttr{"underline", 1} 38 | } 39 | 40 | func FontAttrOverstrike() *FontAttr { 41 | return &FontAttr{"overstrike", 1} 42 | } 43 | 44 | type BaseFont struct { 45 | id string 46 | } 47 | 48 | func (f *BaseFont) Id() string { 49 | return f.id 50 | } 51 | 52 | func (f *BaseFont) IsValid() bool { 53 | return f.id != "" 54 | } 55 | 56 | func (w *BaseFont) String() string { 57 | return fmt.Sprintf("Font{%v}", w.id) 58 | } 59 | 60 | func (w *BaseFont) Description() string { 61 | if w.id == "" { 62 | return "" 63 | } 64 | r, _ := evalAsString(fmt.Sprintf("font actual %v", w.id)) 65 | return r 66 | } 67 | 68 | func (w *BaseFont) Family() string { 69 | r, _ := evalAsString(fmt.Sprintf("font actual %v -family", w.id)) 70 | return r 71 | } 72 | 73 | func (w *BaseFont) Size() int { 74 | r, _ := evalAsInt(fmt.Sprintf("font actual %v -size", w.id)) 75 | return r 76 | } 77 | 78 | func (w *BaseFont) IsBold() bool { 79 | r, _ := evalAsString(fmt.Sprintf("font actual %v -weight", w.id)) 80 | return r == "bold" 81 | } 82 | 83 | func (w *BaseFont) IsItalic() bool { 84 | r, _ := evalAsString(fmt.Sprintf("font actual %v -slant", w.id)) 85 | return r == "italic" 86 | } 87 | 88 | func (w *BaseFont) IsUnderline() bool { 89 | r, _ := evalAsBool(fmt.Sprintf("font actual %v -underline", w.id)) 90 | return r 91 | } 92 | 93 | func (w *BaseFont) IsOverstrike() bool { 94 | r, _ := evalAsBool(fmt.Sprintf("font actual %v -overstrike", w.id)) 95 | return r 96 | } 97 | 98 | func (w *BaseFont) MeasureTextWidth(text string) int { 99 | setObjText("atk_tmp_text", text) 100 | r, _ := evalAsInt(fmt.Sprintf("font measure %v $atk_tmp_text", w.id)) 101 | return r 102 | } 103 | 104 | func (w *BaseFont) Ascent() int { 105 | r, _ := evalAsInt(fmt.Sprintf("font metrics %v -ascent", w.id)) 106 | return r 107 | } 108 | 109 | func (w *BaseFont) Descent() int { 110 | r, _ := evalAsInt(fmt.Sprintf("font metrics %v -descent", w.id)) 111 | return r 112 | } 113 | 114 | func (w *BaseFont) Clone() *UserFont { 115 | iid := makeNamedId("atk_font") 116 | script := fmt.Sprintf("font create %v %v", iid, w.Description()) 117 | if eval(script) != nil { 118 | return nil 119 | } 120 | return &UserFont{BaseFont{iid}} 121 | } 122 | 123 | type UserFont struct { 124 | BaseFont 125 | } 126 | 127 | func (f *UserFont) Destroy() error { 128 | if f.id == "" { 129 | return ErrInvalid 130 | } 131 | eval(fmt.Sprintf("font delete %v", f.id)) 132 | f.id = "" 133 | return nil 134 | } 135 | 136 | func NewUserFont(family string, size int, attributes ...*FontAttr) *UserFont { 137 | var attrList []string 138 | for _, attr := range attributes { 139 | if attr == nil { 140 | continue 141 | } 142 | attrList = append(attrList, fmt.Sprintf("-%v {%v}", attr.key, attr.value)) 143 | } 144 | iid := makeNamedId("atk_font") 145 | setObjText("atk_tmp_family", family) 146 | script := fmt.Sprintf("font create %v -family $atk_tmp_family -size %v", iid, size) 147 | if len(attrList) > 0 { 148 | script += " " + strings.Join(attrList, " ") 149 | } 150 | err := eval(script) 151 | if err != nil { 152 | return nil 153 | } 154 | return &UserFont{BaseFont{iid}} 155 | } 156 | 157 | func NewUserFontFromClone(font Font) *UserFont { 158 | if font == nil { 159 | return nil 160 | } 161 | iid := makeNamedId("atk_font") 162 | script := fmt.Sprintf("font create %v", iid) 163 | if font != nil { 164 | script += " " + font.Description() 165 | } 166 | err := eval(script) 167 | if err != nil { 168 | return nil 169 | } 170 | return &UserFont{BaseFont{iid}} 171 | } 172 | 173 | func (w *UserFont) SetFamily(family string) error { 174 | setObjText("atk_tmp_family", family) 175 | return eval(fmt.Sprintf("font configure %v -family $atk_tmp_family", w.id)) 176 | } 177 | 178 | func (w *UserFont) SetSize(size int) error { 179 | return eval(fmt.Sprintf("font configure %v -size {%v}", w.id, size)) 180 | } 181 | 182 | func (w *UserFont) SetBold(bold bool) error { 183 | var v string 184 | if bold { 185 | v = "bold" 186 | } else { 187 | v = "normal" 188 | } 189 | return eval(fmt.Sprintf("font configure %v -weight {%v}", w.id, v)) 190 | } 191 | 192 | func (w *UserFont) SetItalic(italic bool) error { 193 | var v string 194 | if italic { 195 | v = "italic" 196 | } else { 197 | v = "roman" 198 | } 199 | return eval(fmt.Sprintf("font configure %v -slant {%v}", w.id, v)) 200 | } 201 | 202 | func (w *UserFont) SetUnderline(underline bool) error { 203 | return eval(fmt.Sprintf("font configure %v -underline {%v}", w.id, boolToInt(underline))) 204 | } 205 | 206 | func (w *UserFont) SetOverstrike(overstrike bool) error { 207 | return eval(fmt.Sprintf("font configure %v -overstrike {%v}", w.id, boolToInt(overstrike))) 208 | } 209 | 210 | func FontFamilieList() []string { 211 | r, _ := evalAsStringList("font families") 212 | return r 213 | } 214 | 215 | //tk system default font 216 | type SysFont struct { 217 | BaseFont 218 | } 219 | 220 | type SysFontType int 221 | 222 | const ( 223 | SysDefaultFont SysFontType = 0 224 | SysTextFont 225 | SysFixedFont 226 | SysMenuFont 227 | SysHeadingFont 228 | SysCaptionFont 229 | SysSmallCaptionFont 230 | SysIconFont 231 | SysTooltipFont 232 | ) 233 | 234 | var ( 235 | sysFontNameList = []string{ 236 | "TKDefaultFont", 237 | "TKTextFont", 238 | "TKFixedFont", 239 | "TKMenuFont", 240 | "TKHeadingFont", 241 | "TKCaptionFont", 242 | "TKSmallCaptionFont", 243 | "TKIconFont", 244 | "TKTooltipFont", 245 | } 246 | sysFontList []*SysFont 247 | ) 248 | 249 | func init() { 250 | for _, name := range sysFontNameList { 251 | sysFontList = append(sysFontList, &SysFont{BaseFont{name}}) 252 | } 253 | } 254 | 255 | func LoadSysFont(typ SysFontType) *SysFont { 256 | if int(typ) >= 0 && int(typ) < len(sysFontList) { 257 | return sysFontList[typ] 258 | } 259 | return nil 260 | } 261 | 262 | func parserFontResult(r string, err error) Font { 263 | if err != nil || r == "" { 264 | return nil 265 | } 266 | for _, f := range sysFontList { 267 | if f.Id() == r { 268 | return f 269 | } 270 | } 271 | return &UserFont{BaseFont{r}} 272 | } 273 | -------------------------------------------------------------------------------- /tk/font_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import ( 6 | "testing" 7 | ) 8 | 9 | func init() { 10 | registerTest("Font", testFont) 11 | } 12 | 13 | func testFont(t *testing.T) { 14 | font := NewUserFont("Courier", 18, FontAttrBold(), FontAttrItalic(), FontAttrUnderline(), FontAttrOverstrike()) 15 | defer font.Destroy() 16 | 17 | fname := font.Family() 18 | font.SetFamily("Courier") 19 | if v := font.Family(); v != fname { 20 | t.Fatal(v) 21 | } 22 | 23 | font.SetSize(20) 24 | if v := font.Size(); v != 20 { 25 | t.Fatal(v, 20) 26 | } 27 | 28 | font.SetBold(true) 29 | if v := font.IsBold(); v != true { 30 | t.Fatal(v) 31 | } 32 | 33 | font.SetItalic(true) 34 | if v := font.IsItalic(); v != true { 35 | t.Fatal(v) 36 | } 37 | 38 | font.SetUnderline(true) 39 | if v := font.IsUnderline(); v != true { 40 | t.Fatal(v) 41 | } 42 | 43 | font.SetOverstrike(true) 44 | if v := font.IsOverstrike(); v != true { 45 | t.Fatal(v) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /tk/frame.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import "fmt" 6 | 7 | // frame 8 | type Frame struct { 9 | BaseWidget 10 | } 11 | 12 | func NewFrame(parent Widget, attributes ...*WidgetAttr) *Frame { 13 | theme := checkInitUseTheme(attributes) 14 | iid := makeNamedWidgetId(parent, "atk_frame") 15 | info := CreateWidgetInfo(iid, WidgetTypeFrame, theme, attributes) 16 | if info == nil { 17 | return nil 18 | } 19 | w := &Frame{} 20 | w.id = iid 21 | w.info = info 22 | RegisterWidget(w) 23 | return w 24 | } 25 | 26 | func (w *Frame) Attach(id string) error { 27 | info, err := CheckWidgetInfo(id, WidgetTypeFrame) 28 | if err != nil { 29 | return err 30 | } 31 | w.id = id 32 | w.info = info 33 | RegisterWidget(w) 34 | return nil 35 | } 36 | 37 | func (w *Frame) SetBorderWidth(width int) error { 38 | return eval(fmt.Sprintf("%v configure -borderwidth {%v}", w.id, width)) 39 | } 40 | 41 | func (w *Frame) BorderWidth() int { 42 | r, _ := evalAsInt(fmt.Sprintf("%v cget -borderwidth", w.id)) 43 | return r 44 | } 45 | 46 | func (w *Frame) SetReliefStyle(relief ReliefStyle) error { 47 | return eval(fmt.Sprintf("%v configure -relief {%v}", w.id, relief)) 48 | } 49 | 50 | func (w *Frame) ReliefStyle() ReliefStyle { 51 | r, err := evalAsString(fmt.Sprintf("%v cget -relief", w.id)) 52 | return parserReliefStyleResult(r, err) 53 | } 54 | 55 | func (w *Frame) SetWidth(width int) error { 56 | return eval(fmt.Sprintf("%v configure -width {%v}", w.id, width)) 57 | } 58 | 59 | func (w *Frame) Width() int { 60 | r, _ := evalAsInt(fmt.Sprintf("%v cget -width", w.id)) 61 | return r 62 | } 63 | 64 | func (w *Frame) SetHeight(height int) error { 65 | return eval(fmt.Sprintf("%v configure -height {%v}", w.id, height)) 66 | } 67 | 68 | func (w *Frame) Height() int { 69 | r, _ := evalAsInt(fmt.Sprintf("%v cget -height", w.id)) 70 | return r 71 | } 72 | 73 | func (w *Frame) SetPaddingN(padx int, pady int) error { 74 | if w.info.IsTtk { 75 | return eval(fmt.Sprintf("%v configure -padding {%v %v}", w.id, padx, pady)) 76 | } 77 | return eval(fmt.Sprintf("%v configure -padx {%v} -pady {%v}", w.id, padx, pady)) 78 | } 79 | 80 | func (w *Frame) PaddingN() (int, int) { 81 | var r string 82 | var err error 83 | if w.info.IsTtk { 84 | r, err = evalAsString(fmt.Sprintf("%v cget -padding", w.id)) 85 | } else { 86 | r1, _ := evalAsString(fmt.Sprintf("%v cget -padx", w.id)) 87 | r2, _ := evalAsString(fmt.Sprintf("%v cget -pady", w.id)) 88 | r = r1 + " " + r2 89 | } 90 | return parserPaddingResult(r, err) 91 | } 92 | 93 | func (w *Frame) SetPadding(pad Pad) error { 94 | return w.SetPaddingN(pad.X, pad.Y) 95 | } 96 | 97 | func (w *Frame) Padding() Pad { 98 | x, y := w.PaddingN() 99 | return Pad{x, y} 100 | } 101 | 102 | func (w *Frame) SetTakeFocus(takefocus bool) error { 103 | return eval(fmt.Sprintf("%v configure -takefocus {%v}", w.id, boolToInt(takefocus))) 104 | } 105 | 106 | func (w *Frame) IsTakeFocus() bool { 107 | r, _ := evalAsBool(fmt.Sprintf("%v cget -takefocus", w.id)) 108 | return r 109 | } 110 | 111 | func FrameAttrBorderWidth(width int) *WidgetAttr { 112 | return &WidgetAttr{"borderwidth", width} 113 | } 114 | 115 | func FrameAttrReliefStyle(relief ReliefStyle) *WidgetAttr { 116 | return &WidgetAttr{"relief", relief} 117 | } 118 | 119 | func FrameAttrWidth(width int) *WidgetAttr { 120 | return &WidgetAttr{"width", width} 121 | } 122 | 123 | func FrameAttrHeight(height int) *WidgetAttr { 124 | return &WidgetAttr{"height", height} 125 | } 126 | 127 | func FrameAttrPadding(pad Pad) *WidgetAttr { 128 | return &WidgetAttr{"pad", pad} 129 | } 130 | 131 | func FrameAttrTakeFocus(takefocus bool) *WidgetAttr { 132 | return &WidgetAttr{"takefocus", boolToInt(takefocus)} 133 | } 134 | -------------------------------------------------------------------------------- /tk/frame_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import "testing" 6 | 7 | func init() { 8 | registerTest("Frame", testFrame) 9 | } 10 | 11 | func testFrame(t *testing.T) { 12 | w := NewFrame(nil, FrameAttrBorderWidth(20), FrameAttrReliefStyle(1), FrameAttrWidth(20), FrameAttrHeight(20), FrameAttrTakeFocus(true)) 13 | defer w.Destroy() 14 | 15 | w.SetBorderWidth(20) 16 | if v := w.BorderWidth(); v != 20 { 17 | t.Fatal("BorderWidth", 20, v) 18 | } 19 | 20 | w.SetReliefStyle(1) 21 | if v := w.ReliefStyle(); v != 1 { 22 | t.Fatal("ReliefStyle", 1, v) 23 | } 24 | 25 | w.SetWidth(20) 26 | if v := w.Width(); v != 20 { 27 | t.Fatal("Width", 20, v) 28 | } 29 | 30 | w.SetHeight(20) 31 | if v := w.Height(); v != 20 { 32 | t.Fatal("Height", 20, v) 33 | } 34 | 35 | w.SetTakeFocus(true) 36 | if v := w.IsTakeFocus(); v != true { 37 | t.Fatal("IsTakeFocus", true, v) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /tk/grid.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import ( 6 | "fmt" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | func GridAttrColumn(n int) *LayoutAttr { 12 | return &LayoutAttr{"column", n} 13 | } 14 | 15 | func GridAttrColumnSpan(n int) *LayoutAttr { 16 | return &LayoutAttr{"columnspan", n} 17 | } 18 | 19 | func GridAttrRow(n int) *LayoutAttr { 20 | return &LayoutAttr{"row", n} 21 | } 22 | 23 | func GridAttrRowSpan(n int) *LayoutAttr { 24 | return &LayoutAttr{"rowspan", n} 25 | } 26 | 27 | func GridAttrInMaster(w Widget) *LayoutAttr { 28 | if !IsValidWidget(w) { 29 | return nil 30 | } 31 | return &LayoutAttr{"in", w.Id()} 32 | } 33 | 34 | func GridAttrIpadx(padx int) *LayoutAttr { 35 | return &LayoutAttr{"ipadx", padx} 36 | } 37 | 38 | func GridAttrIpady(pady int) *LayoutAttr { 39 | return &LayoutAttr{"ipady", pady} 40 | } 41 | 42 | func GridAttrPadx(padx int) *LayoutAttr { 43 | return &LayoutAttr{"padx", padx} 44 | } 45 | 46 | func GridAttrPady(pady int) *LayoutAttr { 47 | return &LayoutAttr{"pady", pady} 48 | } 49 | 50 | func GridAttrSticky(v Sticky) *LayoutAttr { 51 | return &LayoutAttr{"sticky", v} 52 | } 53 | 54 | type GridIndexAttr struct { 55 | key string 56 | value interface{} 57 | } 58 | 59 | func GridIndexAttrMinSize(amount int) *GridIndexAttr { 60 | return &GridIndexAttr{"minsize", amount} 61 | } 62 | 63 | func GridIndexAttrPad(amount int) *GridIndexAttr { 64 | return &GridIndexAttr{"pad", amount} 65 | } 66 | 67 | func GridIndexAttrWeight(value int) *GridIndexAttr { 68 | return &GridIndexAttr{"weight", value} 69 | } 70 | 71 | func GridIndexAttrUniform(groupname string) *GridIndexAttr { 72 | return &GridIndexAttr{"uniform", groupname} 73 | } 74 | 75 | func Grid(widget Widget, attributes ...*LayoutAttr) error { 76 | return GridList([]Widget{widget}, attributes...) 77 | } 78 | 79 | func GridRemove(widget Widget) error { 80 | if !IsValidWidget(widget) { 81 | return ErrInvalid 82 | } 83 | return eval("grid forget " + widget.Id()) 84 | } 85 | 86 | var ( 87 | gridAttrKeys = []string{ 88 | "column", "columnspan", 89 | "row", "rowspan", 90 | "in", 91 | "ipadx", "ipady", 92 | "padx", "pady", 93 | "sticky", 94 | } 95 | gridIndexAttrKeys = []string{ 96 | "minsize", 97 | "pad", 98 | "weight", 99 | "uniform", 100 | } 101 | ) 102 | 103 | func GridList(widgets []Widget, attributes ...*LayoutAttr) error { 104 | var idList []string 105 | for _, w := range widgets { 106 | if IsValidWidget(w) { 107 | w = checkLayoutWidget(w) 108 | idList = append(idList, w.Id()) 109 | } else { 110 | idList = append(idList, "x") 111 | } 112 | } 113 | if len(idList) == 0 { 114 | return ErrInvalid 115 | } 116 | var attrList []string 117 | for _, attr := range attributes { 118 | if attr == nil || !isValidKey(attr.Key, gridAttrKeys) { 119 | continue 120 | } 121 | attrList = append(attrList, fmt.Sprintf("-%v {%v}", attr.Key, attr.Value)) 122 | } 123 | script := fmt.Sprintf("grid %v", strings.Join(idList, " ")) 124 | if len(attrList) > 0 { 125 | script += " " + strings.Join(attrList, " ") 126 | } 127 | return eval(script) 128 | } 129 | 130 | // row index from 0; -1=all 131 | func GridRowIndex(master Widget, index int, attributes ...*GridIndexAttr) error { 132 | return gridIndex(master, true, index, attributes) 133 | } 134 | 135 | // column index from 0; -1=all 136 | func GridColumnIndex(master Widget, index int, attributes ...*GridIndexAttr) error { 137 | return gridIndex(master, false, index, attributes) 138 | } 139 | 140 | func gridIndex(master Widget, row bool, index int, attributes []*GridIndexAttr) error { 141 | if master == nil { 142 | master = rootWindow 143 | } 144 | var sindex string 145 | if index < 0 { 146 | sindex = "all" 147 | } else { 148 | sindex = strconv.Itoa(index) 149 | } 150 | var attrList []string 151 | for _, attr := range attributes { 152 | if attr == nil || !isValidKey(attr.key, gridIndexAttrKeys) { 153 | continue 154 | } 155 | attrList = append(attrList, fmt.Sprintf("-%v {%v}", attr.key, attr.value)) 156 | } 157 | var script string 158 | if row { 159 | script = fmt.Sprintf("grid rowconfigure %v %v", master.Id(), sindex) 160 | } else { 161 | script = fmt.Sprintf("grid columnconfigure %v %v", master.Id(), sindex) 162 | } 163 | if len(attrList) > 0 { 164 | script += " " + strings.Join(attrList, " ") 165 | } 166 | return eval(script) 167 | } 168 | -------------------------------------------------------------------------------- /tk/gridlayout.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import "fmt" 6 | 7 | type GridLayout struct { 8 | *LayoutFrame 9 | items []*LayoutItem 10 | } 11 | 12 | func (w *GridLayout) AddWidget(widget Widget, attrs ...*LayoutAttr) error { 13 | if !IsValidWidget(widget) { 14 | return ErrInvalid 15 | } 16 | return Grid(widget, AppendLayoutAttrs(attrs, GridAttrInMaster(w))...) 17 | } 18 | 19 | func (w *GridLayout) AddWidgets(widgets ...Widget) error { 20 | return GridList(widgets, GridAttrInMaster(w)) 21 | } 22 | 23 | func (w *GridLayout) AddWidgetList(widgets []Widget, attrs ...*LayoutAttr) error { 24 | return GridList(widgets, AppendLayoutAttrs(attrs, GridAttrInMaster(w))...) 25 | } 26 | 27 | func (w *GridLayout) AddWidgetEx(widget Widget, row int, column int, rowspan int, columnspan int, sticky Sticky) error { 28 | if !IsValidWidget(widget) { 29 | return ErrInvalid 30 | } 31 | return Grid(widget, GridAttrRow(row), GridAttrColumn(column), 32 | GridAttrRowSpan(rowspan), GridAttrColumnSpan(columnspan), 33 | GridAttrSticky(sticky), GridAttrInMaster(w)) 34 | } 35 | 36 | func (w *GridLayout) RemoveWidget(widget Widget) error { 37 | if !IsValidWidget(widget) { 38 | return ErrInvalid 39 | } 40 | return GridRemove(widget) 41 | } 42 | 43 | func (w *GridLayout) Repack() error { 44 | return Pack(w, PackAttrFill(FillBoth), PackAttrExpand(true)) 45 | } 46 | 47 | func (w *GridLayout) SetBorderWidth(width int) error { 48 | return eval(fmt.Sprintf("%v configure -borderwidth {%v}", w.Id(), width)) 49 | } 50 | 51 | func (w *GridLayout) BorderWidth() int { 52 | r, _ := evalAsInt(fmt.Sprintf("%v cget -borderwidth", w.Id())) 53 | return r 54 | } 55 | 56 | // row index from 0, -1=all 57 | func (w *GridLayout) SetRowAttr(row int, pad int, weight int, group string) error { 58 | return GridRowIndex(w, row, GridIndexAttrPad(pad), GridIndexAttrWeight(weight), GridIndexAttrUniform(group)) 59 | } 60 | 61 | // column index from 0, -1=all 62 | func (w *GridLayout) SetColumnAttr(column int, pad int, weight int, group string) error { 63 | return GridColumnIndex(w, column, GridIndexAttrPad(pad), GridIndexAttrWeight(weight), GridIndexAttrUniform(group)) 64 | } 65 | 66 | func NewGridLayout(parent Widget) *GridLayout { 67 | grid := &GridLayout{NewLayoutFrame(parent), nil} 68 | grid.Lower(nil) 69 | grid.Repack() 70 | return grid 71 | } 72 | -------------------------------------------------------------------------------- /tk/ids.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import ( 6 | "fmt" 7 | "sync" 8 | ) 9 | 10 | func NewGenInt64Func(id int64) func() <-chan int64 { 11 | ch := make(chan int64) 12 | go func(i int64) { 13 | for { 14 | i++ 15 | ch <- i 16 | } 17 | }(id) 18 | return func() <-chan int64 { 19 | return ch 20 | } 21 | } 22 | 23 | func NewGenIntFunc(id int) func() <-chan int { 24 | ch := make(chan int) 25 | go func(i int) { 26 | for { 27 | i++ 28 | ch <- i 29 | } 30 | }(id) 31 | return func() <-chan int { 32 | return ch 33 | } 34 | } 35 | 36 | type NamedId interface { 37 | GetId(name string) string 38 | } 39 | 40 | type baseNamedId struct { 41 | m map[string]int 42 | } 43 | 44 | func (m *baseNamedId) GetId(name string) (r string) { 45 | m.m[name]++ 46 | r = fmt.Sprintf("%v%v", name, m.m[name]) 47 | return 48 | } 49 | 50 | type safeNamedId struct { 51 | sync.Mutex 52 | m map[string]int 53 | } 54 | 55 | func (m *safeNamedId) GetId(name string) (r string) { 56 | m.Lock() 57 | m.m[name]++ 58 | r = fmt.Sprintf("%v%v", name, m.m[name]) 59 | m.Unlock() 60 | return 61 | } 62 | 63 | func NewNamedId(safe bool) NamedId { 64 | if safe { 65 | return &safeNamedId{m: make(map[string]int)} 66 | } 67 | return &baseNamedId{make(map[string]int)} 68 | } 69 | 70 | var ( 71 | atkNamedId = NewNamedId(false) 72 | ) 73 | 74 | func makeNamedId(name string) string { 75 | return atkNamedId.GetId(name) 76 | } 77 | 78 | func makeNamedWidgetId(parent Widget, typ string) string { 79 | if parent == nil || parent.Id() == "." { 80 | return makeNamedId("." + typ) 81 | } 82 | return makeNamedId(parent.Id() + "." + typ) 83 | } 84 | 85 | func makeActionId() string { 86 | return makeNamedId("atk_action") 87 | } 88 | 89 | func makeBindEventId() string { 90 | return makeNamedId("atk_bindevent") 91 | } 92 | 93 | func makeTreeItemId(treeid string, pid string) string { 94 | if pid != "" { 95 | return makeNamedId(pid + ".I") 96 | } 97 | return makeNamedId(treeid + ".I") 98 | } 99 | 100 | func variableId(id string) string { 101 | return "::atk" + id + "_variable" 102 | } 103 | 104 | func evalSetValue(id string, value string) error { 105 | return eval(fmt.Sprintf("set %v {%v}", id, value)) 106 | } 107 | 108 | func evalGetValue(id string) string { 109 | r, _ := evalAsString(fmt.Sprintf("set %v", id)) 110 | return r 111 | } 112 | 113 | func traceVariable(id string, fn func()) error { 114 | act := makeActionId() 115 | mainInterp.CreateAction(act, func(args []string) { 116 | if fn != nil { 117 | fn() 118 | } 119 | }) 120 | return eval(fmt.Sprintf("trace add variable %v write %v", id, act)) 121 | } 122 | -------------------------------------------------------------------------------- /tk/image.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import ( 6 | "errors" 7 | "fmt" 8 | "image" 9 | "image/color" 10 | _ "image/png" 11 | "os" 12 | "path/filepath" 13 | "strings" 14 | 15 | "github.com/visualfc/atk/tk/interp" 16 | ) 17 | 18 | type Image struct { 19 | id string 20 | photo *interp.Photo 21 | tk85alpha color.Color 22 | } 23 | 24 | func (i *Image) Id() string { 25 | return i.id 26 | } 27 | 28 | type ImageAttr struct { 29 | key string 30 | value interface{} 31 | } 32 | 33 | func ImageAttrGamma(gamma float64) *ImageAttr { 34 | return &ImageAttr{"gamma", gamma} 35 | } 36 | 37 | func ImageAttrTk85AlphaColor(color color.Color) *ImageAttr { 38 | return &ImageAttr{"tk85alphacolor", color} 39 | } 40 | 41 | func LoadImage(file string, attributes ...*ImageAttr) (*Image, error) { 42 | if file == "" { 43 | return nil, ErrInvalid 44 | } 45 | var fileImage image.Image 46 | if filepath.Ext(file) == ".gif" { 47 | attributes = append(attributes, &ImageAttr{"file", file}) 48 | } else { 49 | file, err := os.Open(file) 50 | if err != nil { 51 | return nil, err 52 | } 53 | im, _, err := image.Decode(file) 54 | file.Close() 55 | if err != nil { 56 | return nil, err 57 | } 58 | fileImage = im 59 | } 60 | im := NewImage(attributes...) 61 | if im == nil { 62 | return nil, errors.New("NewImage failed") 63 | } 64 | if fileImage != nil { 65 | im.SetImage(fileImage) 66 | } 67 | return im, nil 68 | } 69 | 70 | func NewImage(attributes ...*ImageAttr) *Image { 71 | var attrList []string 72 | var tk85alphacolor color.Color 73 | for _, attr := range attributes { 74 | if attr == nil { 75 | continue 76 | } 77 | if attr.key == "tk85alphacolor" { 78 | if clr, ok := attr.value.(color.Color); ok { 79 | tk85alphacolor = clr 80 | } 81 | continue 82 | } 83 | if s, ok := attr.value.(string); ok { 84 | pname := "atk_tmp_" + attr.key 85 | setObjText(pname, s) 86 | attrList = append(attrList, fmt.Sprintf("-%v $%v", attr.key, pname)) 87 | continue 88 | } 89 | attrList = append(attrList, fmt.Sprintf("-%v {%v}", attr.key, attr.value)) 90 | } 91 | iid := makeNamedId("atk_image") 92 | script := fmt.Sprintf("image create photo %v", iid) 93 | if len(attrList) > 0 { 94 | script += " " + strings.Join(attrList, " ") 95 | } 96 | err := eval(script) 97 | if err != nil { 98 | return nil 99 | } 100 | photo := interp.FindPhoto(mainInterp, iid) 101 | if photo == nil { 102 | return nil 103 | } 104 | return &Image{iid, photo, tk85alphacolor} 105 | } 106 | 107 | func (i *Image) IsValid() bool { 108 | return i.id != "" && i.photo != nil 109 | } 110 | 111 | func (i *Image) SetImage(img image.Image) *Image { 112 | err := i.photo.PutImage(img, i.tk85alpha) 113 | if err != nil { 114 | dumpError(err) 115 | } 116 | return i 117 | } 118 | 119 | func (i *Image) SetZoomedImage(img image.Image, zoomX, zoomY, subsampleX, subsampleY int) *Image { 120 | err := i.photo.PutZoomedImage(img, zoomX, zoomY, subsampleX, subsampleY, i.tk85alpha) 121 | if err != nil { 122 | dumpError(err) 123 | } 124 | return i 125 | } 126 | 127 | func (i *Image) ToImage() image.Image { 128 | return i.photo.ToImage() 129 | } 130 | 131 | func (i *Image) Blank() *Image { 132 | i.photo.Blank() 133 | return i 134 | } 135 | 136 | func (i *Image) SizeN() (width int, height int) { 137 | return i.photo.Size() 138 | } 139 | 140 | func (i *Image) Size() Size { 141 | w, h := i.SizeN() 142 | return Size{w, h} 143 | } 144 | 145 | func (i *Image) SetSizeN(width int, height int) *Image { 146 | err := i.photo.SetSize(width, height) 147 | if err != nil { 148 | dumpError(err) 149 | } 150 | return i 151 | } 152 | 153 | func (i *Image) SetSize(sz Size) *Image { 154 | return i.SetSizeN(sz.Width, sz.Height) 155 | } 156 | 157 | func (i *Image) Gamma() float64 { 158 | v, _ := evalAsFloat64(fmt.Sprintf("%v cget -gamma", i.id)) 159 | return v 160 | } 161 | 162 | func (i *Image) SetGamma(v float64) *Image { 163 | eval(fmt.Sprintf("%v configure -gamma {%v}", i.id, v)) 164 | return i 165 | } 166 | 167 | func parserImageResult(id string, err error) *Image { 168 | if err != nil { 169 | return nil 170 | } 171 | photo := interp.FindPhoto(mainInterp, id) 172 | if photo == nil { 173 | return nil 174 | } 175 | return &Image{id, photo, nil} 176 | } 177 | -------------------------------------------------------------------------------- /tk/interp/cbytes_go16_unix.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | // +build !go1.7,!windows 4 | 5 | package interp 6 | 7 | import ( 8 | "unsafe" 9 | ) 10 | 11 | /* 12 | #include 13 | #include 14 | */ 15 | import "C" 16 | 17 | func toCBytes(data []byte) unsafe.Pointer { 18 | size := C.size_t(len(data)) 19 | ptr := C.malloc(size) 20 | C.memcpy(ptr, unsafe.Pointer(&data[0]), size) 21 | return ptr 22 | } 23 | -------------------------------------------------------------------------------- /tk/interp/cbytes_unix.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | // +build go1.7,!windows 4 | 5 | package interp 6 | 7 | import ( 8 | "unsafe" 9 | ) 10 | 11 | import "C" 12 | 13 | func toCBytes(data []byte) unsafe.Pointer { 14 | return C.CBytes(data) 15 | } 16 | -------------------------------------------------------------------------------- /tk/interp/interp.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package interp 4 | 5 | import ( 6 | "errors" 7 | "strconv" 8 | ) 9 | 10 | const ( 11 | TCL_OK = 0 12 | TCL_ERROR = 1 13 | ) 14 | 15 | type Tcl_QueuePosition int 16 | 17 | const ( 18 | TCL_QUEUE_TAIL Tcl_QueuePosition = 0 19 | TCL_QUEUE_HEAD 20 | TCL_QUEUE_MARK 21 | ) 22 | 23 | const ( 24 | TCL_DONT_WAIT = 1 << 1 25 | TCL_WINDOW_EVENTS = 1 << 2 26 | TCL_FILE_EVENTS = 1 << 3 27 | TCL_TIMER_EVENTS = 1 << 4 28 | TCL_IDLE_EVENTS = 1 << 5 29 | TCL_ALL_EVENTS = ^TCL_DONT_WAIT 30 | ) 31 | 32 | var ( 33 | globalCommandMap = NewCommandMap() 34 | globalActionMap = NewActionMap() 35 | ) 36 | 37 | type ActionMap struct { 38 | fnMap map[uintptr]func([]string) 39 | id uintptr 40 | } 41 | 42 | func NewActionMap() *ActionMap { 43 | return &ActionMap{make(map[uintptr]func([]string)), 1} 44 | } 45 | 46 | func (m *ActionMap) Register(fn func([]string)) uintptr { 47 | m.id = m.id + 1 48 | m.fnMap[m.id] = fn 49 | return m.id 50 | } 51 | 52 | func (m *ActionMap) UnRegister(id uintptr) { 53 | delete(m.fnMap, id) 54 | } 55 | 56 | func (m *ActionMap) Invoke(id uintptr, args []string) error { 57 | fn, ok := m.fnMap[id] 58 | if !ok { 59 | return errors.New("Not found action") 60 | } 61 | fn(args) 62 | return nil 63 | } 64 | 65 | type CommandMap struct { 66 | fnMap map[uintptr]func([]string) (string, error) 67 | id uintptr 68 | } 69 | 70 | func (m *CommandMap) Register(fn func([]string) (string, error)) uintptr { 71 | m.id = m.id + 1 72 | m.fnMap[m.id] = fn 73 | return m.id 74 | } 75 | 76 | func (m *CommandMap) UnRegister(id uintptr) { 77 | delete(m.fnMap, id) 78 | } 79 | 80 | func (m *CommandMap) Find(id uintptr) func([]string) (string, error) { 81 | return m.fnMap[id] 82 | } 83 | 84 | func (m *CommandMap) Invoke(id uintptr, args []string) (string, error) { 85 | fn, ok := m.fnMap[id] 86 | if !ok { 87 | return "", errors.New("Not found command") 88 | } 89 | return fn(args) 90 | } 91 | 92 | func NewCommandMap() *CommandMap { 93 | return &CommandMap{make(map[uintptr]func([]string) (string, error)), 1} 94 | } 95 | 96 | func (interp *Interp) EvalAsString(script string) (string, error) { 97 | err := interp.Eval(script) 98 | if err != nil { 99 | return "", err 100 | } 101 | return interp.GetStringResult(), nil 102 | } 103 | 104 | func (interp *Interp) EvalAsInt64(script string) (int64, error) { 105 | err := interp.Eval(script) 106 | if err != nil { 107 | return 0, err 108 | } 109 | return interp.GetInt64Result(), nil 110 | } 111 | 112 | func (interp *Interp) EvalAsInt(script string) (int, error) { 113 | err := interp.Eval(script) 114 | if err != nil { 115 | return 0, err 116 | } 117 | return interp.GetIntResult(), nil 118 | } 119 | 120 | func (interp *Interp) EvalAsUint(script string) (uint, error) { 121 | err := interp.Eval(script) 122 | if err != nil { 123 | return 0, err 124 | } 125 | return interp.GetUintResult(), nil 126 | } 127 | 128 | func (interp *Interp) EvalAsFloat64(script string) (float64, error) { 129 | err := interp.Eval(script) 130 | if err != nil { 131 | return 0, err 132 | } 133 | return interp.GetFloat64Result(), nil 134 | } 135 | 136 | func (interp *Interp) EvalAsBool(script string) (bool, error) { 137 | err := interp.Eval(script) 138 | if err != nil { 139 | return false, err 140 | } 141 | return interp.GetBoolResult(), nil 142 | } 143 | 144 | func (interp *Interp) EvalAsObj(script string) (*Obj, error) { 145 | err := interp.Eval(script) 146 | if err != nil { 147 | return nil, err 148 | } 149 | return interp.GetObjResult(), nil 150 | } 151 | 152 | func (interp *Interp) EvalAsListObj(script string) (*ListObj, error) { 153 | err := interp.Eval(script) 154 | if err != nil { 155 | return nil, err 156 | } 157 | return interp.GetListObjResult(), nil 158 | } 159 | 160 | func (interp *Interp) EvalAsStringList(script string) ([]string, error) { 161 | err := interp.Eval(script) 162 | if err != nil { 163 | return nil, err 164 | } 165 | return interp.GetListObjResult().ToStringList(), nil 166 | } 167 | 168 | func (interp *Interp) EvalAsIntList(script string) ([]int, error) { 169 | err := interp.Eval(script) 170 | if err != nil { 171 | return nil, err 172 | } 173 | return interp.GetListObjResult().ToIntList(), nil 174 | } 175 | 176 | func (interp *Interp) TclVersion() string { 177 | ver, _ := interp.EvalAsString("set tcl_version") 178 | return ver 179 | } 180 | 181 | func (interp *Interp) TclPatchLevel() string { 182 | ver, _ := interp.EvalAsString("set tcl_patchLevel") 183 | return ver 184 | } 185 | 186 | func (interp *Interp) TkVersion() string { 187 | ver, _ := interp.EvalAsString("set tk_version") 188 | return ver 189 | } 190 | 191 | func (interp *Interp) TkPatchLevel() string { 192 | ver, _ := interp.EvalAsString("set tk_patchLevel") 193 | return ver 194 | } 195 | 196 | func (p *Interp) GetStringResult() string { 197 | return p.GetObjResult().ToString() 198 | } 199 | 200 | func (p *Interp) GetIntResult() int { 201 | return p.GetObjResult().ToInt() 202 | } 203 | 204 | func (p *Interp) GetUintResult() uint { 205 | return p.GetObjResult().ToUint() 206 | } 207 | 208 | func (p *Interp) GetInt64Result() int64 { 209 | return p.GetObjResult().ToInt64() 210 | } 211 | 212 | func (p *Interp) GetFloat64Result() float64 { 213 | return p.GetObjResult().ToFloat64() 214 | } 215 | 216 | func (p *Interp) GetBoolResult() bool { 217 | return p.GetObjResult().ToBool() 218 | } 219 | 220 | func (p *Interp) GetErrorResult() error { 221 | return errors.New(p.GetObjResult().ToString()) 222 | } 223 | 224 | func (p *Interp) GetStringVar(name string, global bool) string { 225 | obj := p.GetVar(name, global) 226 | if obj == nil { 227 | return "" 228 | } 229 | return obj.ToString() 230 | } 231 | 232 | func (p *Interp) GetIntVar(name string, global bool) int { 233 | obj := p.GetVar(name, global) 234 | if obj == nil { 235 | return 0 236 | } 237 | return obj.ToInt() 238 | } 239 | 240 | func (p *Interp) GetInt64Var(name string, global bool) int64 { 241 | obj := p.GetVar(name, global) 242 | if obj == nil { 243 | return 0 244 | } 245 | return obj.ToInt64() 246 | } 247 | 248 | func (p *Interp) GetFloadt64Var(name string, global bool) float64 { 249 | obj := p.GetVar(name, global) 250 | if obj == nil { 251 | return 0 252 | } 253 | return obj.ToFloat64() 254 | } 255 | 256 | func (p *Interp) GetBoolVar(name string, global bool) bool { 257 | obj := p.GetVar(name, global) 258 | if obj == nil { 259 | return false 260 | } 261 | return obj.ToBool() 262 | } 263 | 264 | func (p *Interp) SetIntVar(name string, value int, global bool) error { 265 | return p.SetStringVar(name, strconv.Itoa(value), global) 266 | } 267 | 268 | func (p *Interp) SetInt64Var(name string, value int64, global bool) error { 269 | return p.SetStringVar(name, strconv.FormatInt(value, 10), global) 270 | } 271 | 272 | func (p *Interp) SetFloat64Var(name string, value float64, global bool) error { 273 | return p.SetStringVar(name, strconv.FormatFloat(value, 'E', -1, 64), global) 274 | } 275 | 276 | func (p *Interp) SetBoolVar(name string, b bool, global bool) error { 277 | if b { 278 | return p.SetStringVar(name, "1", global) 279 | } 280 | return p.SetStringVar(name, "0", global) 281 | } 282 | -------------------------------------------------------------------------------- /tk/interp/interp_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package interp 4 | 5 | import ( 6 | "fmt" 7 | "log" 8 | "math" 9 | "strconv" 10 | "strings" 11 | "testing" 12 | "time" 13 | ) 14 | 15 | var ( 16 | interp *Interp 17 | ) 18 | 19 | func init() { 20 | var err error 21 | interp, err = NewInterp() 22 | if err != nil { 23 | panic(err) 24 | } 25 | err = interp.InitTcl("") 26 | if err != nil { 27 | log.Println(err) 28 | } 29 | fmt.Println("tcl version", interp.TclPatchLevel()) 30 | err = interp.InitTk("") 31 | if err != nil { 32 | log.Println(err) 33 | } 34 | fmt.Println("tk version", interp.TkPatchLevel()) 35 | } 36 | 37 | func TestInterp(t *testing.T) { 38 | a, err := interp.EvalAsString("set a {hello world}\nset a") 39 | if err != nil { 40 | t.Fatal(err) 41 | } 42 | if a != "hello world" { 43 | t.Fatal("EvalAsString", a) 44 | } 45 | b, err := interp.EvalAsInt64(fmt.Sprintf("set b %v\nexpr $b", int64(math.MaxInt64))) 46 | if err != nil { 47 | t.Fatal(err) 48 | } 49 | if b != int64(math.MaxInt64) { 50 | t.Fatal("EvalAsInt64", b) 51 | } 52 | c, err := interp.EvalAsInt("set c 100\nexpr $c") 53 | if err != nil { 54 | t.Fatal(err) 55 | } 56 | if c != 100 { 57 | t.Fatal("EvalAsInt") 58 | } 59 | d, err := interp.EvalAsFloat64("set d 1e12\nexpr $d") 60 | if err != nil { 61 | t.Fatal(err) 62 | } 63 | if d != 1e12 { 64 | t.Fatal("EvalAsFloat64", d) 65 | } 66 | } 67 | 68 | func TestVar(t *testing.T) { 69 | var err error 70 | err = interp.SetStringVar("a", "$hello world {", true) 71 | if err != nil { 72 | t.Fatal(err) 73 | } 74 | if r := interp.GetStringVar("a", true); r != "$hello world {" { 75 | t.Fatal("string", r) 76 | } 77 | err = interp.AppendStringVar("a", "$ok}\"", true) 78 | if r := interp.GetStringVar("a", true); r != "$hello world {$ok}\"" { 79 | t.Fatal("string", r) 80 | } 81 | interp.SetIntVar("a", 100, true) 82 | if r := interp.GetIntVar("a", true); r != 100 { 83 | t.Fatal("int", r) 84 | } 85 | interp.SetFloat64Var("a", 1.123456789e9, true) 86 | if r := interp.GetFloadt64Var("a", true); r != 1.123456789e9 { 87 | t.Fatal("float64", r) 88 | } 89 | interp.SetBoolVar("a", true, true) 90 | if r := interp.GetBoolVar("a", true); r != true { 91 | t.Fatal("bool", r) 92 | } 93 | err = interp.UnsetVar("a", true) 94 | if err != nil { 95 | t.Fatal(err) 96 | } 97 | 98 | lst := NewListObj(interp) 99 | lst.AppendStringList([]string{"123", "OK {$ok"}) 100 | if v := lst.Length(); v != 2 { 101 | t.Fatal("length", v) 102 | } 103 | lst.SetStringList([]string{"abc", "中文 $var\t{", "{}$OK"}) 104 | if v := lst.Length(); v != 3 { 105 | t.Fatal("SetStringList", v) 106 | } 107 | lst.AppendString("$OK") 108 | lst.AppendStringList([]string{"end"}) 109 | if v := lst.Length(); v != 5 { 110 | t.Fatal("AppendStringList", v) 111 | } 112 | if v := lst.IndexString(1); v != "中文 $var\t{" { 113 | t.Fatal("IndexString", v) 114 | } 115 | lst.InsertString(0, "first") 116 | if v := lst.Length(); v != 6 { 117 | t.Fatal("InsertString", v) 118 | } 119 | if v := lst.IndexString(0); v != "first" { 120 | t.Fatal("IndexString", v) 121 | } 122 | lst.SetIndexObj(0, nil) 123 | lst.SetIndexString(0, "update") 124 | if v := lst.IndexString(0); v != "update" { 125 | t.Fatal("SetIndexString", v) 126 | } 127 | lst.Remove(1, 2) 128 | if v := lst.Length(); v != 4 { 129 | t.Fatal("Remove", v) 130 | } 131 | } 132 | 133 | func TestCommand(t *testing.T) { 134 | interp.CreateCommand("go::join", func(args []string) (string, error) { 135 | return strings.Join(args, ","), nil 136 | }) 137 | s, err := interp.EvalAsString("go::join hello world") 138 | if err != nil { 139 | t.Fatal(err, s) 140 | } 141 | if s != "hello,world" { 142 | t.Fatal(s) 143 | } 144 | interp.CreateCommand("go::sum", func(args []string) (string, error) { 145 | var sum int 146 | for _, arg := range args { 147 | i, err := strconv.Atoi(arg) 148 | if err != nil { 149 | return "", err 150 | } 151 | sum += i 152 | } 153 | return strconv.Itoa(sum), nil 154 | }) 155 | sum, err := interp.EvalAsInt("expr [go::sum 100 200 300]") 156 | if err != nil { 157 | t.Fatal(err) 158 | } 159 | if sum != 600 { 160 | t.Fatal("CreateCommand") 161 | } 162 | var check_success bool 163 | interp.CreateAction("go::action", func(args []string) { 164 | check_success = true 165 | }) 166 | err = interp.Eval("go::action") 167 | if err != nil { 168 | t.Fatal(err) 169 | } 170 | if !check_success { 171 | t.Fatal("CreateAction") 172 | } 173 | } 174 | 175 | func TestObj(t *testing.T) { 176 | if NewStringObj("string", interp).ToString() != "string" { 177 | t.Fatal("string obj") 178 | } 179 | if f := NewFloat64Obj(math.MaxFloat64, interp).ToFloat64(); f != math.MaxFloat64 { 180 | t.Fatal("float64 obj", f) 181 | } 182 | if f := NewFloat64Obj(-math.MaxFloat64, interp).ToFloat64(); f != -math.MaxFloat64 { 183 | t.Fatal("float64 obj", f) 184 | } 185 | if f := NewFloat64Obj(1.123456789123456789, interp).ToFloat64(); f != 1.123456789123456789 { 186 | t.Fatal("float64 obj", f) 187 | } 188 | if f := NewInt64Obj(math.MaxInt64, interp).ToInt64(); f != math.MaxInt64 { 189 | t.Fatal("int64 obj", f) 190 | } 191 | if f := NewInt64Obj(math.MinInt64, interp).ToInt64(); f != math.MinInt64 { 192 | t.Fatal("int64 obj", f) 193 | } 194 | if f := NewIntObj(math.MaxInt32, interp).ToInt(); f != math.MaxInt32 { 195 | t.Fatal("int obj", f) 196 | } 197 | if f := NewIntObj(math.MinInt32, interp).ToInt(); f != math.MinInt32 { 198 | t.Fatal("int obj", f) 199 | } 200 | if NewBoolObj(true, interp).ToBool() != true { 201 | t.Fatal("bool boj") 202 | } 203 | } 204 | 205 | func TestPhoto(t *testing.T) { 206 | err := interp.Eval("image create photo myimg -file $tk_library/images/pwrdLogo200.gif") 207 | if err != nil { 208 | t.Log("skip test photo", err) 209 | return 210 | } 211 | photo := FindPhoto(interp, "myimg") 212 | if photo == nil { 213 | t.Fatal("FindPhoto") 214 | } 215 | w, h := photo.Size() 216 | if w != 130 || h != 200 { 217 | t.Fatal("Size", w, h) 218 | } 219 | err = photo.SetSize(100, 150) 220 | if err != nil { 221 | t.Fatal(err) 222 | } 223 | goImage := photo.ToImage() 224 | if goImage == nil { 225 | t.Fatal("ToImage") 226 | } 227 | err = interp.Eval("image create photo myimg2") 228 | if err != nil { 229 | t.Fatal("create photo false") 230 | } 231 | photo2 := FindPhoto(interp, "myimg2") 232 | if photo2 == nil { 233 | t.Fatal("FindPhoto") 234 | } 235 | err = photo2.PutImage(goImage, nil) 236 | if err != nil { 237 | t.Fatal(err) 238 | } 239 | err = photo2.PutZoomedImage(goImage, 1, 2, 3, 6, nil) 240 | if err != nil { 241 | t.Fatal(err) 242 | } 243 | w2, h2 := photo2.Size() 244 | if w2 != 100 || h2 != 150 { 245 | t.Fatal("Size") 246 | } 247 | } 248 | 249 | func TestTkSync(t *testing.T) { 250 | MainLoop(func() { 251 | go func() { 252 | fmt.Println("run tk mainloop wait 1 sec async destroy") 253 | <-time.After(1e9) 254 | Async(func() { 255 | interp.Destroy() 256 | }) 257 | }() 258 | }) 259 | } 260 | -------------------------------------------------------------------------------- /tk/interp/syncmap.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | // +build go1.9 4 | 5 | package interp 6 | 7 | import ( 8 | "sync" 9 | ) 10 | 11 | var ( 12 | globalAsyncEvent sync.Map 13 | ) 14 | -------------------------------------------------------------------------------- /tk/interp/syncmap_18.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | // +build !go1.9 4 | 5 | package interp 6 | 7 | import ( 8 | "golang.org/x/sync/syncmap" 9 | ) 10 | 11 | var ( 12 | globalAsyncEvent syncmap.Map 13 | ) 14 | -------------------------------------------------------------------------------- /tk/label.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import "fmt" 6 | 7 | // tk::label 8 | type Label struct { 9 | BaseWidget 10 | } 11 | 12 | func NewLabel(parent Widget, text string, attributes ...*WidgetAttr) *Label { 13 | theme := checkInitUseTheme(attributes) 14 | iid := makeNamedWidgetId(parent, "atk_label") 15 | attributes = append(attributes, &WidgetAttr{"text", text}) 16 | info := CreateWidgetInfo(iid, WidgetTypeLabel, theme, attributes) 17 | if info == nil { 18 | return nil 19 | } 20 | w := &Label{} 21 | w.id = iid 22 | w.info = info 23 | RegisterWidget(w) 24 | return w 25 | } 26 | 27 | func (w *Label) Attach(id string) error { 28 | info, err := CheckWidgetInfo(id, WidgetTypeLabel) 29 | if err != nil { 30 | return err 31 | } 32 | w.id = id 33 | w.info = info 34 | RegisterWidget(w) 35 | return nil 36 | } 37 | 38 | func (w *Label) SetBackground(color string) error { 39 | setObjText("atk_tmp_text", color) 40 | return eval(fmt.Sprintf("%v configure -background $atk_tmp_text", w.id)) 41 | } 42 | 43 | func (w *Label) Background() string { 44 | r, _ := evalAsString(fmt.Sprintf("%v cget -background", w.id)) 45 | return r 46 | } 47 | 48 | func (w *Label) SetBorderWidth(width int) error { 49 | return eval(fmt.Sprintf("%v configure -borderwidth {%v}", w.id, width)) 50 | } 51 | 52 | func (w *Label) BorderWidth() int { 53 | r, _ := evalAsInt(fmt.Sprintf("%v cget -borderwidth", w.id)) 54 | return r 55 | } 56 | 57 | func (w *Label) SetForground(color string) error { 58 | setObjText("atk_tmp_text", color) 59 | return eval(fmt.Sprintf("%v configure -foreground $atk_tmp_text", w.id)) 60 | } 61 | 62 | func (w *Label) Forground() string { 63 | r, _ := evalAsString(fmt.Sprintf("%v cget -foreground", w.id)) 64 | return r 65 | } 66 | 67 | func (w *Label) SetReliefStyle(relief ReliefStyle) error { 68 | return eval(fmt.Sprintf("%v configure -relief {%v}", w.id, relief)) 69 | } 70 | 71 | func (w *Label) ReliefStyle() ReliefStyle { 72 | r, err := evalAsString(fmt.Sprintf("%v cget -relief", w.id)) 73 | return parserReliefStyleResult(r, err) 74 | } 75 | 76 | func (w *Label) SetFont(font Font) error { 77 | if font == nil { 78 | return ErrInvalid 79 | } 80 | return eval(fmt.Sprintf("%v configure -font {%v}", w.id, font.Id())) 81 | } 82 | 83 | func (w *Label) Font() Font { 84 | r, err := evalAsString(fmt.Sprintf("%v cget -font", w.id)) 85 | return parserFontResult(r, err) 86 | } 87 | 88 | func (w *Label) SetAnchor(anchor Anchor) error { 89 | return eval(fmt.Sprintf("%v configure -anchor {%v}", w.id, anchor)) 90 | } 91 | 92 | func (w *Label) Anchor() Anchor { 93 | r, err := evalAsString(fmt.Sprintf("%v cget -anchor", w.id)) 94 | return parserAnchorResult(r, err) 95 | } 96 | 97 | func (w *Label) SetJustify(justify Justify) error { 98 | return eval(fmt.Sprintf("%v configure -justify {%v}", w.id, justify)) 99 | } 100 | 101 | func (w *Label) Justify() Justify { 102 | r, err := evalAsString(fmt.Sprintf("%v cget -justify", w.id)) 103 | return parserJustifyResult(r, err) 104 | } 105 | 106 | func (w *Label) SetWrapLength(wraplength int) error { 107 | return eval(fmt.Sprintf("%v configure -wraplength {%v}", w.id, wraplength)) 108 | } 109 | 110 | func (w *Label) WrapLength() int { 111 | r, _ := evalAsInt(fmt.Sprintf("%v cget -wraplength", w.id)) 112 | return r 113 | } 114 | 115 | func (w *Label) SetImage(image *Image) error { 116 | if image == nil { 117 | return ErrInvalid 118 | } 119 | return eval(fmt.Sprintf("%v configure -image {%v}", w.id, image.Id())) 120 | } 121 | 122 | func (w *Label) Image() *Image { 123 | r, err := evalAsString(fmt.Sprintf("%v cget -image", w.id)) 124 | return parserImageResult(r, err) 125 | } 126 | 127 | func (w *Label) SetCompound(compound Compound) error { 128 | return eval(fmt.Sprintf("%v configure -compound {%v}", w.id, compound)) 129 | } 130 | 131 | func (w *Label) Compound() Compound { 132 | r, err := evalAsString(fmt.Sprintf("%v cget -compound", w.id)) 133 | return parserCompoundResult(r, err) 134 | } 135 | 136 | func (w *Label) SetText(text string) error { 137 | setObjText("atk_tmp_text", text) 138 | return eval(fmt.Sprintf("%v configure -text $atk_tmp_text", w.id)) 139 | } 140 | 141 | func (w *Label) Text() string { 142 | r, _ := evalAsString(fmt.Sprintf("%v cget -text", w.id)) 143 | return r 144 | } 145 | 146 | func (w *Label) SetWidth(width int) error { 147 | return eval(fmt.Sprintf("%v configure -width {%v}", w.id, width)) 148 | } 149 | 150 | func (w *Label) Width() int { 151 | r, _ := evalAsInt(fmt.Sprintf("%v cget -width", w.id)) 152 | return r 153 | } 154 | 155 | func (w *Label) SetPaddingN(padx int, pady int) error { 156 | if w.info.IsTtk { 157 | return eval(fmt.Sprintf("%v configure -padding {%v %v}", w.id, padx, pady)) 158 | } 159 | return eval(fmt.Sprintf("%v configure -padx {%v} -pady {%v}", w.id, padx, pady)) 160 | } 161 | 162 | func (w *Label) PaddingN() (int, int) { 163 | var r string 164 | var err error 165 | if w.info.IsTtk { 166 | r, err = evalAsString(fmt.Sprintf("%v cget -padding", w.id)) 167 | } else { 168 | r1, _ := evalAsString(fmt.Sprintf("%v cget -padx", w.id)) 169 | r2, _ := evalAsString(fmt.Sprintf("%v cget -pady", w.id)) 170 | r = r1 + " " + r2 171 | } 172 | return parserPaddingResult(r, err) 173 | } 174 | 175 | func (w *Label) SetPadding(pad Pad) error { 176 | return w.SetPaddingN(pad.X, pad.Y) 177 | } 178 | 179 | func (w *Label) Padding() Pad { 180 | x, y := w.PaddingN() 181 | return Pad{x, y} 182 | } 183 | 184 | func (w *Label) SetState(state State) error { 185 | return eval(fmt.Sprintf("%v configure -state {%v}", w.id, state)) 186 | } 187 | 188 | func (w *Label) State() State { 189 | r, err := evalAsString(fmt.Sprintf("%v cget -state", w.id)) 190 | return parserStateResult(r, err) 191 | } 192 | 193 | func (w *Label) SetTakeFocus(takefocus bool) error { 194 | return eval(fmt.Sprintf("%v configure -takefocus {%v}", w.id, boolToInt(takefocus))) 195 | } 196 | 197 | func (w *Label) IsTakeFocus() bool { 198 | r, _ := evalAsBool(fmt.Sprintf("%v cget -takefocus", w.id)) 199 | return r 200 | } 201 | 202 | func LabelAttrBackground(color string) *WidgetAttr { 203 | return &WidgetAttr{"background", color} 204 | } 205 | 206 | func LabelAttrBorderWidth(width int) *WidgetAttr { 207 | return &WidgetAttr{"borderwidth", width} 208 | } 209 | 210 | func LabelAttrForground(color string) *WidgetAttr { 211 | return &WidgetAttr{"foreground", color} 212 | } 213 | 214 | func LabelAttrReliefStyle(relief ReliefStyle) *WidgetAttr { 215 | return &WidgetAttr{"relief", relief} 216 | } 217 | 218 | func LabelAttrFont(font Font) *WidgetAttr { 219 | if font == nil { 220 | return nil 221 | } 222 | return &WidgetAttr{"font", font.Id()} 223 | } 224 | 225 | func LabelAttrAnchor(anchor Anchor) *WidgetAttr { 226 | return &WidgetAttr{"anchor", anchor} 227 | } 228 | 229 | func LabelAttrJustify(justify Justify) *WidgetAttr { 230 | return &WidgetAttr{"justify", justify} 231 | } 232 | 233 | func LabelAttrWrapLength(wraplength int) *WidgetAttr { 234 | return &WidgetAttr{"wraplength", wraplength} 235 | } 236 | 237 | func LabelAttrImage(image *Image) *WidgetAttr { 238 | if image == nil { 239 | return nil 240 | } 241 | return &WidgetAttr{"image", image.Id()} 242 | } 243 | 244 | func LabelAttrCompound(compound Compound) *WidgetAttr { 245 | return &WidgetAttr{"compound", compound} 246 | } 247 | 248 | func LabelAttrText(text string) *WidgetAttr { 249 | return &WidgetAttr{"text", text} 250 | } 251 | 252 | func LabelAttrWidth(width int) *WidgetAttr { 253 | return &WidgetAttr{"width", width} 254 | } 255 | 256 | func LabelAttrPadding(padding Pad) *WidgetAttr { 257 | return &WidgetAttr{"padding", padding} 258 | } 259 | 260 | func LabelAttrState(state State) *WidgetAttr { 261 | return &WidgetAttr{"state", state} 262 | } 263 | 264 | func LabelAttrTakeFocus(takefocus bool) *WidgetAttr { 265 | return &WidgetAttr{"takefocus", boolToInt(takefocus)} 266 | } 267 | -------------------------------------------------------------------------------- /tk/label_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import "testing" 6 | 7 | func init() { 8 | registerTest("Label", testLabel) 9 | } 10 | 11 | func testLabel(t *testing.T) { 12 | w := NewLabel(nil, "test", LabelAttrBackground("blue"), LabelAttrBorderWidth(20), LabelAttrForground("blue"), LabelAttrReliefStyle(1), LabelAttrAnchor(AnchorCenter), LabelAttrJustify(1), LabelAttrWrapLength(20), LabelAttrCompound(CompoundNone), LabelAttrText("text"), LabelAttrWidth(20), LabelAttrState(StateNormal), LabelAttrTakeFocus(true)) 13 | defer w.Destroy() 14 | 15 | w.SetBackground("blue") 16 | if v := w.Background(); v != "blue" { 17 | t.Fatal("Background", "blue", v) 18 | } 19 | 20 | w.SetBorderWidth(20) 21 | if v := w.BorderWidth(); v != 20 { 22 | t.Fatal("BorderWidth", 20, v) 23 | } 24 | 25 | w.SetForground("blue") 26 | if v := w.Forground(); v != "blue" { 27 | t.Fatal("Forground", "blue", v) 28 | } 29 | 30 | w.SetReliefStyle(1) 31 | if v := w.ReliefStyle(); v != 1 { 32 | t.Fatal("ReliefStyle", 1, v) 33 | } 34 | 35 | w.SetAnchor(AnchorCenter) 36 | if v := w.Anchor(); v != AnchorCenter { 37 | t.Fatal("Anchor", AnchorCenter, v) 38 | } 39 | 40 | w.SetJustify(1) 41 | if v := w.Justify(); v != 1 { 42 | t.Fatal("Justify", 1, v) 43 | } 44 | 45 | w.SetWrapLength(20) 46 | if v := w.WrapLength(); v != 20 { 47 | t.Fatal("WrapLength", 20, v) 48 | } 49 | 50 | w.SetCompound(CompoundNone) 51 | if v := w.Compound(); v != CompoundNone { 52 | t.Fatal("Compound", CompoundNone, v) 53 | } 54 | 55 | w.SetText("text") 56 | if v := w.Text(); v != "text" { 57 | t.Fatal("Text", "text", v) 58 | } 59 | 60 | w.SetWidth(20) 61 | if v := w.Width(); v != 20 { 62 | t.Fatal("Width", 20, v) 63 | } 64 | 65 | w.SetState(StateNormal) 66 | if v := w.State(); v != StateNormal { 67 | t.Fatal("State", StateNormal, v) 68 | } 69 | 70 | w.SetTakeFocus(true) 71 | if v := w.IsTakeFocus(); v != true { 72 | t.Fatal("IsTakeFocus", true, v) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /tk/labelframe.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import "fmt" 6 | 7 | // label frame 8 | type LabelFrame struct { 9 | BaseWidget 10 | } 11 | 12 | func NewLabelFrame(parent Widget, attributes ...*WidgetAttr) *LabelFrame { 13 | theme := checkInitUseTheme(attributes) 14 | iid := makeNamedWidgetId(parent, "atk_labelframe") 15 | info := CreateWidgetInfo(iid, WidgetTypeLabelFrame, theme, attributes) 16 | if info == nil { 17 | return nil 18 | } 19 | w := &LabelFrame{} 20 | w.id = iid 21 | w.info = info 22 | RegisterWidget(w) 23 | return w 24 | } 25 | 26 | func (w *LabelFrame) Attach(id string) error { 27 | info, err := CheckWidgetInfo(id, WidgetTypeLabelFrame) 28 | if err != nil { 29 | return err 30 | } 31 | w.id = id 32 | w.info = info 33 | RegisterWidget(w) 34 | return nil 35 | } 36 | 37 | func (w *LabelFrame) SetLabelText(text string) error { 38 | setObjText("atk_tmp_text", text) 39 | return eval(fmt.Sprintf("%v configure -text $atk_tmp_text", w.id)) 40 | } 41 | 42 | func (w *LabelFrame) LabelText() string { 43 | r, _ := evalAsString(fmt.Sprintf("%v cget -text", w.id)) 44 | return r 45 | } 46 | 47 | func (w *LabelFrame) SetLabelAnchor(anchor Anchor) error { 48 | return eval(fmt.Sprintf("%v configure -labelanchor {%v}", w.id, anchor)) 49 | } 50 | 51 | func (w *LabelFrame) LabelAnchor() Anchor { 52 | r, err := evalAsString(fmt.Sprintf("%v cget -labelanchor", w.id)) 53 | return parserAnchorResult(r, err) 54 | } 55 | 56 | func (w *LabelFrame) SetBorderWidth(width int) error { 57 | return eval(fmt.Sprintf("%v configure -borderwidth {%v}", w.id, width)) 58 | } 59 | 60 | func (w *LabelFrame) BorderWidth() int { 61 | r, _ := evalAsInt(fmt.Sprintf("%v cget -borderwidth", w.id)) 62 | return r 63 | } 64 | 65 | func (w *LabelFrame) SetReliefStyle(relief ReliefStyle) error { 66 | return eval(fmt.Sprintf("%v configure -relief {%v}", w.id, relief)) 67 | } 68 | 69 | func (w *LabelFrame) ReliefStyle() ReliefStyle { 70 | r, err := evalAsString(fmt.Sprintf("%v cget -relief", w.id)) 71 | return parserReliefStyleResult(r, err) 72 | } 73 | 74 | func (w *LabelFrame) SetWidth(width int) error { 75 | return eval(fmt.Sprintf("%v configure -width {%v}", w.id, width)) 76 | } 77 | 78 | func (w *LabelFrame) Width() int { 79 | r, _ := evalAsInt(fmt.Sprintf("%v cget -width", w.id)) 80 | return r 81 | } 82 | 83 | func (w *LabelFrame) SetHeight(height int) error { 84 | return eval(fmt.Sprintf("%v configure -height {%v}", w.id, height)) 85 | } 86 | 87 | func (w *LabelFrame) Height() int { 88 | r, _ := evalAsInt(fmt.Sprintf("%v cget -height", w.id)) 89 | return r 90 | } 91 | 92 | func (w *LabelFrame) SetPaddingN(padx int, pady int) error { 93 | if w.info.IsTtk { 94 | return eval(fmt.Sprintf("%v configure -padding {%v %v}", w.id, padx, pady)) 95 | } 96 | return eval(fmt.Sprintf("%v configure -padx {%v} -pady {%v}", w.id, padx, pady)) 97 | } 98 | 99 | func (w *LabelFrame) PaddingN() (int, int) { 100 | var r string 101 | var err error 102 | if w.info.IsTtk { 103 | r, err = evalAsString(fmt.Sprintf("%v cget -padding", w.id)) 104 | } else { 105 | r1, _ := evalAsString(fmt.Sprintf("%v cget -padx", w.id)) 106 | r2, _ := evalAsString(fmt.Sprintf("%v cget -pady", w.id)) 107 | r = r1 + " " + r2 108 | } 109 | return parserPaddingResult(r, err) 110 | } 111 | 112 | func (w *LabelFrame) SetPadding(pad Pad) error { 113 | return w.SetPaddingN(pad.X, pad.Y) 114 | } 115 | 116 | func (w *LabelFrame) Padding() Pad { 117 | x, y := w.PaddingN() 118 | return Pad{x, y} 119 | } 120 | 121 | func (w *LabelFrame) SetTakeFocus(takefocus bool) error { 122 | return eval(fmt.Sprintf("%v configure -takefocus {%v}", w.id, boolToInt(takefocus))) 123 | } 124 | 125 | func (w *LabelFrame) IsTakeFocus() bool { 126 | r, _ := evalAsBool(fmt.Sprintf("%v cget -takefocus", w.id)) 127 | return r 128 | } 129 | 130 | func LabelFrameAttrLabelText(text string) *WidgetAttr { 131 | return &WidgetAttr{"text", text} 132 | } 133 | 134 | func LabelFrameAttrLabelAnchor(anchor Anchor) *WidgetAttr { 135 | return &WidgetAttr{"labelanchor", anchor} 136 | } 137 | 138 | func LabelFrameAttrBorderWidth(width int) *WidgetAttr { 139 | return &WidgetAttr{"borderwidth", width} 140 | } 141 | 142 | func LabelFrameAttrReliefStyle(relief ReliefStyle) *WidgetAttr { 143 | return &WidgetAttr{"relief", relief} 144 | } 145 | 146 | func LabelFrameAttrWidth(width int) *WidgetAttr { 147 | return &WidgetAttr{"width", width} 148 | } 149 | 150 | func LabelFrameAttrHeight(height int) *WidgetAttr { 151 | return &WidgetAttr{"height", height} 152 | } 153 | 154 | func LabelFrameAttrPadding(pad Pad) *WidgetAttr { 155 | return &WidgetAttr{"pad", pad} 156 | } 157 | 158 | func LabelFrameAttrTakeFocus(takefocus bool) *WidgetAttr { 159 | return &WidgetAttr{"takefocus", boolToInt(takefocus)} 160 | } 161 | -------------------------------------------------------------------------------- /tk/labelframe_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import "testing" 6 | 7 | func init() { 8 | registerTest("LabelFrame", testLabelFrame) 9 | } 10 | 11 | func testLabelFrame(t *testing.T) { 12 | w := NewLabelFrame(nil, LabelFrameAttrLabelText("text"), LabelFrameAttrLabelAnchor(AnchorNorthWest), LabelFrameAttrBorderWidth(20), LabelFrameAttrReliefStyle(1), LabelFrameAttrWidth(20), LabelFrameAttrHeight(20), LabelFrameAttrTakeFocus(true)) 13 | defer w.Destroy() 14 | 15 | w.SetLabelText("text") 16 | if v := w.LabelText(); v != "text" { 17 | t.Fatal("LabelText", "text", v) 18 | } 19 | 20 | w.SetLabelAnchor(AnchorNorthWest) 21 | if v := w.LabelAnchor(); v != AnchorNorthWest { 22 | t.Fatal("LabelAnchor", AnchorNorthWest, v) 23 | } 24 | 25 | w.SetBorderWidth(20) 26 | if v := w.BorderWidth(); v != 20 { 27 | t.Fatal("BorderWidth", 20, v) 28 | } 29 | 30 | w.SetReliefStyle(1) 31 | if v := w.ReliefStyle(); v != 1 { 32 | t.Fatal("ReliefStyle", 1, v) 33 | } 34 | 35 | w.SetWidth(20) 36 | if v := w.Width(); v != 20 { 37 | t.Fatal("Width", 20, v) 38 | } 39 | 40 | w.SetHeight(20) 41 | if v := w.Height(); v != 20 { 42 | t.Fatal("Height", 20, v) 43 | } 44 | 45 | w.SetTakeFocus(true) 46 | if v := w.IsTakeFocus(); v != true { 47 | t.Fatal("IsTakeFocus", true, v) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /tk/layout.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import "fmt" 6 | 7 | type LayoutWidget interface { 8 | Widget 9 | LayoutWidget() Widget 10 | } 11 | 12 | func checkLayoutWidget(widget Widget) Widget { 13 | if w, ok := (widget).(LayoutWidget); ok { 14 | return w.LayoutWidget() 15 | } 16 | return widget 17 | } 18 | 19 | type Layout interface { 20 | Widget 21 | AddWidget(widget Widget, attrs ...*LayoutAttr) error 22 | AddLayout(layout Layout, attrs ...*LayoutAttr) error 23 | RemoveWidget(widget Widget) error 24 | RemoveLayout(layout Layout) error 25 | } 26 | 27 | type LayoutAttr struct { 28 | Key string 29 | Value interface{} 30 | } 31 | 32 | type LayoutItem struct { 33 | widget Widget 34 | attrs []*LayoutAttr 35 | } 36 | 37 | type LayoutSpacer struct { 38 | BaseWidget 39 | space int 40 | expand bool 41 | } 42 | 43 | func (w *LayoutSpacer) Type() WidgetType { 44 | return WidgetTypeLayoutSpacer 45 | } 46 | 47 | func (w *LayoutSpacer) TypeName() string { 48 | return "LayoutSpacer" 49 | } 50 | 51 | func (w *LayoutSpacer) SetSpace(space int) error { 52 | w.space = space 53 | return nil 54 | } 55 | 56 | func (w *LayoutSpacer) Space() int { 57 | return w.space 58 | } 59 | 60 | func (w *LayoutSpacer) SetExpand(expand bool) error { 61 | w.expand = expand 62 | return nil 63 | } 64 | 65 | func (w *LayoutSpacer) IsExpand() bool { 66 | return w.expand 67 | } 68 | 69 | // width ignore for PackLayout 70 | func (w *LayoutSpacer) SetWidth(width int) error { 71 | return eval(fmt.Sprintf("%v configure -width {%v}", w.id, width)) 72 | } 73 | 74 | func (w *LayoutSpacer) Width() int { 75 | r, _ := evalAsInt(fmt.Sprintf("%v cget -width", w.id)) 76 | return r 77 | } 78 | 79 | // height ignore for PackLayout 80 | func (w *LayoutSpacer) SetHeight(height int) error { 81 | return eval(fmt.Sprintf("%v configure -height {%v}", w.id, height)) 82 | } 83 | 84 | func (w *LayoutSpacer) Height() int { 85 | r, _ := evalAsInt(fmt.Sprintf("%v cget -height", w.id)) 86 | return r 87 | } 88 | 89 | func NewLayoutSpacer(parent Widget, space int, expand bool) *LayoutSpacer { 90 | theme := checkInitUseTheme(nil) 91 | iid := makeNamedWidgetId(parent, "atk_layoutspacer") 92 | info := CreateWidgetInfo(iid, WidgetTypeFrame, theme, nil) 93 | if info == nil { 94 | return nil 95 | } 96 | w := &LayoutSpacer{} 97 | w.id = iid 98 | w.info = info 99 | w.space = space 100 | w.expand = expand 101 | RegisterWidget(w) 102 | return w 103 | } 104 | 105 | type LayoutFrame struct { 106 | BaseWidget 107 | } 108 | 109 | func (w *LayoutFrame) Type() WidgetType { 110 | return WidgetTypeLayoutFrame 111 | } 112 | 113 | func (w *LayoutFrame) TypeName() string { 114 | return "LayoutFrame" 115 | } 116 | 117 | func NewLayoutFrame(parent Widget, attributes ...*WidgetAttr) *LayoutFrame { 118 | theme := checkInitUseTheme(attributes) 119 | iid := makeNamedWidgetId(parent, "atk_layoutframe") 120 | info := CreateWidgetInfo(iid, WidgetTypeFrame, theme, attributes) 121 | if info == nil { 122 | return nil 123 | } 124 | w := &LayoutFrame{} 125 | w.id = iid 126 | w.info = info 127 | RegisterWidget(w) 128 | return w 129 | } 130 | 131 | func AppendLayoutAttrs(org []*LayoutAttr, attributes ...*LayoutAttr) []*LayoutAttr { 132 | var remain []*LayoutAttr 133 | var find bool 134 | for _, attr := range attributes { 135 | find = false 136 | for _, old := range org { 137 | if old.Key == attr.Key { 138 | old.Value = attr.Value 139 | find = true 140 | break 141 | } 142 | } 143 | if !find { 144 | remain = append(remain, attr) 145 | } 146 | } 147 | return append(org, remain...) 148 | } 149 | -------------------------------------------------------------------------------- /tk/listbox_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import "testing" 6 | 7 | func init() { 8 | registerTest("ListBox", testListBox) 9 | } 10 | 11 | func testListBox(t *testing.T) { 12 | w := NewListBox(nil, ListBoxAttrBackground("blue"), ListBoxAttrBorderWidth(20), ListBoxAttrForground("blue"), ListBoxAttrReliefStyle(1), ListBoxAttrJustify(1), ListBoxAttrWidth(20), ListBoxAttrHeight(20), ListBoxAttrState(StateNormal), ListBoxAttrSelectMode(ListSelectSingle), ListBoxAttrTakeFocus(true)) 13 | defer w.Destroy() 14 | 15 | w.SetBackground("blue") 16 | if v := w.Background(); v != "blue" { 17 | t.Fatal("Background", "blue", v) 18 | } 19 | 20 | w.SetBorderWidth(20) 21 | if v := w.BorderWidth(); v != 20 { 22 | t.Fatal("BorderWidth", 20, v) 23 | } 24 | 25 | w.SetForground("blue") 26 | if v := w.Forground(); v != "blue" { 27 | t.Fatal("Forground", "blue", v) 28 | } 29 | 30 | w.SetReliefStyle(1) 31 | if v := w.ReliefStyle(); v != 1 { 32 | t.Fatal("ReliefStyle", 1, v) 33 | } 34 | 35 | w.SetJustify(1) 36 | if v := w.Justify(); v != 1 { 37 | t.Fatal("Justify", 1, v) 38 | } 39 | 40 | w.SetWidth(20) 41 | if v := w.Width(); v != 20 { 42 | t.Fatal("Width", 20, v) 43 | } 44 | 45 | w.SetHeight(20) 46 | if v := w.Height(); v != 20 { 47 | t.Fatal("Height", 20, v) 48 | } 49 | 50 | w.SetState(StateNormal) 51 | if v := w.State(); v != StateNormal { 52 | t.Fatal("State", StateNormal, v) 53 | } 54 | 55 | w.SetSelectMode(ListSelectSingle) 56 | if v := w.SelectMode(); v != ListSelectSingle { 57 | t.Fatal("SelectMode", ListSelectSingle, v) 58 | } 59 | 60 | w.SetTakeFocus(true) 61 | if v := w.IsTakeFocus(); v != true { 62 | t.Fatal("IsTakeFocus", true, v) 63 | } 64 | 65 | w.SetState(StateNormal) 66 | w.SetItems([]string{"100", "$ok", "{300"}) 67 | if v := w.Items(); v[0] != "100" || v[1] != "$ok" || v[2] != "{300" { 68 | t.Fatal("values", v) 69 | } 70 | if v := w.ItemCount(); v != 3 { 71 | t.Fatal("SetItems", v) 72 | } 73 | w.InsertItem(0, "first") 74 | if v := w.ItemCount(); v != 4 { 75 | t.Fatal("InsertItem", v) 76 | } 77 | if v := w.ItemText(0); v != "first" { 78 | t.Fatal("InsertItem", v) 79 | } 80 | w.SetItemText(1, "$mm") 81 | if v := w.ItemText(1); v != "$mm" { 82 | t.Fatal("SetItemText", v) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /tk/menu_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import "testing" 6 | 7 | func init() { 8 | registerTest("Menu", testMenu) 9 | } 10 | 11 | func testMenu(t *testing.T) { 12 | w := NewMenu(nil, MenuAttrActiveBackground("blue"), MenuAttrActiveForground("blue"), MenuAttrBackground("blue"), MenuAttrForground("blue"), MenuAttrSelectColor("blue"), MenuAttrDisabledForground("blue"), MenuAttrActiveBorderWidth(20), MenuAttrBorderWidth(20), MenuAttrReliefStyle(1), MenuAttrTearoffTitle("text"), MenuAttrTearoff(true), MenuAttrTakeFocus(true)) 13 | defer w.Destroy() 14 | 15 | w.SetActiveBackground("blue") 16 | if v := w.ActiveBackground(); v != "blue" { 17 | t.Fatal("ActiveBackground", "blue", v) 18 | } 19 | 20 | w.SetActiveForground("blue") 21 | if v := w.ActiveForground(); v != "blue" { 22 | t.Fatal("ActiveForground", "blue", v) 23 | } 24 | 25 | w.SetBackground("blue") 26 | if v := w.Background(); v != "blue" { 27 | t.Fatal("Background", "blue", v) 28 | } 29 | 30 | w.SetForground("blue") 31 | if v := w.Forground(); v != "blue" { 32 | t.Fatal("Forground", "blue", v) 33 | } 34 | 35 | w.SetSelectColor("blue") 36 | if v := w.SelectColor(); v != "blue" { 37 | t.Fatal("SelectColor", "blue", v) 38 | } 39 | 40 | w.SetDisabledForground("blue") 41 | if v := w.DisabledForground(); v != "blue" { 42 | t.Fatal("DisabledForground", "blue", v) 43 | } 44 | 45 | w.SetActiveBorderWidth(20) 46 | if v := w.ActiveBorderWidth(); v != 20 { 47 | t.Fatal("ActiveBorderWidth", 20, v) 48 | } 49 | 50 | w.SetBorderWidth(20) 51 | if v := w.BorderWidth(); v != 20 { 52 | t.Fatal("BorderWidth", 20, v) 53 | } 54 | 55 | w.SetReliefStyle(1) 56 | if v := w.ReliefStyle(); v != 1 { 57 | t.Fatal("ReliefStyle", 1, v) 58 | } 59 | 60 | w.SetTearoffTitle("text") 61 | if v := w.TearoffTitle(); v != "text" { 62 | t.Fatal("TearoffTitle", "text", v) 63 | } 64 | 65 | w.SetTearoff(true) 66 | if v := w.IsTearoff(); v != true { 67 | t.Fatal("IsTearoff", true, v) 68 | } 69 | 70 | w.SetTakeFocus(true) 71 | if v := w.IsTakeFocus(); v != true { 72 | t.Fatal("IsTakeFocus", true, v) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /tk/menubutton.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import "fmt" 6 | 7 | // menubutton 8 | type MenuButton struct { 9 | BaseWidget 10 | } 11 | 12 | func NewMenuButton(parent Widget, text string, attributes ...*WidgetAttr) *MenuButton { 13 | theme := checkInitUseTheme(attributes) 14 | iid := makeNamedWidgetId(parent, "atk_menubutton") 15 | attributes = append(attributes, &WidgetAttr{"text", text}) 16 | info := CreateWidgetInfo(iid, WidgetTypeMenuButton, theme, attributes) 17 | if info == nil { 18 | return nil 19 | } 20 | w := &MenuButton{} 21 | w.id = iid 22 | w.info = info 23 | RegisterWidget(w) 24 | return w 25 | } 26 | 27 | func (w *MenuButton) Attach(id string) error { 28 | info, err := CheckWidgetInfo(id, WidgetTypeMenuButton) 29 | if err != nil { 30 | return err 31 | } 32 | w.id = id 33 | w.info = info 34 | RegisterWidget(w) 35 | return nil 36 | } 37 | 38 | func (w *MenuButton) SetText(text string) error { 39 | setObjText("atk_tmp_text", text) 40 | return eval(fmt.Sprintf("%v configure -text $atk_tmp_text", w.id)) 41 | } 42 | 43 | func (w *MenuButton) Text() string { 44 | r, _ := evalAsString(fmt.Sprintf("%v cget -text", w.id)) 45 | return r 46 | } 47 | 48 | func (w *MenuButton) SetWidth(width int) error { 49 | return eval(fmt.Sprintf("%v configure -width {%v}", w.id, width)) 50 | } 51 | 52 | func (w *MenuButton) Width() int { 53 | r, _ := evalAsInt(fmt.Sprintf("%v cget -width", w.id)) 54 | return r 55 | } 56 | 57 | func (w *MenuButton) SetImage(image *Image) error { 58 | if image == nil { 59 | return ErrInvalid 60 | } 61 | return eval(fmt.Sprintf("%v configure -image {%v}", w.id, image.Id())) 62 | } 63 | 64 | func (w *MenuButton) Image() *Image { 65 | r, err := evalAsString(fmt.Sprintf("%v cget -image", w.id)) 66 | return parserImageResult(r, err) 67 | } 68 | 69 | func (w *MenuButton) SetCompound(compound Compound) error { 70 | return eval(fmt.Sprintf("%v configure -compound {%v}", w.id, compound)) 71 | } 72 | 73 | func (w *MenuButton) Compound() Compound { 74 | r, err := evalAsString(fmt.Sprintf("%v cget -compound", w.id)) 75 | return parserCompoundResult(r, err) 76 | } 77 | 78 | func (w *MenuButton) SetPaddingN(padx int, pady int) error { 79 | if w.info.IsTtk { 80 | return eval(fmt.Sprintf("%v configure -padding {%v %v}", w.id, padx, pady)) 81 | } 82 | return eval(fmt.Sprintf("%v configure -padx {%v} -pady {%v}", w.id, padx, pady)) 83 | } 84 | 85 | func (w *MenuButton) PaddingN() (int, int) { 86 | var r string 87 | var err error 88 | if w.info.IsTtk { 89 | r, err = evalAsString(fmt.Sprintf("%v cget -padding", w.id)) 90 | } else { 91 | r1, _ := evalAsString(fmt.Sprintf("%v cget -padx", w.id)) 92 | r2, _ := evalAsString(fmt.Sprintf("%v cget -pady", w.id)) 93 | r = r1 + " " + r2 94 | } 95 | return parserPaddingResult(r, err) 96 | } 97 | 98 | func (w *MenuButton) SetPadding(pad Pad) error { 99 | return w.SetPaddingN(pad.X, pad.Y) 100 | } 101 | 102 | func (w *MenuButton) Padding() Pad { 103 | x, y := w.PaddingN() 104 | return Pad{x, y} 105 | } 106 | 107 | func (w *MenuButton) SetState(state State) error { 108 | return eval(fmt.Sprintf("%v configure -state {%v}", w.id, state)) 109 | } 110 | 111 | func (w *MenuButton) State() State { 112 | r, err := evalAsString(fmt.Sprintf("%v cget -state", w.id)) 113 | return parserStateResult(r, err) 114 | } 115 | 116 | func (w *MenuButton) SetTakeFocus(takefocus bool) error { 117 | return eval(fmt.Sprintf("%v configure -takefocus {%v}", w.id, boolToInt(takefocus))) 118 | } 119 | 120 | func (w *MenuButton) IsTakeFocus() bool { 121 | r, _ := evalAsBool(fmt.Sprintf("%v cget -takefocus", w.id)) 122 | return r 123 | } 124 | 125 | func (w *MenuButton) SetDirection(direction Direction) error { 126 | return eval(fmt.Sprintf("%v configure -direction {%v}", w.id, direction)) 127 | } 128 | 129 | func (w *MenuButton) Direction() Direction { 130 | r, err := evalAsString(fmt.Sprintf("%v cget -direction", w.id)) 131 | return parserDirectionResult(r, err) 132 | } 133 | 134 | func (w *MenuButton) SetMenu(menu *Menu) error { 135 | if menu == nil { 136 | return ErrInvalid 137 | } 138 | return eval(fmt.Sprintf("%v configure -menu {%v}", w.id, menu.Id())) 139 | } 140 | 141 | func (w *MenuButton) Menu() *Menu { 142 | r, err := evalAsString(fmt.Sprintf("%v cget -menu", w.id)) 143 | return parserMenuResult(r, err) 144 | } 145 | 146 | func MenuButtonAttrText(text string) *WidgetAttr { 147 | return &WidgetAttr{"text", text} 148 | } 149 | 150 | func MenuButtonAttrWidth(width int) *WidgetAttr { 151 | return &WidgetAttr{"width", width} 152 | } 153 | 154 | func MenuButtonAttrImage(image *Image) *WidgetAttr { 155 | if image == nil { 156 | return nil 157 | } 158 | return &WidgetAttr{"image", image.Id()} 159 | } 160 | 161 | func MenuButtonAttrCompound(compound Compound) *WidgetAttr { 162 | return &WidgetAttr{"compound", compound} 163 | } 164 | 165 | func MenuButtonAttrPadding(padding Pad) *WidgetAttr { 166 | return &WidgetAttr{"padding", padding} 167 | } 168 | 169 | func MenuButtonAttrState(state State) *WidgetAttr { 170 | return &WidgetAttr{"state", state} 171 | } 172 | 173 | func MenuButtonAttrTakeFocus(takefocus bool) *WidgetAttr { 174 | return &WidgetAttr{"takefocus", boolToInt(takefocus)} 175 | } 176 | 177 | func MenuButtonAttrDirection(direction Direction) *WidgetAttr { 178 | return &WidgetAttr{"direction", direction} 179 | } 180 | 181 | func MenuButtonAttrMenu(menu *Menu) *WidgetAttr { 182 | if menu == nil { 183 | return nil 184 | } 185 | return &WidgetAttr{"menu", menu.Id()} 186 | } 187 | -------------------------------------------------------------------------------- /tk/menubutton_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import "testing" 6 | 7 | func init() { 8 | registerTest("MenuButton", testMenuButton) 9 | } 10 | 11 | func testMenuButton(t *testing.T) { 12 | w := NewMenuButton(nil, "test", MenuButtonAttrText("text"), MenuButtonAttrWidth(20), MenuButtonAttrCompound(CompoundNone), MenuButtonAttrState(StateNormal), MenuButtonAttrTakeFocus(true), MenuButtonAttrDirection(DirectionBelow)) 13 | defer w.Destroy() 14 | 15 | w.SetText("text") 16 | if v := w.Text(); v != "text" { 17 | t.Fatal("Text", "text", v) 18 | } 19 | 20 | w.SetWidth(20) 21 | if v := w.Width(); v != 20 { 22 | t.Fatal("Width", 20, v) 23 | } 24 | 25 | w.SetCompound(CompoundNone) 26 | if v := w.Compound(); v != CompoundNone { 27 | t.Fatal("Compound", CompoundNone, v) 28 | } 29 | 30 | w.SetState(StateNormal) 31 | if v := w.State(); v != StateNormal { 32 | t.Fatal("State", StateNormal, v) 33 | } 34 | 35 | w.SetTakeFocus(true) 36 | if v := w.IsTakeFocus(); v != true { 37 | t.Fatal("IsTakeFocus", true, v) 38 | } 39 | 40 | w.SetDirection(DirectionBelow) 41 | if v := w.Direction(); v != DirectionBelow { 42 | t.Fatal("Direction", DirectionBelow, v) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /tk/notebook.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import ( 6 | "fmt" 7 | "strings" 8 | ) 9 | 10 | // notebook 11 | type Notebook struct { 12 | BaseWidget 13 | } 14 | 15 | func NewNotebook(parent Widget, attributes ...*WidgetAttr) *Notebook { 16 | theme := checkInitUseTheme(attributes) 17 | iid := makeNamedWidgetId(parent, "atk_notebook") 18 | info := CreateWidgetInfo(iid, WidgetTypeNotebook, theme, attributes) 19 | if info == nil { 20 | return nil 21 | } 22 | w := &Notebook{} 23 | w.id = iid 24 | w.info = info 25 | RegisterWidget(w) 26 | return w 27 | } 28 | 29 | func (w *Notebook) Attach(id string) error { 30 | info, err := CheckWidgetInfo(id, WidgetTypeNotebook) 31 | if err != nil { 32 | return err 33 | } 34 | w.id = id 35 | w.info = info 36 | RegisterWidget(w) 37 | return nil 38 | } 39 | 40 | func (w *Notebook) SetWidth(width int) error { 41 | return eval(fmt.Sprintf("%v configure -width {%v}", w.id, width)) 42 | } 43 | 44 | func (w *Notebook) Width() int { 45 | r, _ := evalAsInt(fmt.Sprintf("%v cget -width", w.id)) 46 | return r 47 | } 48 | 49 | func (w *Notebook) SetHeight(height int) error { 50 | return eval(fmt.Sprintf("%v configure -height {%v}", w.id, height)) 51 | } 52 | 53 | func (w *Notebook) Height() int { 54 | r, _ := evalAsInt(fmt.Sprintf("%v cget -height", w.id)) 55 | return r 56 | } 57 | 58 | func (w *Notebook) SetTakeFocus(takefocus bool) error { 59 | return eval(fmt.Sprintf("%v configure -takefocus {%v}", w.id, boolToInt(takefocus))) 60 | } 61 | 62 | func (w *Notebook) IsTakeFocus() bool { 63 | r, _ := evalAsBool(fmt.Sprintf("%v cget -takefocus", w.id)) 64 | return r 65 | } 66 | 67 | func (w *Notebook) SetPaddingN(padx int, pady int) error { 68 | if w.info.IsTtk { 69 | return eval(fmt.Sprintf("%v configure -padding {%v %v}", w.id, padx, pady)) 70 | } 71 | return eval(fmt.Sprintf("%v configure -padx {%v} -pady {%v}", w.id, padx, pady)) 72 | } 73 | 74 | func (w *Notebook) PaddingN() (int, int) { 75 | var r string 76 | var err error 77 | if w.info.IsTtk { 78 | r, err = evalAsString(fmt.Sprintf("%v cget -padding", w.id)) 79 | } else { 80 | r1, _ := evalAsString(fmt.Sprintf("%v cget -padx", w.id)) 81 | r2, _ := evalAsString(fmt.Sprintf("%v cget -pady", w.id)) 82 | r = r1 + " " + r2 83 | } 84 | return parserPaddingResult(r, err) 85 | } 86 | 87 | func (w *Notebook) SetPadding(pad Pad) error { 88 | return w.SetPaddingN(pad.X, pad.Y) 89 | } 90 | 91 | func (w *Notebook) Padding() Pad { 92 | x, y := w.PaddingN() 93 | return Pad{x, y} 94 | } 95 | 96 | var ( 97 | tabAttributes = []string{ 98 | "padding", 99 | "state", 100 | "sticky", 101 | "text", 102 | "image", 103 | "compound", 104 | } 105 | ) 106 | 107 | func isTabAttributes(attr string) bool { 108 | for _, v := range tabAttributes { 109 | if v == attr { 110 | return true 111 | } 112 | } 113 | return false 114 | } 115 | 116 | func buildTabAttributeScript(ttk bool, attributes ...*WidgetAttr) string { 117 | var list []string 118 | for _, attr := range attributes { 119 | if attr == nil { 120 | continue 121 | } 122 | if attr.Key == "padding" { 123 | list = append(list, checkPaddingScript(ttk, attr)) 124 | continue 125 | } 126 | if !isTabAttributes(attr.Key) { 127 | continue 128 | } 129 | if s, ok := attr.Value.(string); ok { 130 | pname := "atk_tmp_" + attr.Key 131 | setObjText(pname, s) 132 | list = append(list, fmt.Sprintf("-%v $%v", attr.Key, pname)) 133 | continue 134 | } 135 | list = append(list, fmt.Sprintf("-%v {%v}", attr.Key, attr.Value)) 136 | } 137 | return strings.Join(list, " ") 138 | } 139 | 140 | func (w *Notebook) AddTab(widget Widget, text string, attributes ...*WidgetAttr) error { 141 | if !IsValidWidget(widget) { 142 | return ErrInvalid 143 | } 144 | attributes = append(attributes, &WidgetAttr{"text", text}) 145 | extra := buildTabAttributeScript(w.info.IsTtk, attributes...) 146 | script := fmt.Sprintf("%v add %v", w.id, widget.Id()) 147 | if extra != "" { 148 | script += " " + extra 149 | } 150 | return eval(script) 151 | } 152 | 153 | func (w *Notebook) InsertTab(pos int, widget Widget, text string, attributes ...*WidgetAttr) error { 154 | if !IsValidWidget(widget) { 155 | return ErrInvalid 156 | } 157 | attributes = append(attributes, &WidgetAttr{"text", text}) 158 | extra := buildTabAttributeScript(w.info.IsTtk, attributes...) 159 | script := fmt.Sprintf("%v insert %v %v", w.id, pos, widget.Id()) 160 | if extra != "" { 161 | script += " " + extra 162 | } 163 | return eval(script) 164 | } 165 | 166 | func (w *Notebook) SetTab(widget Widget, text string, attributes ...*WidgetAttr) error { 167 | if !IsValidWidget(widget) { 168 | return ErrInvalid 169 | } 170 | attributes = append(attributes, &WidgetAttr{"text", text}) 171 | extra := buildTabAttributeScript(w.info.IsTtk, attributes...) 172 | script := fmt.Sprintf("%v tab %v", w.id, widget.Id()) 173 | if extra != "" { 174 | script += " " + extra 175 | } 176 | return eval(script) 177 | } 178 | 179 | func (w *Notebook) RemoveTab(widget Widget) error { 180 | if !IsValidWidget(widget) { 181 | return ErrInvalid 182 | } 183 | return eval(fmt.Sprintf("%v forget %v", w.id, widget.Id())) 184 | } 185 | 186 | func (w *Notebook) SetCurrentTab(widget Widget) error { 187 | if !IsValidWidget(widget) { 188 | return ErrInvalid 189 | } 190 | return eval(fmt.Sprintf("%v select %v", w.id, widget.Id())) 191 | } 192 | 193 | func (w *Notebook) CurrentTab() Widget { 194 | r, _ := evalAsString(fmt.Sprintf("%v select", w.id)) 195 | return FindWidget(r) 196 | } 197 | 198 | func (w *Notebook) CurrentTabIndex() int { 199 | r, _ := evalAsInt(fmt.Sprintf("%v index current", w.id)) 200 | return r 201 | } 202 | 203 | func (w *Notebook) TabCount() int { 204 | r, _ := evalAsInt(fmt.Sprintf("%v index end", w.id)) 205 | return r 206 | } 207 | 208 | func (w *Notebook) TabIndex(widget Widget) int { 209 | if !IsValidWidget(widget) { 210 | return -1 211 | } 212 | r, _ := evalAsInt(fmt.Sprintf("%v index %v", w.id, widget.Id())) 213 | return r 214 | } 215 | 216 | func TabAttrState(state State) *WidgetAttr { 217 | return &WidgetAttr{"state", state} 218 | } 219 | 220 | func TabAttrSticky(sticky Sticky) *WidgetAttr { 221 | return &WidgetAttr{"sticky", sticky} 222 | } 223 | 224 | func TabAttrPadding(padding Pad) *WidgetAttr { 225 | return &WidgetAttr{"padding", padding} 226 | } 227 | 228 | func TabAttrText(text string) *WidgetAttr { 229 | return &WidgetAttr{"text", text} 230 | } 231 | 232 | func TabAttrImage(image *Image) *WidgetAttr { 233 | if image == nil { 234 | return nil 235 | } 236 | return &WidgetAttr{"image", image.Id()} 237 | } 238 | 239 | func TabAttrCompound(compound Compound) *WidgetAttr { 240 | return &WidgetAttr{"compound", compound} 241 | } 242 | 243 | func NotebookAttrWidth(width int) *WidgetAttr { 244 | return &WidgetAttr{"width", width} 245 | } 246 | 247 | func NotebookAttrHeight(height int) *WidgetAttr { 248 | return &WidgetAttr{"height", height} 249 | } 250 | 251 | func NotebookAttrTakeFocus(takefocus bool) *WidgetAttr { 252 | return &WidgetAttr{"takefocus", boolToInt(takefocus)} 253 | } 254 | 255 | func NotebookAttrPadding(padding Pad) *WidgetAttr { 256 | return &WidgetAttr{"padding", padding} 257 | } 258 | -------------------------------------------------------------------------------- /tk/notebook_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import "testing" 6 | 7 | func init() { 8 | registerTest("Notebook", testNotebook) 9 | } 10 | 11 | func testNotebook(t *testing.T) { 12 | w := NewNotebook(nil, NotebookAttrWidth(20), NotebookAttrHeight(20), NotebookAttrTakeFocus(true)) 13 | defer w.Destroy() 14 | 15 | w.SetWidth(20) 16 | if v := w.Width(); v != 20 { 17 | t.Fatal("Width", 20, v) 18 | } 19 | 20 | w.SetHeight(20) 21 | if v := w.Height(); v != 20 { 22 | t.Fatal("Height", 20, v) 23 | } 24 | 25 | w.SetTakeFocus(true) 26 | if v := w.IsTakeFocus(); v != true { 27 | t.Fatal("IsTakeFocus", true, v) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tk/pack.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import ( 6 | "fmt" 7 | "strings" 8 | ) 9 | 10 | func PackAttrSide(side Side) *LayoutAttr { 11 | return &LayoutAttr{"side", side} 12 | } 13 | 14 | func PackAttrSideLeft() *LayoutAttr { 15 | return &LayoutAttr{"side", "left"} 16 | } 17 | 18 | func PackAttrSideRight() *LayoutAttr { 19 | return &LayoutAttr{"side", "right"} 20 | } 21 | 22 | func PackAttrSideTop() *LayoutAttr { 23 | return &LayoutAttr{"side", "top"} 24 | } 25 | 26 | func PackAttrSideBottom() *LayoutAttr { 27 | return &LayoutAttr{"side", "bottom"} 28 | } 29 | 30 | func PackAttrPadx(padx int) *LayoutAttr { 31 | return &LayoutAttr{"padx", padx} 32 | } 33 | 34 | func PackAttrPady(pady int) *LayoutAttr { 35 | return &LayoutAttr{"pady", pady} 36 | } 37 | 38 | func PackAttrIpadx(padx int) *LayoutAttr { 39 | return &LayoutAttr{"ipadx", padx} 40 | } 41 | 42 | func PackAttrIpady(pady int) *LayoutAttr { 43 | return &LayoutAttr{"ipady", pady} 44 | } 45 | 46 | func PackAttrAnchor(anchor Anchor) *LayoutAttr { 47 | v := anchor.String() 48 | if v == "" { 49 | return nil 50 | } 51 | return &LayoutAttr{"anchor", v} 52 | } 53 | 54 | func PackAttrExpand(b bool) *LayoutAttr { 55 | return &LayoutAttr{"expand", boolToInt(b)} 56 | } 57 | 58 | func PackAttrFill(fill Fill) *LayoutAttr { 59 | return &LayoutAttr{"fill", fill} 60 | } 61 | 62 | func PackAttrFillX() *LayoutAttr { 63 | return &LayoutAttr{"fill", "x"} 64 | } 65 | 66 | func PackAttrFillY() *LayoutAttr { 67 | return &LayoutAttr{"fill", "y"} 68 | } 69 | 70 | func PackAttrFillBoth() *LayoutAttr { 71 | return &LayoutAttr{"fill", "both"} 72 | } 73 | 74 | func PackAttrFillNone() *LayoutAttr { 75 | return &LayoutAttr{"fill", "none"} 76 | } 77 | 78 | func PackAttrBefore(w Widget) *LayoutAttr { 79 | if !IsValidWidget(w) { 80 | return nil 81 | } 82 | return &LayoutAttr{"before", w.Id()} 83 | } 84 | 85 | func PackAttrAfter(w Widget) *LayoutAttr { 86 | if !IsValidWidget(w) { 87 | return nil 88 | } 89 | return &LayoutAttr{"after", w.Id()} 90 | } 91 | 92 | func PackAttrInMaster(w Widget) *LayoutAttr { 93 | if !IsValidWidget(w) { 94 | return nil 95 | } 96 | return &LayoutAttr{"in", w.Id()} 97 | } 98 | 99 | var ( 100 | packAttrKeys = []string{ 101 | "side", 102 | "padx", "pady", 103 | "ipadx", "ipady", 104 | "anchor", 105 | "expand", 106 | "fill", 107 | "before", 108 | "after", 109 | "in", 110 | } 111 | ) 112 | 113 | func Pack(widget Widget, attributes ...*LayoutAttr) error { 114 | return PackList([]Widget{widget}, attributes...) 115 | } 116 | 117 | func PackRemove(widget Widget) error { 118 | if !IsValidWidget(widget) { 119 | return ErrInvalid 120 | } 121 | widget = checkLayoutWidget(widget) 122 | return eval("pack forget " + widget.Id()) 123 | } 124 | 125 | func PackList(widgets []Widget, attributes ...*LayoutAttr) error { 126 | var idList []string 127 | for _, w := range widgets { 128 | if IsValidWidget(w) { 129 | w = checkLayoutWidget(w) 130 | idList = append(idList, w.Id()) 131 | } 132 | } 133 | if len(idList) == 0 { 134 | return ErrInvalid 135 | } 136 | var attrList []string 137 | for _, attr := range attributes { 138 | if attr == nil || !isValidKey(attr.Key, packAttrKeys) { 139 | continue 140 | } 141 | attrList = append(attrList, fmt.Sprintf("-%v {%v}", attr.Key, attr.Value)) 142 | } 143 | script := fmt.Sprintf("pack %v", strings.Join(idList, " ")) 144 | if len(attrList) > 0 { 145 | script += " " + strings.Join(attrList, " ") 146 | } 147 | return eval(script) 148 | } 149 | -------------------------------------------------------------------------------- /tk/packlayout.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import ( 6 | "fmt" 7 | ) 8 | 9 | type PackLayout struct { 10 | *LayoutFrame 11 | side Side 12 | pad *Pad 13 | items []*LayoutItem 14 | } 15 | 16 | func (w *PackLayout) SetSide(side Side) error { 17 | w.side = side 18 | return w.Repack() 19 | } 20 | 21 | func (w *PackLayout) SetPadding(pad Pad) error { 22 | return w.SetPaddingN(pad.X, pad.Y) 23 | } 24 | 25 | func (w *PackLayout) SetPaddingN(padx int, pady int) error { 26 | w.pad = &Pad{padx, pady} 27 | return w.Repack() 28 | } 29 | 30 | func (w *PackLayout) removeItem(widget Widget) error { 31 | n := w.indexOfWidget(widget) 32 | if n == -1 { 33 | return ErrInvalid 34 | } 35 | PackRemove(widget) 36 | w.items = append(w.items[:n], w.items[n+1:]...) 37 | return nil 38 | } 39 | 40 | func (w *PackLayout) indexOfWidget(widget Widget) int { 41 | for n, v := range w.items { 42 | if v.widget == widget { 43 | return n 44 | } 45 | } 46 | return -1 47 | } 48 | 49 | func (w *PackLayout) AddWidget(widget Widget, attributes ...*LayoutAttr) error { 50 | if !IsValidWidget(widget) { 51 | return ErrInvalid 52 | } 53 | n := w.indexOfWidget(widget) 54 | if n != -1 { 55 | w.items = append(w.items[:n], w.items[n+1:]...) 56 | } 57 | w.items = append(w.items, &LayoutItem{widget, attributes}) 58 | return w.Repack() 59 | } 60 | 61 | func (w *PackLayout) InsertWidget(index int, widget Widget, attributes ...*LayoutAttr) error { 62 | if index < 0 { 63 | return w.AddWidget(widget, attributes...) 64 | } 65 | n := w.indexOfWidget(widget) 66 | if n != -1 { 67 | if n == index { 68 | return ErrExist 69 | } 70 | w.items = append(w.items[:n], w.items[n+1:]...) 71 | } 72 | if index >= len(w.items) { 73 | return w.AddWidget(widget, attributes...) 74 | } 75 | w.items = append(w.items[:index], append([]*LayoutItem{&LayoutItem{widget, attributes}}, w.items[index:]...)...) 76 | return w.Repack() 77 | } 78 | 79 | func (w *PackLayout) AddWidgetEx(widget Widget, fill Fill, expand bool, anchor Anchor) error { 80 | return w.AddWidget(widget, 81 | PackAttrFill(fill), PackAttrExpand(expand), 82 | PackAttrAnchor(anchor)) 83 | } 84 | 85 | func (w *PackLayout) InsertWidgetEx(index int, widget Widget, fill Fill, expand bool, anchor Anchor) error { 86 | return w.InsertWidget(index, widget, 87 | PackAttrFill(fill), PackAttrExpand(expand), 88 | PackAttrAnchor(anchor)) 89 | } 90 | 91 | func (w *PackLayout) AddWidgets(widgets ...Widget) error { 92 | for _, widget := range widgets { 93 | n := w.indexOfWidget(widget) 94 | if n != -1 { 95 | w.items = append(w.items[:n], w.items[n+1:]...) 96 | } 97 | w.items = append(w.items, &LayoutItem{widget, nil}) 98 | } 99 | return w.Repack() 100 | } 101 | 102 | func (w *PackLayout) AddWidgetList(widgets []Widget, attributes ...*LayoutAttr) error { 103 | for _, widget := range widgets { 104 | n := w.indexOfWidget(widget) 105 | if n != -1 { 106 | w.items = append(w.items[:n], w.items[n+1:]...) 107 | } 108 | w.items = append(w.items, &LayoutItem{widget, attributes}) 109 | } 110 | return w.Repack() 111 | } 112 | 113 | func (w *PackLayout) RemoveWidget(widget Widget) error { 114 | if !IsValidWidget(widget) { 115 | return ErrInvalid 116 | } 117 | return w.removeItem(widget) 118 | } 119 | 120 | func (w *PackLayout) SetWidgetAttr(widget Widget, attributes ...*LayoutAttr) error { 121 | if !IsValidWidget(widget) { 122 | return ErrInvalid 123 | } 124 | n := w.indexOfWidget(widget) 125 | if n == -1 { 126 | return ErrInvalid 127 | } 128 | w.items[n].attrs = attributes 129 | return w.Repack() 130 | } 131 | 132 | func (w *PackLayout) itemAttr() []*LayoutAttr { 133 | itemsAttr := []*LayoutAttr{PackAttrSide(w.side), PackAttrInMaster(w)} 134 | if w.pad != nil { 135 | itemsAttr = append(itemsAttr, PackAttrPadx(w.pad.X), PackAttrPady(w.pad.Y)) 136 | } 137 | return itemsAttr 138 | } 139 | 140 | func (w *PackLayout) resetSpacerAttr(item *LayoutItem, s *LayoutSpacer) { 141 | if s.IsExpand() { 142 | s.SetWidth(0) 143 | s.SetHeight(0) 144 | if w.side == SideTop || w.side == SideBottom { 145 | item.attrs = AppendLayoutAttrs(item.attrs, PackAttrFillY(), PackAttrExpand(true)) 146 | } else { 147 | item.attrs = AppendLayoutAttrs(item.attrs, PackAttrFillX(), PackAttrExpand(true)) 148 | } 149 | } else { 150 | item.attrs = AppendLayoutAttrs(item.attrs, PackAttrFillNone(), PackAttrExpand(false)) 151 | if w.side == SideTop || w.side == SideBottom { 152 | s.SetHeight(s.space) 153 | s.SetWidth(0) 154 | } else { 155 | s.SetWidth(s.space) 156 | s.SetHeight(0) 157 | } 158 | } 159 | } 160 | 161 | func (w *PackLayout) Repack() error { 162 | for _, item := range w.items { 163 | if item.widget == nil { 164 | continue 165 | } 166 | if s, ok := item.widget.(*LayoutSpacer); ok { 167 | w.resetSpacerAttr(item, s) 168 | } 169 | Pack(item.widget, AppendLayoutAttrs(item.attrs, w.itemAttr()...)...) 170 | } 171 | return Pack(w, PackAttrFill(FillBoth), PackAttrExpand(true)) 172 | } 173 | 174 | func (w *PackLayout) SetBorderWidth(width int) error { 175 | return eval(fmt.Sprintf("%v configure -borderwidth {%v}", w.Id(), width)) 176 | } 177 | 178 | func (w *PackLayout) BorderWidth() int { 179 | r, _ := evalAsInt(fmt.Sprintf("%v cget -borderwidth", w.Id())) 180 | return r 181 | } 182 | 183 | func NewPackLayout(parent Widget, side Side) *PackLayout { 184 | pack := &PackLayout{NewLayoutFrame(parent), side, nil, nil} 185 | pack.Lower(nil) 186 | pack.Repack() 187 | return pack 188 | } 189 | 190 | func NewHPackLayout(parent Widget) *PackLayout { 191 | return NewPackLayout(parent, SideLeft) 192 | } 193 | 194 | func NewVPackLayout(parent Widget) *PackLayout { 195 | return NewPackLayout(parent, SideTop) 196 | } 197 | -------------------------------------------------------------------------------- /tk/paned.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import "fmt" 6 | 7 | // panedwindow 8 | type Paned struct { 9 | BaseWidget 10 | } 11 | 12 | func NewPaned(parent Widget, orient Orient, attributes ...*WidgetAttr) *Paned { 13 | iid := makeNamedWidgetId(parent, "atk_paned") 14 | attributes = append(attributes, &WidgetAttr{"orient", orient}) 15 | info := CreateWidgetInfo(iid, WidgetTypePaned, true, attributes) 16 | if info == nil { 17 | return nil 18 | } 19 | w := &Paned{} 20 | w.id = iid 21 | w.info = info 22 | RegisterWidget(w) 23 | return w 24 | } 25 | 26 | func (w *Paned) Attach(id string) error { 27 | info, err := CheckWidgetInfo(id, WidgetTypePaned) 28 | if err != nil { 29 | return err 30 | } 31 | w.id = id 32 | w.info = info 33 | RegisterWidget(w) 34 | return nil 35 | } 36 | 37 | func (w *Paned) SetWidth(width int) error { 38 | return eval(fmt.Sprintf("%v configure -width {%v}", w.id, width)) 39 | } 40 | 41 | func (w *Paned) Width() int { 42 | r, _ := evalAsInt(fmt.Sprintf("%v cget -width", w.id)) 43 | return r 44 | } 45 | 46 | func (w *Paned) SetHeight(height int) error { 47 | return eval(fmt.Sprintf("%v configure -height {%v}", w.id, height)) 48 | } 49 | 50 | func (w *Paned) Height() int { 51 | r, _ := evalAsInt(fmt.Sprintf("%v cget -height", w.id)) 52 | return r 53 | } 54 | 55 | func (w *Paned) AddWidget(widget Widget, weight int) error { 56 | if !IsValidWidget(widget) { 57 | return ErrInvalid 58 | } 59 | return eval(fmt.Sprintf("%v add %v -weight %v", w.id, widget.Id(), weight)) 60 | } 61 | 62 | func (w *Paned) InsertWidget(pane int, widget Widget, weight int) error { 63 | if !IsValidWidget(widget) { 64 | return ErrInvalid 65 | } 66 | return eval(fmt.Sprintf("%v insert %v %v -weight %v", w.id, pane, widget.Id(), weight)) 67 | } 68 | 69 | func (w *Paned) SetPane(pane int, weight int) error { 70 | return eval(fmt.Sprintf("%v pane %v -weight %v", w.id, pane, weight)) 71 | } 72 | 73 | func (w *Paned) RemovePane(pane int) error { 74 | return eval(fmt.Sprintf("%v forget %v", w.id, pane)) 75 | } 76 | 77 | func PanedAttrWidth(width int) *WidgetAttr { 78 | return &WidgetAttr{"width", width} 79 | } 80 | 81 | func PanedAttrHeight(height int) *WidgetAttr { 82 | return &WidgetAttr{"height", height} 83 | } 84 | -------------------------------------------------------------------------------- /tk/paned_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import "testing" 6 | 7 | func init() { 8 | registerTest("Paned", testPaned) 9 | } 10 | 11 | func testPaned(t *testing.T) { 12 | w := NewPaned(nil, Vertical) 13 | defer w.Destroy() 14 | 15 | w.SetWidth(20) 16 | if v := w.Width(); v != 20 { 17 | t.Fatal("Width", 20, v) 18 | } 19 | 20 | w.SetHeight(20) 21 | if v := w.Height(); v != 20 { 22 | t.Fatal("Height", 20, v) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tk/place.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import ( 6 | "fmt" 7 | "strings" 8 | ) 9 | 10 | func PlaceAttrAnchor(anchor Anchor) *LayoutAttr { 11 | v := anchor.String() 12 | if v == "" { 13 | return nil 14 | } 15 | return &LayoutAttr{"anchor", v} 16 | } 17 | 18 | func PlaceAttrBorderMode(mode BorderMode) *LayoutAttr { 19 | v := mode.String() 20 | if v == "" { 21 | return nil 22 | } 23 | return &LayoutAttr{"bordermode", v} 24 | } 25 | 26 | func PlaceAttrWidth(size int) *LayoutAttr { 27 | return &LayoutAttr{"width", size} 28 | } 29 | 30 | func PlaceAttrHeight(size int) *LayoutAttr { 31 | return &LayoutAttr{"height", size} 32 | } 33 | 34 | func PlaceAttrRelWidth(size float64) *LayoutAttr { 35 | return &LayoutAttr{"relwidth", size} 36 | } 37 | 38 | func PlaceAttrRelHeight(size float64) *LayoutAttr { 39 | return &LayoutAttr{"relheight", size} 40 | } 41 | 42 | func PlaceAttrX(location int) *LayoutAttr { 43 | return &LayoutAttr{"x", location} 44 | } 45 | 46 | func PlaceAttrY(location int) *LayoutAttr { 47 | return &LayoutAttr{"y", location} 48 | } 49 | 50 | func PlaceAttrRelX(location float64) *LayoutAttr { 51 | return &LayoutAttr{"relx", location} 52 | } 53 | 54 | func PlaceAttrRelY(location float64) *LayoutAttr { 55 | return &LayoutAttr{"rely", location} 56 | } 57 | 58 | func PlaceAttrInMaster(w Widget) *LayoutAttr { 59 | if !IsValidWidget(w) { 60 | return nil 61 | } 62 | return &LayoutAttr{"in", w.Id()} 63 | } 64 | 65 | var ( 66 | placeAttrKeys = []string{ 67 | "anchor", 68 | "bordermode", 69 | "x", "y", 70 | "relx", "rely", 71 | "width", "height", 72 | "relwidth", "relheight", 73 | "in", 74 | } 75 | ) 76 | 77 | func Place(widget Widget, attributes ...*LayoutAttr) error { 78 | if !IsValidWidget(widget) { 79 | return ErrInvalid 80 | } 81 | var attrList []string 82 | for _, attr := range attributes { 83 | if attr == nil || !isValidKey(attr.Key, placeAttrKeys) { 84 | continue 85 | } 86 | attrList = append(attrList, fmt.Sprintf("-%v {%v}", attr.Key, attr.Value)) 87 | } 88 | script := fmt.Sprintf("place %v", widget.Id()) 89 | if len(attrList) > 0 { 90 | script += " " + strings.Join(attrList, " ") 91 | } 92 | return eval(script) 93 | } 94 | 95 | func PlaceRemove(widget Widget) error { 96 | if !IsValidWidget(widget) { 97 | return ErrInvalid 98 | } 99 | widget = checkLayoutWidget(widget) 100 | return eval("place forget " + widget.Id()) 101 | } 102 | -------------------------------------------------------------------------------- /tk/placeframe.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | type PlaceFrame struct { 6 | *Frame 7 | items []*LayoutItem 8 | } 9 | 10 | func (w *PlaceFrame) removeItem(widget Widget) error { 11 | n := w.indexOfWidget(widget) 12 | if n == -1 { 13 | return ErrInvalid 14 | } 15 | PlaceRemove(widget) 16 | w.items = append(w.items[:n], w.items[n+1:]...) 17 | return nil 18 | } 19 | 20 | func (w *PlaceFrame) indexOfWidget(widget Widget) int { 21 | for n, v := range w.items { 22 | if v.widget == widget { 23 | return n 24 | } 25 | } 26 | return -1 27 | } 28 | 29 | func (w *PlaceFrame) AddWidget(widget Widget, attributes ...*LayoutAttr) error { 30 | if !IsValidWidget(widget) { 31 | return ErrInvalid 32 | } 33 | n := w.indexOfWidget(widget) 34 | if n != -1 { 35 | w.items = append(w.items[:n], w.items[n+1:]...) 36 | } 37 | w.items = append(w.items, &LayoutItem{widget, attributes}) 38 | return w.Repack() 39 | } 40 | 41 | func (w *PlaceFrame) InsertWidget(index int, widget Widget, attributes ...*LayoutAttr) error { 42 | if index < 0 { 43 | return w.AddWidget(widget, attributes...) 44 | } 45 | n := w.indexOfWidget(widget) 46 | if n != -1 { 47 | if n == index { 48 | return ErrExist 49 | } 50 | w.items = append(w.items[:n], w.items[n+1:]...) 51 | } 52 | if index >= len(w.items) { 53 | return w.AddWidget(widget, attributes...) 54 | } 55 | w.items = append(w.items[:index], append([]*LayoutItem{&LayoutItem{widget, attributes}}, w.items[index:]...)...) 56 | return w.Repack() 57 | } 58 | 59 | func (w *PlaceFrame) RemoveWidget(widget Widget) error { 60 | if !IsValidWidget(widget) { 61 | return ErrInvalid 62 | } 63 | return w.removeItem(widget) 64 | } 65 | 66 | func (w *PlaceFrame) SetWidgetAttr(widget Widget, attributes ...*LayoutAttr) error { 67 | if !IsValidWidget(widget) { 68 | return ErrInvalid 69 | } 70 | n := w.indexOfWidget(widget) 71 | if n == -1 { 72 | return ErrInvalid 73 | } 74 | w.items[n].attrs = attributes 75 | return w.Repack() 76 | } 77 | 78 | func (w *PlaceFrame) Repack() error { 79 | for _, item := range w.items { 80 | if item.widget == nil { 81 | continue 82 | } 83 | Place(item.widget, AppendLayoutAttrs(item.attrs, PlaceAttrInMaster(w))...) 84 | } 85 | return Pack(w, PackAttrFill(FillBoth), PackAttrExpand(true)) 86 | } 87 | 88 | func NewPlaceFrame(parent Widget) *PlaceFrame { 89 | place := &PlaceFrame{NewFrame(parent), nil} 90 | place.Lower(nil) 91 | place.Repack() 92 | return place 93 | } 94 | -------------------------------------------------------------------------------- /tk/progressbar.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import "fmt" 6 | 7 | // progressbar 8 | type ProgressBar struct { 9 | BaseWidget 10 | } 11 | 12 | func NewProgressBar(parent Widget, orient Orient, attributes ...*WidgetAttr) *ProgressBar { 13 | iid := makeNamedWidgetId(parent, "atk_progressbar") 14 | attributes = append(attributes, &WidgetAttr{"orient", orient}) 15 | info := CreateWidgetInfo(iid, WidgetTypeProgressBar, true, attributes) 16 | if info == nil { 17 | return nil 18 | } 19 | w := &ProgressBar{} 20 | w.id = iid 21 | w.info = info 22 | RegisterWidget(w) 23 | return w 24 | } 25 | 26 | func (w *ProgressBar) Attach(id string) error { 27 | info, err := CheckWidgetInfo(id, WidgetTypeProgressBar) 28 | if err != nil { 29 | return err 30 | } 31 | w.id = id 32 | w.info = info 33 | RegisterWidget(w) 34 | return nil 35 | } 36 | 37 | func (w *ProgressBar) SetOrient(orient Orient) error { 38 | return eval(fmt.Sprintf("%v configure -orient {%v}", w.id, orient)) 39 | } 40 | 41 | func (w *ProgressBar) Orient() Orient { 42 | r, err := evalAsString(fmt.Sprintf("%v cget -orient", w.id)) 43 | return parserOrientResult(r, err) 44 | } 45 | 46 | func (w *ProgressBar) SetTakeFocus(takefocus bool) error { 47 | return eval(fmt.Sprintf("%v configure -takefocus {%v}", w.id, boolToInt(takefocus))) 48 | } 49 | 50 | func (w *ProgressBar) IsTakeFocus() bool { 51 | r, _ := evalAsBool(fmt.Sprintf("%v cget -takefocus", w.id)) 52 | return r 53 | } 54 | 55 | func (w *ProgressBar) SetLength(length int) error { 56 | return eval(fmt.Sprintf("%v configure -length {%v}", w.id, length)) 57 | } 58 | 59 | func (w *ProgressBar) Length() int { 60 | r, _ := evalAsInt(fmt.Sprintf("%v cget -length", w.id)) 61 | return r 62 | } 63 | 64 | func (w *ProgressBar) SetMaximum(maximum float64) error { 65 | return eval(fmt.Sprintf("%v configure -maximum {%v}", w.id, maximum)) 66 | } 67 | 68 | func (w *ProgressBar) Maximum() float64 { 69 | r, _ := evalAsFloat64(fmt.Sprintf("%v cget -maximum", w.id)) 70 | return r 71 | } 72 | 73 | func (w *ProgressBar) SetValue(value float64) error { 74 | return eval(fmt.Sprintf("%v configure -value {%v}", w.id, value)) 75 | } 76 | 77 | func (w *ProgressBar) Value() float64 { 78 | r, _ := evalAsFloat64(fmt.Sprintf("%v cget -value", w.id)) 79 | return r 80 | } 81 | 82 | func (w *ProgressBar) Phase() int { 83 | r, _ := evalAsInt(fmt.Sprintf("%v cget -phase", w.id)) 84 | return r 85 | } 86 | 87 | func (w *ProgressBar) SetDeterminateMode(b bool) error { 88 | var mode string 89 | if b { 90 | mode = "determinate" 91 | } else { 92 | mode = "indeterminate" 93 | } 94 | return eval(fmt.Sprintf("%v configure -mode %v", w.id, mode)) 95 | } 96 | 97 | func (w *ProgressBar) IsDeterminateMode() bool { 98 | r, _ := evalAsString(fmt.Sprintf("%v cget -mode", w.id)) 99 | return r == "determinate" 100 | } 101 | 102 | func (w *ProgressBar) Start() error { 103 | return w.StartEx(50) 104 | } 105 | 106 | func (w *ProgressBar) StartEx(ms int) error { 107 | return eval(fmt.Sprintf("%v start %v", w.id, ms)) 108 | } 109 | 110 | func (w *ProgressBar) Stop() error { 111 | return eval(fmt.Sprintf("%v stop", w.id)) 112 | } 113 | 114 | func (w *ProgressBar) Pause() error { 115 | cur := w.Value() 116 | w.Stop() 117 | return w.SetValue(cur) 118 | } 119 | 120 | func ProgressBarAttrOrient(orient Orient) *WidgetAttr { 121 | return &WidgetAttr{"orient", orient} 122 | } 123 | 124 | func ProgressBarAttrTakeFocus(takefocus bool) *WidgetAttr { 125 | return &WidgetAttr{"takefocus", boolToInt(takefocus)} 126 | } 127 | 128 | func ProgressBarAttrLength(length int) *WidgetAttr { 129 | return &WidgetAttr{"length", length} 130 | } 131 | 132 | func ProgressBarAttrMaximum(maximum float64) *WidgetAttr { 133 | return &WidgetAttr{"maximum", maximum} 134 | } 135 | 136 | func ProgressBarAttrValue(value float64) *WidgetAttr { 137 | return &WidgetAttr{"value", value} 138 | } 139 | -------------------------------------------------------------------------------- /tk/progressbar_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import "testing" 6 | 7 | func init() { 8 | registerTest("ProgressBar", testProgressBar) 9 | } 10 | 11 | func testProgressBar(t *testing.T) { 12 | w := NewProgressBar(nil, Vertical) 13 | defer w.Destroy() 14 | 15 | w.SetOrient(Horizontal) 16 | if v := w.Orient(); v != Horizontal { 17 | t.Fatal("Orient", Horizontal, v) 18 | } 19 | 20 | w.SetTakeFocus(true) 21 | if v := w.IsTakeFocus(); v != true { 22 | t.Fatal("IsTakeFocus", true, v) 23 | } 24 | 25 | w.SetLength(20) 26 | if v := w.Length(); v != 20 { 27 | t.Fatal("Length", 20, v) 28 | } 29 | 30 | w.SetMaximum(0.5) 31 | if v := w.Maximum(); v != 0.5 { 32 | t.Fatal("Maximum", 0.5, v) 33 | } 34 | 35 | w.SetValue(0.5) 36 | if v := w.Value(); v != 0.5 { 37 | t.Fatal("Value", 0.5, v) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /tk/radiobutton.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import "fmt" 6 | 7 | // radio button 8 | type RadioButton struct { 9 | BaseWidget 10 | command *Command 11 | } 12 | 13 | func NewRadioButton(parent Widget, text string, attributes ...*WidgetAttr) *RadioButton { 14 | theme := checkInitUseTheme(attributes) 15 | iid := makeNamedWidgetId(parent, "atk_radiobutton") 16 | attributes = append(attributes, &WidgetAttr{"text", text}) 17 | info := CreateWidgetInfo(iid, WidgetTypeRadioButton, theme, attributes) 18 | if info == nil { 19 | return nil 20 | } 21 | w := &RadioButton{} 22 | w.id = iid 23 | w.info = info 24 | RegisterWidget(w) 25 | return w 26 | } 27 | 28 | func (w *RadioButton) Attach(id string) error { 29 | info, err := CheckWidgetInfo(id, WidgetTypeRadioButton) 30 | if err != nil { 31 | return err 32 | } 33 | w.id = id 34 | w.info = info 35 | RegisterWidget(w) 36 | return nil 37 | } 38 | 39 | func (w *RadioButton) SetText(text string) error { 40 | setObjText("atk_tmp_text", text) 41 | return eval(fmt.Sprintf("%v configure -text $atk_tmp_text", w.id)) 42 | } 43 | 44 | func (w *RadioButton) Text() string { 45 | r, _ := evalAsString(fmt.Sprintf("%v cget -text", w.id)) 46 | return r 47 | } 48 | 49 | func (w *RadioButton) SetWidth(width int) error { 50 | return eval(fmt.Sprintf("%v configure -width {%v}", w.id, width)) 51 | } 52 | 53 | func (w *RadioButton) Width() int { 54 | r, _ := evalAsInt(fmt.Sprintf("%v cget -width", w.id)) 55 | return r 56 | } 57 | 58 | func (w *RadioButton) SetImage(image *Image) error { 59 | if image == nil { 60 | return ErrInvalid 61 | } 62 | return eval(fmt.Sprintf("%v configure -image {%v}", w.id, image.Id())) 63 | } 64 | 65 | func (w *RadioButton) Image() *Image { 66 | r, err := evalAsString(fmt.Sprintf("%v cget -image", w.id)) 67 | return parserImageResult(r, err) 68 | } 69 | 70 | func (w *RadioButton) SetCompound(compound Compound) error { 71 | return eval(fmt.Sprintf("%v configure -compound {%v}", w.id, compound)) 72 | } 73 | 74 | func (w *RadioButton) Compound() Compound { 75 | r, err := evalAsString(fmt.Sprintf("%v cget -compound", w.id)) 76 | return parserCompoundResult(r, err) 77 | } 78 | 79 | func (w *RadioButton) SetPaddingN(padx int, pady int) error { 80 | if w.info.IsTtk { 81 | return eval(fmt.Sprintf("%v configure -padding {%v %v}", w.id, padx, pady)) 82 | } 83 | return eval(fmt.Sprintf("%v configure -padx {%v} -pady {%v}", w.id, padx, pady)) 84 | } 85 | 86 | func (w *RadioButton) PaddingN() (int, int) { 87 | var r string 88 | var err error 89 | if w.info.IsTtk { 90 | r, err = evalAsString(fmt.Sprintf("%v cget -padding", w.id)) 91 | } else { 92 | r1, _ := evalAsString(fmt.Sprintf("%v cget -padx", w.id)) 93 | r2, _ := evalAsString(fmt.Sprintf("%v cget -pady", w.id)) 94 | r = r1 + " " + r2 95 | } 96 | return parserPaddingResult(r, err) 97 | } 98 | 99 | func (w *RadioButton) SetPadding(pad Pad) error { 100 | return w.SetPaddingN(pad.X, pad.Y) 101 | } 102 | 103 | func (w *RadioButton) Padding() Pad { 104 | x, y := w.PaddingN() 105 | return Pad{x, y} 106 | } 107 | 108 | func (w *RadioButton) SetState(state State) error { 109 | return eval(fmt.Sprintf("%v configure -state {%v}", w.id, state)) 110 | } 111 | 112 | func (w *RadioButton) State() State { 113 | r, err := evalAsString(fmt.Sprintf("%v cget -state", w.id)) 114 | return parserStateResult(r, err) 115 | } 116 | 117 | func (w *RadioButton) SetTakeFocus(takefocus bool) error { 118 | return eval(fmt.Sprintf("%v configure -takefocus {%v}", w.id, boolToInt(takefocus))) 119 | } 120 | 121 | func (w *RadioButton) IsTakeFocus() bool { 122 | r, _ := evalAsBool(fmt.Sprintf("%v cget -takefocus", w.id)) 123 | return r 124 | } 125 | 126 | func (w *RadioButton) SetChecked(check bool) *RadioButton { 127 | if check { 128 | eval(fmt.Sprintf("set [%v cget -variable] [%v cget -value]", w.id, w.id)) 129 | } else { 130 | eval(fmt.Sprintf("set [%v cget -variable] {}", w.id)) 131 | } 132 | return w 133 | } 134 | 135 | func (w *RadioButton) IsChecked() bool { 136 | r, _ := evalAsBool(fmt.Sprintf("expr $[%v cget -variable]=={[%v cget -value]}", w.id, w.id)) 137 | return r 138 | } 139 | 140 | func (w *RadioButton) OnCommand(fn func()) error { 141 | if fn == nil { 142 | return ErrInvalid 143 | } 144 | if w.command == nil { 145 | w.command = &Command{} 146 | bindCommand(w.id, "command", w.command.Invoke) 147 | } 148 | w.command.Bind(fn) 149 | return nil 150 | } 151 | 152 | func (w *RadioButton) Invoke() { 153 | eval(fmt.Sprintf("%v invoke", w.id)) 154 | } 155 | 156 | func RadioButtonAttrText(text string) *WidgetAttr { 157 | return &WidgetAttr{"text", text} 158 | } 159 | 160 | func RadioButtonAttrWidth(width int) *WidgetAttr { 161 | return &WidgetAttr{"width", width} 162 | } 163 | 164 | func RadioButtonAttrImage(image *Image) *WidgetAttr { 165 | if image == nil { 166 | return nil 167 | } 168 | return &WidgetAttr{"image", image.Id()} 169 | } 170 | 171 | func RadioButtonAttrCompound(compound Compound) *WidgetAttr { 172 | return &WidgetAttr{"compound", compound} 173 | } 174 | 175 | func RadioButtonAttrPadding(padding Pad) *WidgetAttr { 176 | return &WidgetAttr{"padding", padding} 177 | } 178 | 179 | func RadioButtonAttrState(state State) *WidgetAttr { 180 | return &WidgetAttr{"state", state} 181 | } 182 | 183 | func RadioButtonAttrTakeFocus(takefocus bool) *WidgetAttr { 184 | return &WidgetAttr{"takefocus", boolToInt(takefocus)} 185 | } 186 | -------------------------------------------------------------------------------- /tk/radiobutton_group.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import ( 6 | "fmt" 7 | ) 8 | 9 | type _RadioData struct { 10 | btn *RadioButton 11 | value string 12 | data interface{} 13 | } 14 | 15 | type RadioGroup struct { 16 | id string 17 | rds []*_RadioData 18 | command *Command 19 | } 20 | 21 | func NewRadioGroup() *RadioGroup { 22 | id := makeNamedId("atk_radiogroup") 23 | evalSetValue(id, "") 24 | return &RadioGroup{id, nil, &Command{}} 25 | } 26 | 27 | func (w *RadioGroup) IsValid() bool { 28 | return w.id != "" 29 | } 30 | 31 | func (w *RadioGroup) findRadio(btn *RadioButton) *_RadioData { 32 | for _, rd := range w.rds { 33 | if rd.btn == btn { 34 | return rd 35 | } 36 | } 37 | return nil 38 | } 39 | 40 | func (w *RadioGroup) findRadioByValue(value string) *_RadioData { 41 | for _, rd := range w.rds { 42 | if rd.value == value { 43 | return rd 44 | } 45 | } 46 | return nil 47 | } 48 | 49 | func (w *RadioGroup) AddNewRadio(parent Widget, text string, data interface{}, attributes ...*WidgetAttr) *RadioButton { 50 | btn := NewRadioButton(parent, text, attributes...) 51 | w.AddRadio(btn, data) 52 | return btn 53 | } 54 | 55 | func (w *RadioGroup) AddRadios(btns ...*RadioButton) error { 56 | for _, btn := range btns { 57 | w.AddRadio(btn, nil) 58 | } 59 | return nil 60 | } 61 | 62 | func (w *RadioGroup) AddRadio(btn *RadioButton, data interface{}) error { 63 | if w.findRadio(btn) != nil { 64 | return ErrExist 65 | } 66 | if !IsValidWidget(btn) { 67 | return ErrInvalid 68 | } 69 | value := makeNamedId("atk_radiovalue") 70 | err := eval(fmt.Sprintf("%v configure -variable {%v} -value {%v}", btn.Id(), w.id, value)) 71 | if err != nil { 72 | return err 73 | } 74 | w.rds = append(w.rds, &_RadioData{btn, value, data}) 75 | btn.OnCommand(w.command.Invoke) 76 | return nil 77 | } 78 | 79 | func (w *RadioGroup) SetRadioData(btn *RadioButton, data interface{}) error { 80 | rd := w.findRadio(btn) 81 | if rd == nil { 82 | return ErrNotExist 83 | } 84 | rd.data = data 85 | return nil 86 | } 87 | 88 | func (w *RadioGroup) RadioList() (lst []*RadioButton) { 89 | for _, v := range w.rds { 90 | lst = append(lst, v.btn) 91 | } 92 | return 93 | } 94 | 95 | func (w *RadioGroup) WidgetList() (lst []Widget) { 96 | for _, v := range w.rds { 97 | lst = append(lst, v.btn) 98 | } 99 | return 100 | } 101 | 102 | func (w *RadioGroup) SetCheckedRadio(btn *RadioButton) error { 103 | rd := w.findRadio(btn) 104 | if rd == nil { 105 | return ErrInvalid 106 | } 107 | evalSetValue(w.id, rd.value) 108 | return nil 109 | } 110 | 111 | func (w *RadioGroup) CheckedRadio() *RadioButton { 112 | s := w.checkedValue() 113 | rd := w.findRadioByValue(s) 114 | if rd != nil { 115 | return rd.btn 116 | } 117 | return nil 118 | } 119 | 120 | func (w *RadioGroup) SetCheckedIndex(index int) error { 121 | if index < 0 || index > len(w.rds) { 122 | return ErrInvalid 123 | } 124 | evalSetValue(w.id, w.rds[index].value) 125 | return nil 126 | } 127 | 128 | func (w *RadioGroup) CheckedIndex() int { 129 | s := w.checkedValue() 130 | for n, rd := range w.rds { 131 | if rd.value == s { 132 | return n 133 | } 134 | } 135 | return -1 136 | } 137 | 138 | func (w *RadioGroup) checkedValue() string { 139 | return evalGetValue(w.id) 140 | } 141 | 142 | func (w *RadioGroup) CheckedData() interface{} { 143 | s := w.checkedValue() 144 | rd := w.findRadioByValue(s) 145 | if rd != nil { 146 | return rd.data 147 | } 148 | return nil 149 | } 150 | 151 | func (w *RadioGroup) RadioData(btn *RadioButton) interface{} { 152 | rd := w.findRadio(btn) 153 | if rd == nil { 154 | return nil 155 | } 156 | return rd.data 157 | } 158 | 159 | func (w *RadioGroup) OnRadioChanged(fn func()) error { 160 | if fn == nil { 161 | return ErrInvalid 162 | } 163 | w.command.Bind(fn) 164 | return nil 165 | } 166 | -------------------------------------------------------------------------------- /tk/radiobutton_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import "testing" 6 | 7 | func init() { 8 | registerTest("RadioButton", testRadioButton) 9 | } 10 | 11 | func testRadioButton(t *testing.T) { 12 | w := NewRadioButton(nil, "test", RadioButtonAttrText("text"), RadioButtonAttrWidth(20), RadioButtonAttrCompound(CompoundNone), RadioButtonAttrState(StateNormal), RadioButtonAttrTakeFocus(true)) 13 | defer w.Destroy() 14 | 15 | w.SetText("text") 16 | if v := w.Text(); v != "text" { 17 | t.Fatal("Text", "text", v) 18 | } 19 | 20 | w.SetWidth(20) 21 | if v := w.Width(); v != 20 { 22 | t.Fatal("Width", 20, v) 23 | } 24 | 25 | w.SetCompound(CompoundNone) 26 | if v := w.Compound(); v != CompoundNone { 27 | t.Fatal("Compound", CompoundNone, v) 28 | } 29 | 30 | w.SetState(StateNormal) 31 | if v := w.State(); v != StateNormal { 32 | t.Fatal("State", StateNormal, v) 33 | } 34 | 35 | w.SetTakeFocus(true) 36 | if v := w.IsTakeFocus(); v != true { 37 | t.Fatal("IsTakeFocus", true, v) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /tk/rsrc_windows_386.syso: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/visualfc/atk/735dfee35d3cb87829358a8c751aad1483c27f3a/tk/rsrc_windows_386.syso -------------------------------------------------------------------------------- /tk/rsrc_windows_amd64.syso: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/visualfc/atk/735dfee35d3cb87829358a8c751aad1483c27f3a/tk/rsrc_windows_amd64.syso -------------------------------------------------------------------------------- /tk/scale.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import "fmt" 6 | 7 | // scale 8 | type Scale struct { 9 | BaseWidget 10 | command *Command 11 | } 12 | 13 | func NewScale(parent Widget, orient Orient, attributes ...*WidgetAttr) *Scale { 14 | iid := makeNamedWidgetId(parent, "atk_separator") 15 | attributes = append(attributes, &WidgetAttr{"orient", orient}) 16 | info := CreateWidgetInfo(iid, WidgetTypeScale, true, attributes) 17 | if info == nil { 18 | return nil 19 | } 20 | w := &Scale{} 21 | w.id = iid 22 | w.info = info 23 | RegisterWidget(w) 24 | return w 25 | } 26 | 27 | func (w *Scale) Attach(id string) error { 28 | info, err := CheckWidgetInfo(id, WidgetTypeScale) 29 | if err != nil { 30 | return err 31 | } 32 | w.id = id 33 | w.info = info 34 | RegisterWidget(w) 35 | return nil 36 | } 37 | 38 | func (w *Scale) SetOrient(orient Orient) error { 39 | return eval(fmt.Sprintf("%v configure -orient {%v}", w.id, orient)) 40 | } 41 | 42 | func (w *Scale) Orient() Orient { 43 | r, err := evalAsString(fmt.Sprintf("%v cget -orient", w.id)) 44 | return parserOrientResult(r, err) 45 | } 46 | 47 | func (w *Scale) SetTakeFocus(takefocus bool) error { 48 | return eval(fmt.Sprintf("%v configure -takefocus {%v}", w.id, boolToInt(takefocus))) 49 | } 50 | 51 | func (w *Scale) IsTakeFocus() bool { 52 | r, _ := evalAsBool(fmt.Sprintf("%v cget -takefocus", w.id)) 53 | return r 54 | } 55 | 56 | func (w *Scale) SetFrom(from float64) error { 57 | return eval(fmt.Sprintf("%v configure -from {%v}", w.id, from)) 58 | } 59 | 60 | func (w *Scale) From() float64 { 61 | r, _ := evalAsFloat64(fmt.Sprintf("%v cget -from", w.id)) 62 | return r 63 | } 64 | 65 | func (w *Scale) SetTo(to float64) error { 66 | return eval(fmt.Sprintf("%v configure -to {%v}", w.id, to)) 67 | } 68 | 69 | func (w *Scale) To() float64 { 70 | r, _ := evalAsFloat64(fmt.Sprintf("%v cget -to", w.id)) 71 | return r 72 | } 73 | 74 | func (w *Scale) SetValue(value float64) error { 75 | return eval(fmt.Sprintf("%v configure -value {%v}", w.id, value)) 76 | } 77 | 78 | func (w *Scale) Value() float64 { 79 | r, _ := evalAsFloat64(fmt.Sprintf("%v cget -value", w.id)) 80 | return r 81 | } 82 | 83 | func (w *Scale) SetLength(length int) error { 84 | return eval(fmt.Sprintf("%v configure -length {%v}", w.id, length)) 85 | } 86 | 87 | func (w *Scale) Length() int { 88 | r, _ := evalAsInt(fmt.Sprintf("%v cget -length", w.id)) 89 | return r 90 | } 91 | 92 | func (w *Scale) OnCommand(fn func()) error { 93 | if fn == nil { 94 | return ErrInvalid 95 | } 96 | if w.command == nil { 97 | w.command = &Command{} 98 | bindCommand(w.id, "command", w.command.Invoke) 99 | } 100 | w.command.Bind(fn) 101 | return nil 102 | } 103 | 104 | func (w *Scale) SetRange(from, to float64) error { 105 | return eval(fmt.Sprintf("%v configure -from {%v} -to {%v}", w.id, from, to)) 106 | } 107 | 108 | func (w *Scale) Range() (float64, float64) { 109 | return w.From(), w.To() 110 | } 111 | 112 | func ScaleAttrOrient(orient Orient) *WidgetAttr { 113 | return &WidgetAttr{"orient", orient} 114 | } 115 | 116 | func ScaleAttrTakeFocus(takefocus bool) *WidgetAttr { 117 | return &WidgetAttr{"takefocus", boolToInt(takefocus)} 118 | } 119 | 120 | func ScaleAttrFrom(from float64) *WidgetAttr { 121 | return &WidgetAttr{"from", from} 122 | } 123 | 124 | func ScaleAttrTo(to float64) *WidgetAttr { 125 | return &WidgetAttr{"to", to} 126 | } 127 | 128 | func ScaleAttrValue(value float64) *WidgetAttr { 129 | return &WidgetAttr{"value", value} 130 | } 131 | 132 | func ScaleAttrLength(length int) *WidgetAttr { 133 | return &WidgetAttr{"length", length} 134 | } 135 | -------------------------------------------------------------------------------- /tk/scale_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import "testing" 6 | 7 | func init() { 8 | registerTest("Scale", testScale) 9 | } 10 | 11 | func testScale(t *testing.T) { 12 | w := NewScale(nil, Vertical) 13 | defer w.Destroy() 14 | 15 | w.SetOrient(Horizontal) 16 | if v := w.Orient(); v != Horizontal { 17 | t.Fatal("Orient", Horizontal, v) 18 | } 19 | 20 | w.SetTakeFocus(true) 21 | if v := w.IsTakeFocus(); v != true { 22 | t.Fatal("IsTakeFocus", true, v) 23 | } 24 | 25 | w.SetFrom(0) 26 | if v := w.From(); v != 0 { 27 | t.Fatal("From", 0, v) 28 | } 29 | 30 | w.SetTo(100) 31 | if v := w.To(); v != 100 { 32 | t.Fatal("To", 100, v) 33 | } 34 | 35 | w.SetValue(0.5) 36 | if v := w.Value(); v != 0.5 { 37 | t.Fatal("Value", 0.5, v) 38 | } 39 | 40 | w.SetLength(20) 41 | if v := w.Length(); v != 20 { 42 | t.Fatal("Length", 20, v) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /tk/scrollbar.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import ( 6 | "fmt" 7 | "strings" 8 | ) 9 | 10 | // scrollbar 11 | type ScrollBar struct { 12 | BaseWidget 13 | command *CommandEx 14 | } 15 | 16 | func NewScrollBar(parent Widget, orient Orient, attributes ...*WidgetAttr) *ScrollBar { 17 | theme := checkInitUseTheme(attributes) 18 | iid := makeNamedWidgetId(parent, "atk_scrollbar") 19 | attributes = append(attributes, &WidgetAttr{"orient", orient}) 20 | info := CreateWidgetInfo(iid, WidgetTypeScrollBar, theme, attributes) 21 | if info == nil { 22 | return nil 23 | } 24 | w := &ScrollBar{} 25 | w.id = iid 26 | w.info = info 27 | RegisterWidget(w) 28 | return w 29 | } 30 | 31 | func (w *ScrollBar) Attach(id string) error { 32 | info, err := CheckWidgetInfo(id, WidgetTypeScrollBar) 33 | if err != nil { 34 | return err 35 | } 36 | w.id = id 37 | w.info = info 38 | RegisterWidget(w) 39 | return nil 40 | } 41 | 42 | func (w *ScrollBar) SetOrient(orient Orient) error { 43 | return eval(fmt.Sprintf("%v configure -orient {%v}", w.id, orient)) 44 | } 45 | 46 | func (w *ScrollBar) Orient() Orient { 47 | r, err := evalAsString(fmt.Sprintf("%v cget -orient", w.id)) 48 | return parserOrientResult(r, err) 49 | } 50 | 51 | func (w *ScrollBar) SetTakeFocus(takefocus bool) error { 52 | return eval(fmt.Sprintf("%v configure -takefocus {%v}", w.id, boolToInt(takefocus))) 53 | } 54 | 55 | func (w *ScrollBar) IsTakeFocus() bool { 56 | r, _ := evalAsBool(fmt.Sprintf("%v cget -takefocus", w.id)) 57 | return r 58 | } 59 | 60 | func (w *ScrollBar) SetScroll(first float64, last float64) error { 61 | err := eval(fmt.Sprintf("%v set %v %v", w.id, first, last)) 62 | return err 63 | } 64 | 65 | func (w *ScrollBar) SetScrollArgs(args []string) error { 66 | err := eval(fmt.Sprintf("%v set %v", w.id, strings.Join(args, " "))) 67 | return err 68 | } 69 | 70 | func (w *ScrollBar) OnCommandEx(fn func([]string) error) error { 71 | if fn == nil { 72 | return ErrInvalid 73 | } 74 | if w.command == nil { 75 | w.command = &CommandEx{} 76 | bindCommandEx(w.id, "command", w.command.Invoke) 77 | } 78 | w.command.Bind(fn) 79 | return nil 80 | } 81 | 82 | func ScrollBarAttrOrient(orient Orient) *WidgetAttr { 83 | return &WidgetAttr{"orient", orient} 84 | } 85 | 86 | func ScrollBarAttrTakeFocus(takefocus bool) *WidgetAttr { 87 | return &WidgetAttr{"takefocus", boolToInt(takefocus)} 88 | } 89 | -------------------------------------------------------------------------------- /tk/scrollbar_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import "testing" 6 | 7 | func init() { 8 | registerTest("ScrollBar", testScrollBar) 9 | } 10 | 11 | func testScrollBar(t *testing.T) { 12 | w := NewScrollBar(nil, Vertical) 13 | defer w.Destroy() 14 | 15 | w.SetOrient(Horizontal) 16 | if v := w.Orient(); v != Horizontal { 17 | t.Fatal("Orient", Horizontal, v) 18 | } 19 | 20 | w.SetTakeFocus(true) 21 | if v := w.IsTakeFocus(); v != true { 22 | t.Fatal("IsTakeFocus", true, v) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tk/scrolllayout.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | type ScrollLayout struct { 6 | *GridLayout 7 | XScrollBar *ScrollBar 8 | YScrollBar *ScrollBar 9 | } 10 | 11 | func NewScrollLayout(parent Widget) *ScrollLayout { 12 | grid := NewGridLayout(parent) 13 | xscroll := NewScrollBar(parent, Horizontal) 14 | yscroll := NewScrollBar(parent, Vertical) 15 | grid.AddWidget(xscroll, GridAttrRow(1), GridAttrColumn(0), GridAttrSticky(StickyEW)) 16 | grid.AddWidget(yscroll, GridAttrRow(0), GridAttrColumn(1), GridAttrSticky(StickyNS)) 17 | return &ScrollLayout{grid, xscroll, yscroll} 18 | } 19 | 20 | func (w *ScrollLayout) SetWidget(widget Widget) error { 21 | if !IsValidWidget(widget) { 22 | return ErrInvalid 23 | } 24 | w.AddWidget(widget, GridAttrRow(0), GridAttrColumn(0), GridAttrSticky(StickyAll)) 25 | w.SetRowAttr(0, 0, 1, "") 26 | w.SetColumnAttr(0, 0, 1, "") 27 | return nil 28 | } 29 | 30 | //export embedded id 31 | func (w *ScrollLayout) Id() string { 32 | return w.id 33 | } 34 | 35 | func (w *ScrollLayout) ShowXScrollBar(b bool) (err error) { 36 | if b { 37 | err = w.AddWidget(w.XScrollBar, GridAttrRow(1), GridAttrColumn(0), GridAttrSticky(StickyEW)) 38 | } else { 39 | err = w.RemoveWidget(w.XScrollBar) 40 | } 41 | return 42 | } 43 | 44 | func (w *ScrollLayout) ShowYScrollBar(b bool) (err error) { 45 | if b { 46 | err = w.AddWidget(w.YScrollBar, GridAttrRow(1), GridAttrColumn(0), GridAttrSticky(StickyEW)) 47 | } else { 48 | err = w.RemoveWidget(w.YScrollBar) 49 | } 50 | return 51 | } 52 | -------------------------------------------------------------------------------- /tk/separator.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import "fmt" 6 | 7 | // separator 8 | type Separator struct { 9 | BaseWidget 10 | } 11 | 12 | func NewSeparator(parent Widget, orient Orient, attributes ...*WidgetAttr) *Separator { 13 | iid := makeNamedWidgetId(parent, "atk_separator") 14 | attributes = append(attributes, &WidgetAttr{"orient", orient}) 15 | info := CreateWidgetInfo(iid, WidgetTypeSeparator, true, attributes) 16 | if info == nil { 17 | return nil 18 | } 19 | w := &Separator{} 20 | w.id = iid 21 | w.info = info 22 | RegisterWidget(w) 23 | return w 24 | } 25 | 26 | func (w *Separator) Attach(id string) error { 27 | info, err := CheckWidgetInfo(id, WidgetTypeSeparator) 28 | if err != nil { 29 | return err 30 | } 31 | w.id = id 32 | w.info = info 33 | RegisterWidget(w) 34 | return nil 35 | } 36 | 37 | func (w *Separator) SetOrient(orient Orient) error { 38 | return eval(fmt.Sprintf("%v configure -orient {%v}", w.id, orient)) 39 | } 40 | 41 | func (w *Separator) Orient() Orient { 42 | r, err := evalAsString(fmt.Sprintf("%v cget -orient", w.id)) 43 | return parserOrientResult(r, err) 44 | } 45 | 46 | func (w *Separator) SetTakeFocus(takefocus bool) error { 47 | return eval(fmt.Sprintf("%v configure -takefocus {%v}", w.id, boolToInt(takefocus))) 48 | } 49 | 50 | func (w *Separator) IsTakeFocus() bool { 51 | r, _ := evalAsBool(fmt.Sprintf("%v cget -takefocus", w.id)) 52 | return r 53 | } 54 | 55 | func SeparatorAttrOrient(orient Orient) *WidgetAttr { 56 | return &WidgetAttr{"orient", orient} 57 | } 58 | 59 | func SeparatorAttrTakeFocus(takefocus bool) *WidgetAttr { 60 | return &WidgetAttr{"takefocus", boolToInt(takefocus)} 61 | } 62 | -------------------------------------------------------------------------------- /tk/separator_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import "testing" 6 | 7 | func init() { 8 | registerTest("Separator", testSeparator) 9 | } 10 | 11 | func testSeparator(t *testing.T) { 12 | w := NewSeparator(nil, Vertical) 13 | defer w.Destroy() 14 | 15 | w.SetOrient(Horizontal) 16 | if v := w.Orient(); v != Horizontal { 17 | t.Fatal("Orient", Horizontal, v) 18 | } 19 | 20 | w.SetTakeFocus(true) 21 | if v := w.IsTakeFocus(); v != true { 22 | t.Fatal("IsTakeFocus", true, v) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tk/spinbox.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import "fmt" 6 | 7 | // spinbox 8 | type SpinBox struct { 9 | BaseWidget 10 | command *Command 11 | xscrollcommand *CommandEx 12 | } 13 | 14 | func NewSpinBox(parent Widget, attributes ...*WidgetAttr) *SpinBox { 15 | theme := checkInitUseTheme(attributes) 16 | iid := makeNamedWidgetId(parent, "atk_spinbox") 17 | info := CreateWidgetInfo(iid, WidgetTypeSpinBox, theme, attributes) 18 | if info == nil { 19 | return nil 20 | } 21 | w := &SpinBox{} 22 | w.id = iid 23 | w.info = info 24 | RegisterWidget(w) 25 | return w 26 | } 27 | 28 | func (w *SpinBox) Attach(id string) error { 29 | info, err := CheckWidgetInfo(id, WidgetTypeSpinBox) 30 | if err != nil { 31 | return err 32 | } 33 | w.id = id 34 | w.info = info 35 | RegisterWidget(w) 36 | return nil 37 | } 38 | 39 | func (w *SpinBox) SetTakeFocus(takefocus bool) error { 40 | return eval(fmt.Sprintf("%v configure -takefocus {%v}", w.id, boolToInt(takefocus))) 41 | } 42 | 43 | func (w *SpinBox) IsTakeFocus() bool { 44 | r, _ := evalAsBool(fmt.Sprintf("%v cget -takefocus", w.id)) 45 | return r 46 | } 47 | 48 | func (w *SpinBox) SetFrom(from float64) error { 49 | return eval(fmt.Sprintf("%v configure -from {%v}", w.id, from)) 50 | } 51 | 52 | func (w *SpinBox) From() float64 { 53 | r, _ := evalAsFloat64(fmt.Sprintf("%v cget -from", w.id)) 54 | return r 55 | } 56 | 57 | func (w *SpinBox) SetTo(to float64) error { 58 | return eval(fmt.Sprintf("%v configure -to {%v}", w.id, to)) 59 | } 60 | 61 | func (w *SpinBox) To() float64 { 62 | r, _ := evalAsFloat64(fmt.Sprintf("%v cget -to", w.id)) 63 | return r 64 | } 65 | 66 | func (w *SpinBox) SetIncrement(increment float64) error { 67 | return eval(fmt.Sprintf("%v configure -increment {%v}", w.id, increment)) 68 | } 69 | 70 | func (w *SpinBox) Increment() float64 { 71 | r, _ := evalAsFloat64(fmt.Sprintf("%v cget -increment", w.id)) 72 | return r 73 | } 74 | 75 | func (w *SpinBox) SetWrap(wrap bool) error { 76 | return eval(fmt.Sprintf("%v configure -wrap {%v}", w.id, boolToInt(wrap))) 77 | } 78 | 79 | func (w *SpinBox) IsWrap() bool { 80 | r, _ := evalAsBool(fmt.Sprintf("%v cget -wrap", w.id)) 81 | return r 82 | } 83 | 84 | func (w *SpinBox) SetTextValues(values []string) error { 85 | setObjTextList("atk_tmp_textlist", values) 86 | return eval(fmt.Sprintf("%v configure -values $atk_tmp_textlist", w.id)) 87 | } 88 | 89 | func (w *SpinBox) TextValues() []string { 90 | r, _ := evalAsStringList(fmt.Sprintf("%v cget -values", w.id)) 91 | return r 92 | } 93 | 94 | func (w *SpinBox) OnCommand(fn func()) error { 95 | if fn == nil { 96 | return ErrInvalid 97 | } 98 | if w.command == nil { 99 | w.command = &Command{} 100 | bindCommand(w.id, "command", w.command.Invoke) 101 | } 102 | w.command.Bind(fn) 103 | return nil 104 | } 105 | 106 | func (w *SpinBox) OnXScrollEx(fn func([]string) error) error { 107 | if fn == nil { 108 | return ErrInvalid 109 | } 110 | if w.xscrollcommand == nil { 111 | w.xscrollcommand = &CommandEx{} 112 | bindCommandEx(w.id, "xscrollcommand", w.xscrollcommand.Invoke) 113 | } 114 | w.xscrollcommand.Bind(fn) 115 | return nil 116 | } 117 | 118 | func (w *SpinBox) OnEditReturn(fn func()) error { 119 | if fn == nil { 120 | return ErrInvalid 121 | } 122 | w.BindEvent("", func(e *Event) { 123 | fn() 124 | }) 125 | return nil 126 | } 127 | 128 | func (w *SpinBox) Entry() *Entry { 129 | return &Entry{w.BaseWidget, nil} 130 | } 131 | 132 | func (w *SpinBox) SetRange(from, to float64) error { 133 | return eval(fmt.Sprintf("%v configure -from {%v} -to {%v}", w.id, from, to)) 134 | } 135 | 136 | func (w *SpinBox) Range() (float64, float64) { 137 | return w.From(), w.To() 138 | } 139 | 140 | func (w *SpinBox) Value() float64 { 141 | r, _ := evalAsFloat64(fmt.Sprintf("%v get", w.id)) 142 | return r 143 | } 144 | 145 | func (w *SpinBox) SetValue(value float64) error { 146 | return eval(fmt.Sprintf("%v set %v", w.id, value)) 147 | } 148 | 149 | func (w *SpinBox) SetTextValue(value string) error { 150 | return eval(fmt.Sprintf("%v set %v", w.id, value)) 151 | } 152 | 153 | func (w *SpinBox) TextValue() string { 154 | r, _ := evalAsString(fmt.Sprintf("%v get", w.id)) 155 | return r 156 | } 157 | 158 | func SpinBoxAttrTakeFocus(takefocus bool) *WidgetAttr { 159 | return &WidgetAttr{"takefocus", boolToInt(takefocus)} 160 | } 161 | 162 | func SpinBoxAttrFrom(from float64) *WidgetAttr { 163 | return &WidgetAttr{"from", from} 164 | } 165 | 166 | func SpinBoxAttrTo(to float64) *WidgetAttr { 167 | return &WidgetAttr{"to", to} 168 | } 169 | 170 | func SpinBoxAttrIncrement(increment float64) *WidgetAttr { 171 | return &WidgetAttr{"increment", increment} 172 | } 173 | 174 | func SpinBoxAttrWrap(wrap bool) *WidgetAttr { 175 | return &WidgetAttr{"wrap", boolToInt(wrap)} 176 | } 177 | 178 | func SpinBoxAttrTextValues(values []string) *WidgetAttr { 179 | return &WidgetAttr{"values", values} 180 | } 181 | -------------------------------------------------------------------------------- /tk/spinbox_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import "testing" 6 | 7 | func init() { 8 | registerTest("SpinBox", testSpinBox) 9 | } 10 | 11 | func testSpinBox(t *testing.T) { 12 | w := NewSpinBox(nil, SpinBoxAttrTakeFocus(true), SpinBoxAttrFrom(0), SpinBoxAttrTo(100), SpinBoxAttrIncrement(0.5), SpinBoxAttrWrap(true)) 13 | defer w.Destroy() 14 | 15 | w.SetTakeFocus(true) 16 | if v := w.IsTakeFocus(); v != true { 17 | t.Fatal("IsTakeFocus", true, v) 18 | } 19 | 20 | w.SetFrom(0) 21 | if v := w.From(); v != 0 { 22 | t.Fatal("From", 0, v) 23 | } 24 | 25 | w.SetTo(100) 26 | if v := w.To(); v != 100 { 27 | t.Fatal("To", 100, v) 28 | } 29 | 30 | w.SetIncrement(0.5) 31 | if v := w.Increment(); v != 0.5 { 32 | t.Fatal("Increment", 0.5, v) 33 | } 34 | 35 | w.SetWrap(true) 36 | if v := w.IsWrap(); v != true { 37 | t.Fatal("IsWrap", true, v) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /tk/text_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import "testing" 6 | 7 | func init() { 8 | registerTest("Text", testText) 9 | } 10 | 11 | func testText(t *testing.T) { 12 | w := NewText(nil, TextAttrBackground("blue"), TextAttrBorderWidth(20), TextAttrForeground("blue"), TextAttrHighlightBackground("blue"), TextAttrHighlightColor("blue"), TextAttrHighlightthickness(20), TextAttrInsertBackground("blue"), TextAttrInsertBorderWidth(20), TextAttrInsertOffTime(20), TextAttrInsertOnTime(20), TextAttrInsertWidth(20), TextAttrReliefStyle(1), TextAttrSelectBackground("blue"), TextAttrSelectborderwidth(20), TextAttrSelectforeground("blue"), TextAttrInactiveSelectBackground("blue"), TextAttrTakeFocus(true), TextAttrAutoSeparatorsOnUndo(true), TextAttrBlockCursor(true), TextAttrWidth(20), TextAttrHeight(20), TextAttrInsertUnfocussed(DisplyCursorHollow), TextAttrMaxUndo(20), TextAttrLineAboveSpace(20), TextAttrLineWrapSpace(20), TextAttrLineBelowSpace(20), TextAttrLineWrap(LineWrapNone), TextAttrEnableUndo(true)) 13 | defer w.Destroy() 14 | 15 | w.SetBackground("blue") 16 | if v := w.Background(); v != "blue" { 17 | t.Fatal("Background", "blue", v) 18 | } 19 | 20 | w.SetBorderWidth(20) 21 | if v := w.BorderWidth(); v != 20 { 22 | t.Fatal("BorderWidth", 20, v) 23 | } 24 | 25 | w.SetForeground("blue") 26 | if v := w.Foreground(); v != "blue" { 27 | t.Fatal("Foreground", "blue", v) 28 | } 29 | 30 | w.SetHighlightBackground("blue") 31 | if v := w.HighlightBackground(); v != "blue" { 32 | t.Fatal("HighlightBackground", "blue", v) 33 | } 34 | 35 | w.SetHighlightColor("blue") 36 | if v := w.HighlightColor(); v != "blue" { 37 | t.Fatal("HighlightColor", "blue", v) 38 | } 39 | 40 | w.SetHighlightthickness(20) 41 | if v := w.Highlightthickness(); v != 20 { 42 | t.Fatal("Highlightthickness", 20, v) 43 | } 44 | 45 | w.SetInsertBackground("blue") 46 | if v := w.InsertBackground(); v != "blue" { 47 | t.Fatal("InsertBackground", "blue", v) 48 | } 49 | 50 | w.SetInsertBorderWidth(20) 51 | if v := w.InsertBorderWidth(); v != 20 { 52 | t.Fatal("InsertBorderWidth", 20, v) 53 | } 54 | 55 | w.SetInsertOffTime(20) 56 | if v := w.InsertOffTime(); v != 20 { 57 | t.Fatal("InsertOffTime", 20, v) 58 | } 59 | 60 | w.SetInsertOnTime(20) 61 | if v := w.InsertOnTime(); v != 20 { 62 | t.Fatal("InsertOnTime", 20, v) 63 | } 64 | 65 | w.SetInsertWidth(20) 66 | if v := w.InsertWidth(); v != 20 { 67 | t.Fatal("InsertWidth", 20, v) 68 | } 69 | 70 | w.SetReliefStyle(1) 71 | if v := w.ReliefStyle(); v != 1 { 72 | t.Fatal("ReliefStyle", 1, v) 73 | } 74 | 75 | w.SetSelectBackground("blue") 76 | if v := w.SelectBackground(); v != "blue" { 77 | t.Fatal("SelectBackground", "blue", v) 78 | } 79 | 80 | w.SetSelectborderwidth(20) 81 | if v := w.Selectborderwidth(); v != 20 { 82 | t.Fatal("Selectborderwidth", 20, v) 83 | } 84 | 85 | w.SetSelectforeground("blue") 86 | if v := w.Selectforeground(); v != "blue" { 87 | t.Fatal("Selectforeground", "blue", v) 88 | } 89 | 90 | w.SetInactiveSelectBackground("blue") 91 | if v := w.InactiveSelectBackground(); v != "blue" { 92 | t.Fatal("InactiveSelectBackground", "blue", v) 93 | } 94 | 95 | w.SetTakeFocus(true) 96 | if v := w.IsTakeFocus(); v != true { 97 | t.Fatal("IsTakeFocus", true, v) 98 | } 99 | 100 | w.SetAutoSeparatorsOnUndo(true) 101 | if v := w.IsAutoSeparatorsOnUndo(); v != true { 102 | t.Fatal("IsAutoSeparatorsOnUndo", true, v) 103 | } 104 | 105 | w.SetBlockCursor(true) 106 | if v := w.IsBlockCursor(); v != true { 107 | t.Fatal("IsBlockCursor", true, v) 108 | } 109 | 110 | w.SetWidth(20) 111 | if v := w.Width(); v != 20 { 112 | t.Fatal("Width", 20, v) 113 | } 114 | 115 | w.SetHeight(20) 116 | if v := w.Height(); v != 20 { 117 | t.Fatal("Height", 20, v) 118 | } 119 | 120 | w.SetInsertUnfocussed(DisplyCursorHollow) 121 | if v := w.InsertUnfocussed(); v != DisplyCursorHollow { 122 | t.Fatal("InsertUnfocussed", DisplyCursorHollow, v) 123 | } 124 | 125 | w.SetMaxUndo(20) 126 | if v := w.MaxUndo(); v != 20 { 127 | t.Fatal("MaxUndo", 20, v) 128 | } 129 | 130 | w.SetLineAboveSpace(20) 131 | if v := w.LineAboveSpace(); v != 20 { 132 | t.Fatal("LineAboveSpace", 20, v) 133 | } 134 | 135 | w.SetLineWrapSpace(20) 136 | if v := w.LineWrapSpace(); v != 20 { 137 | t.Fatal("LineWrapSpace", 20, v) 138 | } 139 | 140 | w.SetLineBelowSpace(20) 141 | if v := w.LineBelowSpace(); v != 20 { 142 | t.Fatal("LineBelowSpace", 20, v) 143 | } 144 | 145 | w.SetLineWrap(LineWrapNone) 146 | if v := w.LineWrap(); v != LineWrapNone { 147 | t.Fatal("LineWrap", LineWrapNone, v) 148 | } 149 | 150 | w.SetEnableUndo(true) 151 | if v := w.IsEnableUndo(); v != true { 152 | t.Fatal("IsEnableUndo", true, v) 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /tk/theme.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | type NativeAttr struct { 6 | Key string 7 | Value string 8 | } 9 | 10 | type Theme interface { 11 | Name() string 12 | IsTtk() bool 13 | InitAttributes(typ WidgetType) []NativeAttr 14 | } 15 | 16 | func SetMainTheme(theme Theme) { 17 | mainTheme = theme 18 | } 19 | 20 | func MainTheme() Theme { 21 | return mainTheme 22 | } 23 | 24 | func HasTheme() bool { 25 | return mainTheme != nil 26 | } 27 | 28 | var ( 29 | mainTheme Theme 30 | ) 31 | -------------------------------------------------------------------------------- /tk/theme_ttk.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import ( 6 | "fmt" 7 | ) 8 | 9 | type ttkTheme struct { 10 | } 11 | 12 | func (t *ttkTheme) Name() string { 13 | return "ttk theme" 14 | } 15 | 16 | func (t *ttkTheme) IsTtk() bool { 17 | return true 18 | } 19 | 20 | func (t *ttkTheme) InitAttributes(typ WidgetType) []NativeAttr { 21 | return nil 22 | } 23 | 24 | func (t *ttkTheme) ThemeIdList() []string { 25 | ttk_theme_list, _ := evalAsStringList("ttk::themes") 26 | return ttk_theme_list 27 | } 28 | 29 | func (t *ttkTheme) SetThemeId(id string) error { 30 | for _, v := range t.ThemeIdList() { 31 | if v == id { 32 | err := eval(fmt.Sprintf("ttk::setTheme %v", id)) 33 | return err 34 | } 35 | } 36 | err := fmt.Errorf("not found ttk_theme id:%v", id) 37 | dumpError(err) 38 | return err 39 | } 40 | 41 | func (t *ttkTheme) ThemeId() string { 42 | r, _ := evalAsString("ttk::style theme use") 43 | return r 44 | } 45 | 46 | var ( 47 | TtkTheme = &ttkTheme{} 48 | ) 49 | 50 | func init() { 51 | /* 52 | registerInit(func() { 53 | ttk_theme_list, _ = evalAsStringList("ttk::themes") 54 | }) 55 | */ 56 | SetMainTheme(TtkTheme) 57 | } 58 | -------------------------------------------------------------------------------- /tk/tk.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import ( 6 | "fmt" 7 | "log" 8 | "runtime" 9 | 10 | "github.com/visualfc/atk/tk/interp" 11 | ) 12 | 13 | var ( 14 | tkHasInit bool 15 | tkWindowInitAutoHide bool 16 | mainInterp *interp.Interp 17 | rootWindow *Window 18 | fnErrorHandle func(error) = func(err error) { 19 | log.Println(err) 20 | } 21 | ) 22 | 23 | func Init() error { 24 | return InitEx(true, "", "") 25 | } 26 | 27 | func InitEx(tk_window_init_hide bool, tcl_library string, tk_library string) (err error) { 28 | mainInterp, err = interp.NewInterp() 29 | if err != nil { 30 | dumpError(err) 31 | return err 32 | } 33 | err = mainInterp.InitTcl(tcl_library) 34 | if err != nil { 35 | dumpError(err) 36 | return err 37 | } 38 | err = mainInterp.InitTk(tk_library) 39 | if err != nil { 40 | dumpError(err) 41 | return err 42 | } 43 | 44 | tkWindowInitAutoHide = tk_window_init_hide 45 | //hide console for macOS bundle 46 | mainInterp.Eval("if {[info commands console] == \"console\"} {console hide}") 47 | 48 | for _, fn := range init_func_list { 49 | fn() 50 | } 51 | rootWindow = &Window{} 52 | rootWindow.Attach(".") 53 | if tkWindowInitAutoHide { 54 | rootWindow.Hide() 55 | } 56 | //hide wish menu on macos 57 | rootWindow.SetMenu(NewMenu(nil)) 58 | tkHasInit = true 59 | return nil 60 | } 61 | 62 | var ( 63 | init_func_list []func() 64 | ) 65 | 66 | func registerInit(fn func()) { 67 | init_func_list = append(init_func_list, fn) 68 | } 69 | 70 | func SetErrorHandle(fn func(error)) { 71 | fnErrorHandle = fn 72 | } 73 | 74 | func MainInterp() *interp.Interp { 75 | return mainInterp 76 | } 77 | 78 | func TclVersion() (ver string) { 79 | return mainInterp.TclVersion() 80 | } 81 | 82 | func TkVersion() (ver string) { 83 | return mainInterp.TkVersion() 84 | } 85 | 86 | func TclLibary() (path string) { 87 | path, _ = evalAsString("set tcl_library") 88 | return 89 | } 90 | 91 | func TkLibrary() (path string) { 92 | path, _ = evalAsString("set tk_library") 93 | return 94 | } 95 | 96 | func MainLoop(fn func()) error { 97 | runtime.LockOSThread() 98 | defer runtime.UnlockOSThread() 99 | if !tkHasInit { 100 | err := Init() 101 | if err != nil { 102 | dumpError(err) 103 | return err 104 | } 105 | } 106 | interp.MainLoop(fn) 107 | return nil 108 | } 109 | 110 | func Async(fn func()) { 111 | interp.Async(fn) 112 | } 113 | 114 | func Update() { 115 | eval("update") 116 | } 117 | 118 | func Quit() { 119 | Async(func() { 120 | DestroyWidget(rootWindow) 121 | }) 122 | } 123 | 124 | func eval(script string) error { 125 | err := mainInterp.Eval(script) 126 | if err != nil { 127 | dumpError(fmt.Errorf("script: %q, error: %q", script, err)) 128 | } 129 | return err 130 | } 131 | 132 | func evalEx(script string, dump bool) error { 133 | err := mainInterp.Eval(script) 134 | if dump && err != nil { 135 | dumpError(err) 136 | } 137 | return err 138 | } 139 | 140 | func evalAsString(script string) (string, error) { 141 | r, err := mainInterp.EvalAsString(script) 142 | if err != nil { 143 | dumpError(err) 144 | } 145 | return r, err 146 | } 147 | 148 | func evalAsStringEx(script string, dump bool) (string, error) { 149 | r, err := mainInterp.EvalAsString(script) 150 | if dump && err != nil { 151 | dumpError(err) 152 | } 153 | return r, err 154 | } 155 | 156 | func evalAsInt(script string) (int, error) { 157 | r, err := mainInterp.EvalAsInt(script) 158 | if err != nil { 159 | dumpError(err) 160 | } 161 | return r, err 162 | } 163 | 164 | func evalAsIntEx(script string, dump bool) (int, error) { 165 | r, err := mainInterp.EvalAsInt(script) 166 | if dump && err != nil { 167 | dumpError(err) 168 | } 169 | return r, err 170 | } 171 | 172 | func evalAsUint(script string) (uint, error) { 173 | r, err := mainInterp.EvalAsUint(script) 174 | if err != nil { 175 | dumpError(err) 176 | } 177 | return r, err 178 | } 179 | 180 | func evalAsUintEx(script string, dump bool) (uint, error) { 181 | r, err := mainInterp.EvalAsUint(script) 182 | if dump && err != nil { 183 | dumpError(err) 184 | } 185 | return r, err 186 | } 187 | 188 | func evalAsFloat64(script string) (float64, error) { 189 | r, err := mainInterp.EvalAsFloat64(script) 190 | if err != nil { 191 | dumpError(err) 192 | } 193 | return r, err 194 | } 195 | 196 | func evalAsFloat64Ex(script string, dump bool) (float64, error) { 197 | r, err := mainInterp.EvalAsFloat64(script) 198 | if dump && err != nil { 199 | dumpError(err) 200 | } 201 | return r, err 202 | } 203 | 204 | func evalAsBool(script string) (bool, error) { 205 | r, err := mainInterp.EvalAsBool(script) 206 | if err != nil { 207 | dumpError(err) 208 | } 209 | return r, err 210 | } 211 | 212 | func evalAsBoolEx(script string, dump bool) (bool, error) { 213 | r, err := mainInterp.EvalAsBool(script) 214 | if dump && err != nil { 215 | dumpError(err) 216 | } 217 | return r, err 218 | } 219 | 220 | func evalAsStringList(script string) ([]string, error) { 221 | r, err := mainInterp.EvalAsStringList(script) 222 | if err != nil { 223 | dumpError(err) 224 | } 225 | return r, err 226 | } 227 | 228 | func evalAsStringListEx(script string, dump bool) ([]string, error) { 229 | r, err := mainInterp.EvalAsStringList(script) 230 | if dump && err != nil { 231 | dumpError(err) 232 | } 233 | return r, err 234 | } 235 | 236 | func evalAsIntList(script string) ([]int, error) { 237 | r, err := mainInterp.EvalAsIntList(script) 238 | if err != nil { 239 | dumpError(err) 240 | } 241 | return r, err 242 | } 243 | 244 | func evalAsIntListEx(script string, dump bool) ([]int, error) { 245 | r, err := mainInterp.EvalAsIntList(script) 246 | if dump && err != nil { 247 | dumpError(err) 248 | } 249 | return r, err 250 | } 251 | 252 | func dumpError(err error) { 253 | if fnErrorHandle != nil { 254 | fnErrorHandle(fmt.Errorf("%v", err)) 255 | } 256 | } 257 | 258 | func setObjText(obj string, text string) { 259 | mainInterp.SetStringVar(obj, text, false) 260 | } 261 | 262 | func setObjTextList(obj string, list []string) { 263 | mainInterp.SetStringList(obj, list, false) 264 | } 265 | -------------------------------------------------------------------------------- /tk/tk.manifest: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | true 15 | 16 | 17 | -------------------------------------------------------------------------------- /tk/tk_rsrc.go: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | 3 | package tk 4 | 5 | // go get github.com/akavel/rsrc 6 | 7 | //go:generate rsrc -manifest tk.manifest -arch amd64 8 | //go:generate rsrc -manifest tk.manifest -arch 386 9 | -------------------------------------------------------------------------------- /tk/tk_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import ( 6 | "fmt" 7 | "testing" 8 | "time" 9 | ) 10 | 11 | func init() { 12 | err := Init() 13 | if err != nil { 14 | panic(err) 15 | } 16 | fnErrorHandle = func(err error) { 17 | panic(err) 18 | } 19 | fmt.Println("TkVersion", TkVersion()) 20 | fmt.Println("TkLibrary", TkLibrary()) 21 | } 22 | 23 | var ( 24 | allTestMap = make(map[string]func(*testing.T)) 25 | ) 26 | 27 | func registerTest(name string, fn func(*testing.T)) { 28 | allTestMap[name] = fn 29 | } 30 | 31 | func TestMain(t *testing.T) { 32 | MainLoop(func() { 33 | t.Log("sub test", "RootWindow") 34 | testRootWindow(t) 35 | 36 | for name, fn := range allTestMap { 37 | t.Log("sub test", name) 38 | fn(t) 39 | } 40 | t.Log("sub test", "Async") 41 | go func() { 42 | <-time.After(1) 43 | Async(func() { 44 | Quit() 45 | }) 46 | }() 47 | }) 48 | } 49 | 50 | func testRootWindow(t *testing.T) { 51 | mw := RootWindow() 52 | 53 | mw.SetTitle("Hello") 54 | if mw.Title() != "Hello" { 55 | t.Error("SetTitle") 56 | } 57 | mw.SetAlpha(0.9) 58 | if mw.Alpha() != 0.9 { 59 | t.Error("SetAlpha") 60 | } 61 | 62 | mw.SetVisible(false) 63 | if mw.IsVisible() { 64 | t.Error("SetVisible") 65 | } 66 | mw.SetVisible(true) 67 | if !mw.IsVisible() { 68 | t.Error("SetVisible") 69 | } 70 | 71 | mw.Iconify() 72 | if !mw.IsIconify() { 73 | t.Error("Iconify") 74 | } 75 | mw.ShowNormal() 76 | 77 | mw.SetTopmost(true) 78 | Update() 79 | if !mw.IsTopmost() { 80 | t.Error("SetTopmost") 81 | } 82 | mw.SetTopmost(false) 83 | 84 | mw.SetGeometryN(100, 200, 300, 400) 85 | x, y, w, h := mw.GeometryN() 86 | if x != 100 || y != 200 || w != 300 || h != 400 { 87 | t.Error("Geometry", x, y, w, h) 88 | } 89 | mw.SetPosN(101, 202) 90 | x, y = mw.PosN() 91 | if x != 101 || y != 202 { 92 | t.Error("Pos", x, y) 93 | } 94 | mw.SetSizeN(301, 302) 95 | w, h = mw.SizeN() 96 | if w != 301 || h != 302 { 97 | t.Error("Size", w, h) 98 | } 99 | 100 | mw.ShowMaximized() 101 | if !mw.IsMaximized() { 102 | t.Error("IsMaximized") 103 | } 104 | mw.ShowNormal() 105 | 106 | mw.SetResizable(false, false) 107 | enableW, enableH := mw.IsResizable() 108 | if enableW != false || enableH != false { 109 | t.Error("Resizable") 110 | } 111 | mw.SetResizable(true, true) 112 | 113 | mw.SetWidth(311) 114 | mw.SetHeight(312) 115 | if mw.Width() != 311 || mw.Height() != 312 { 116 | t.Error("Width/Height") 117 | } 118 | 119 | // mw.SetFullScreen(true) 120 | // //Update() 121 | // if !mw.IsFullScreen() { 122 | // t.Error("IsFullScreen") 123 | // } 124 | // mw.SetFullScreen(false) 125 | 126 | mw.SetMaximumSizeN(500, 600) 127 | w, h = mw.MaximumSizeN() 128 | if w != 500 || h != 600 { 129 | t.Error("MaximumSize") 130 | } 131 | 132 | mw.SetMinimumSizeN(200, 300) 133 | w, h = mw.MinimumSizeN() 134 | if w != 200 || h != 300 { 135 | t.Error("MinimumSize") 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /tk/treeitem.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import "fmt" 6 | 7 | type TreeItem struct { 8 | tree *TreeView 9 | id string 10 | } 11 | 12 | func (t *TreeItem) Id() string { 13 | return t.id 14 | } 15 | 16 | func (t *TreeItem) IsValid() bool { 17 | return t != nil && t.tree != nil 18 | } 19 | 20 | func (t *TreeItem) InsertItem(index int, text string, values []string) *TreeItem { 21 | if !t.IsValid() { 22 | return nil 23 | } 24 | return t.tree.InsertItem(t, index, text, values) 25 | } 26 | 27 | func (t *TreeItem) Index() int { 28 | if !t.IsValid() || t.IsRoot() { 29 | return -1 30 | } 31 | r, err := evalAsIntEx(fmt.Sprintf("%v index {%v}", t.tree.id, t.id), false) 32 | if err != nil { 33 | return -1 34 | } 35 | return r 36 | } 37 | 38 | func (t *TreeItem) IsRoot() bool { 39 | return t.id == "" 40 | } 41 | 42 | func (t *TreeItem) Parent() *TreeItem { 43 | if !t.IsValid() || t.IsRoot() { 44 | return nil 45 | } 46 | r, err := evalAsStringEx(fmt.Sprintf("%v parent {%v}", t.tree.id, t.id), false) 47 | if err != nil { 48 | return nil 49 | } 50 | return &TreeItem{t.tree, r} 51 | } 52 | 53 | func (t *TreeItem) Next() *TreeItem { 54 | if !t.IsValid() || t.IsRoot() { 55 | return nil 56 | } 57 | r, err := evalAsStringEx(fmt.Sprintf("%v next {%v}", t.tree.id, t.id), false) 58 | if err != nil || r == "" { 59 | return nil 60 | } 61 | return &TreeItem{t.tree, r} 62 | } 63 | 64 | func (t *TreeItem) Prev() *TreeItem { 65 | if !t.IsValid() || t.IsRoot() { 66 | return nil 67 | } 68 | r, err := evalAsStringEx(fmt.Sprintf("%v prev {%v}", t.tree.id, t.id), false) 69 | if err != nil || r == "" { 70 | return nil 71 | } 72 | return &TreeItem{t.tree, r} 73 | } 74 | 75 | func (t *TreeItem) Children() (lst []*TreeItem) { 76 | if !t.IsValid() { 77 | return 78 | } 79 | ids, err := evalAsStringList(fmt.Sprintf("%v children {%v}", t.tree.id, t.id)) 80 | if err != nil { 81 | return 82 | } 83 | for _, id := range ids { 84 | lst = append(lst, &TreeItem{t.tree, id}) 85 | } 86 | return 87 | } 88 | 89 | func (t *TreeItem) SetExpanded(expand bool) error { 90 | if !t.IsValid() || t.IsRoot() { 91 | return ErrInvalid 92 | } 93 | return eval(fmt.Sprintf("%v item {%v} -open %v", t.tree.id, t.id, expand)) 94 | } 95 | 96 | func (t *TreeItem) IsExpanded() bool { 97 | if !t.IsValid() || t.IsRoot() { 98 | return false 99 | } 100 | r, _ := evalAsBool(fmt.Sprintf("%v item {%v} -open", t.tree.id, t.id)) 101 | return r 102 | } 103 | 104 | func (t *TreeItem) expandAll(item *TreeItem) error { 105 | for _, child := range item.Children() { 106 | child.SetExpanded(true) 107 | t.expandAll(child) 108 | } 109 | return nil 110 | } 111 | 112 | func (t *TreeItem) ExpandAll() error { 113 | return t.expandAll(t) 114 | } 115 | 116 | func (t *TreeItem) collapseAll(item *TreeItem) error { 117 | for _, child := range item.Children() { 118 | child.SetExpanded(false) 119 | t.collapseAll(child) 120 | } 121 | return nil 122 | } 123 | 124 | func (t *TreeItem) CollapseAll() error { 125 | return t.collapseAll(t) 126 | } 127 | 128 | func (t *TreeItem) Expand() error { 129 | return t.SetExpanded(true) 130 | } 131 | 132 | func (t *TreeItem) Collapse() error { 133 | return t.SetExpanded(false) 134 | } 135 | 136 | func (t *TreeItem) SetText(text string) error { 137 | if !t.IsValid() || t.IsRoot() { 138 | return ErrInvalid 139 | } 140 | setObjText("atk_tree_item", text) 141 | return eval(fmt.Sprintf("%v item {%v} -text $atk_tree_item", t.tree.id, t.id)) 142 | } 143 | 144 | func (t *TreeItem) Text() string { 145 | if !t.IsValid() || t.IsRoot() { 146 | return "" 147 | } 148 | r, _ := evalAsString(fmt.Sprintf("%v item {%v} -text", t.tree.id, t.id)) 149 | return r 150 | } 151 | 152 | func (t *TreeItem) SetValues(values []string) error { 153 | if !t.IsValid() || t.IsRoot() { 154 | return ErrInvalid 155 | } 156 | setObjTextList("atk_tree_values", values) 157 | return eval(fmt.Sprintf("%v item {%v} -values $atk_tree_values", t.tree.id, t.id)) 158 | } 159 | 160 | func (t *TreeItem) Values() []string { 161 | if !t.IsValid() || t.IsRoot() { 162 | return nil 163 | } 164 | r, _ := evalAsStringList(fmt.Sprintf("%v item {%v} -values", t.tree.id, t.id)) 165 | return r 166 | } 167 | 168 | func (t *TreeItem) SetImage(img *Image) error { 169 | if !t.IsValid() || t.IsRoot() { 170 | return ErrInvalid 171 | } 172 | var iid string 173 | if img != nil { 174 | iid = img.Id() 175 | } 176 | return eval(fmt.Sprintf("%v item {%v} -image {%v}", t.tree.id, t.id, iid)) 177 | } 178 | 179 | func (t *TreeItem) Image() *Image { 180 | if !t.IsValid() || t.IsRoot() { 181 | return nil 182 | } 183 | r, err := evalAsString(fmt.Sprintf("%v item {%v} -image", t.tree.id, t.id)) 184 | return parserImageResult(r, err) 185 | } 186 | 187 | func (t *TreeItem) SetColumnText(column int, text string) error { 188 | if column < 0 { 189 | return ErrInvalid 190 | } else if column == 0 { 191 | return t.SetText(text) 192 | } 193 | if !t.IsValid() || t.IsRoot() { 194 | return ErrInvalid 195 | } 196 | setObjText("atk_tree_column", text) 197 | return eval(fmt.Sprintf("%v set {%v} %v $atk_tree_column", t.tree.id, t.id, column-1)) 198 | } 199 | 200 | func (t *TreeItem) ColumnText(column int) string { 201 | if column < 0 { 202 | return "" 203 | } else if column == 0 { 204 | return t.Text() 205 | } 206 | if !t.IsValid() || t.IsRoot() { 207 | return "" 208 | } 209 | r, _ := evalAsString(fmt.Sprintf("%v set {%v} %v", t.tree.id, t.id, column-1)) 210 | return r 211 | } 212 | -------------------------------------------------------------------------------- /tk/treeview_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import "testing" 6 | 7 | func init() { 8 | registerTest("TreeView", testTreeView) 9 | } 10 | 11 | func testTreeView(t *testing.T) { 12 | w := NewTreeView(nil, TreeViewAttrTakeFocus(true), TreeViewAttrHeight(20), TreeViewAttrTreeSelectMode(TreeSelectBrowse)) 13 | defer w.Destroy() 14 | 15 | w.SetTakeFocus(true) 16 | if v := w.IsTakeFocus(); v != true { 17 | t.Fatal("IsTakeFocus", true, v) 18 | } 19 | 20 | w.SetHeight(20) 21 | if v := w.Height(); v != 20 { 22 | t.Fatal("Height", 20, v) 23 | } 24 | 25 | w.SetTreeSelectMode(TreeSelectBrowse) 26 | if v := w.TreeSelectMode(); v != TreeSelectBrowse { 27 | t.Fatal("TreeSelectMode", TreeSelectBrowse, v) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tk/utils.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | func parserTwoInt(s string) (n1 int, n2 int) { 6 | var p = &n1 7 | for _, r := range s { 8 | if r == ' ' { 9 | p = &n2 10 | } else { 11 | *p = *p*10 + int(r-'0') 12 | } 13 | } 14 | return 15 | } 16 | 17 | func boolToInt(b bool) int { 18 | if b { 19 | return 1 20 | } 21 | return 0 22 | } 23 | 24 | func isValidKey(key string, keys []string) bool { 25 | for _, v := range keys { 26 | if v == key { 27 | return true 28 | } 29 | } 30 | return false 31 | } 32 | 33 | func SubString(text string, start int, end int) string { 34 | var n int = -1 35 | var r string 36 | for _, v := range text { 37 | n++ 38 | if n < start { 39 | continue 40 | } 41 | if n >= end { 42 | break 43 | } 44 | r += string(v) 45 | } 46 | return r 47 | } 48 | -------------------------------------------------------------------------------- /tk/widget.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import ( 6 | "fmt" 7 | "reflect" 8 | "strings" 9 | ) 10 | 11 | type Widget interface { 12 | Id() string 13 | Info() *WidgetInfo 14 | Type() WidgetType 15 | TypeName() string 16 | Parent() Widget 17 | Children() []Widget 18 | IsValid() bool 19 | Destroy() error 20 | DestroyChildren() error 21 | // attribute 22 | NativeAttribute(key string) (value string) 23 | SetNativeAttribute(key string, value string) error 24 | SetAttributes(attributes ...*WidgetAttr) error 25 | // event 26 | BindEvent(event string, fn func(*Event)) error 27 | BindKeyEvent(fn func(e *KeyEvent)) error 28 | BindKeyEventEx(fnPress func(e *KeyEvent), fnRelease func(e *KeyEvent)) error 29 | BindInfo() []string 30 | ClearBind(event string) error 31 | // grab 32 | SetGrab() error 33 | ReleaseGrab() error 34 | IsGrab() bool 35 | // focus 36 | SetFocus() error 37 | IsFocus() bool 38 | Lower(below Widget) error 39 | Raise(above Widget) error 40 | } 41 | 42 | var ( 43 | globalWidgetMap = make(map[string]Widget) 44 | ) 45 | 46 | func IsNilInterface(w Widget) bool { 47 | if w == nil { 48 | return true 49 | } 50 | return reflect.ValueOf(w).IsNil() 51 | } 52 | 53 | func RegisterWidget(w Widget) { 54 | if IsNilInterface(w) { 55 | return 56 | } 57 | globalWidgetMap[w.Id()] = w 58 | } 59 | 60 | func FindWidget(id string) Widget { 61 | return globalWidgetMap[id] 62 | } 63 | 64 | func LookupWidget(id string) (w Widget, ok bool) { 65 | w, ok = globalWidgetMap[id] 66 | return 67 | } 68 | 69 | func ParentOfWidget(w Widget) Widget { 70 | if IsNilInterface(w) { 71 | return nil 72 | } 73 | id := w.Id() 74 | if id == "." { 75 | return nil 76 | } 77 | pos := strings.LastIndex(id, ".") 78 | if pos == -1 { 79 | return nil 80 | } else if pos == 0 { 81 | return rootWindow 82 | } 83 | return globalWidgetMap[id[:pos]] 84 | } 85 | 86 | func ChildrenOfWidget(w Widget) (list []Widget) { 87 | if IsNilInterface(w) { 88 | return nil 89 | } 90 | id := w.Id() 91 | if id == "." { 92 | for k, v := range globalWidgetMap { 93 | if strings.HasPrefix(k, id) { 94 | if k == "." { 95 | continue 96 | } else if strings.Index(k[1:], ".") >= 0 { 97 | continue 98 | } 99 | list = append(list, v) 100 | } 101 | } 102 | } else { 103 | id = id + "." 104 | offset := len(id) 105 | for k, v := range globalWidgetMap { 106 | if strings.HasPrefix(k, id) { 107 | if strings.Index(k[offset:], ".") >= 0 { 108 | continue 109 | } 110 | list = append(list, v) 111 | } 112 | } 113 | } 114 | return 115 | } 116 | 117 | func removeWidget(id string) { 118 | if id == "." { 119 | globalWidgetMap = make(map[string]Widget) 120 | } else { 121 | delete(globalWidgetMap, id) 122 | id = id + "." 123 | var list []string 124 | for k, _ := range globalWidgetMap { 125 | if strings.HasPrefix(k, id) { 126 | list = append(list, k) 127 | } 128 | } 129 | for _, k := range list { 130 | delete(globalWidgetMap, k) 131 | } 132 | } 133 | } 134 | 135 | func IsValidWidget(w Widget) bool { 136 | if IsNilInterface(w) { 137 | return false 138 | } 139 | _, ok := globalWidgetMap[w.Id()] 140 | return ok 141 | } 142 | 143 | func DestroyWidget(w Widget) error { 144 | if !IsValidWidget(w) { 145 | return ErrInvalid 146 | } 147 | id := w.Id() 148 | eval(fmt.Sprintf("destroy %v", id)) 149 | removeWidget(id) 150 | return nil 151 | } 152 | 153 | func dumpWidgetHelp(w Widget, offset string, space string, ar *[]string) { 154 | s := fmt.Sprintf("%v%v", space, w) 155 | *ar = append(*ar, s) 156 | for _, child := range w.Children() { 157 | dumpWidgetHelp(child, offset, space+offset, ar) 158 | } 159 | } 160 | 161 | func DumpWidget(w Widget) string { 162 | return DumpWidgetEx(w, "\t") 163 | } 164 | 165 | func DumpWidgetEx(w Widget, offset string) string { 166 | var ar []string 167 | dumpWidgetHelp(w, offset, "", &ar) 168 | return strings.Join(ar, "\n") 169 | } 170 | -------------------------------------------------------------------------------- /tk/widget_attr.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import ( 6 | "fmt" 7 | ) 8 | 9 | // widget attribute 10 | type WidgetAttr struct { 11 | Key string 12 | Value interface{} 13 | } 14 | 15 | // setup widget init enable/disable theme 16 | func WidgetAttrInitUseTheme(use bool) *WidgetAttr { 17 | return &WidgetAttr{"init_use_theme", use} 18 | } 19 | 20 | // setup widget font 21 | func WidgetAttrFont(font Font) *WidgetAttr { 22 | if font == nil { 23 | return nil 24 | } 25 | return &WidgetAttr{"font", font.Id()} 26 | } 27 | 28 | // setup widget width 29 | func WidgetAttrWidth(width int) *WidgetAttr { 30 | return &WidgetAttr{"width", width} 31 | } 32 | 33 | // setup widget height pixel or line 34 | func WidgetAttrHeight(height int) *WidgetAttr { 35 | return &WidgetAttr{"height", height} 36 | } 37 | 38 | // setup widget text 39 | func WidgetAttrText(text string) *WidgetAttr { 40 | return &WidgetAttr{"text", text} 41 | } 42 | 43 | // setup widget image 44 | func WidgetAttrImage(image *Image) *WidgetAttr { 45 | if image == nil { 46 | return nil 47 | } 48 | return &WidgetAttr{"image", image.Id()} 49 | } 50 | 51 | // setup widget border style (tk relief attribute) 52 | func WidgetAttrReliefStyle(style ReliefStyle) *WidgetAttr { 53 | return &WidgetAttr{"relief", style} 54 | } 55 | 56 | // setup widget border width 57 | func WidgetAttrBorderWidth(width int) *WidgetAttr { 58 | return &WidgetAttr{"borderwidth", width} 59 | } 60 | 61 | // setup widget padding (ttk padding or tk padx/pady) 62 | func WidgetAttrPadding(pad Pad) *WidgetAttr { 63 | return &WidgetAttr{"padding", pad} 64 | } 65 | 66 | // setup widget padding (ttk padding or tk padx/pady) 67 | func WidgetAttrPaddingN(padx int, pady int) *WidgetAttr { 68 | return &WidgetAttr{"padding", Pad{padx, pady}} 69 | } 70 | 71 | func checkPaddingScript(ttk bool, attr *WidgetAttr) string { 72 | if pad, ok := attr.Value.(Pad); ok { 73 | if ttk { 74 | return fmt.Sprintf("-padding {%v %v}", pad.X, pad.Y) 75 | } else { 76 | return fmt.Sprintf("-padx {%v} -pady {%v}", pad.X, pad.Y) 77 | } 78 | } 79 | return "" 80 | } 81 | 82 | func checkInitUseTheme(attributes []*WidgetAttr) bool { 83 | for _, attr := range attributes { 84 | if attr != nil && attr.Key == "init_use_theme" { 85 | if use, ok := attr.Value.(bool); ok { 86 | return use 87 | } 88 | } 89 | } 90 | return mainTheme != nil 91 | } 92 | -------------------------------------------------------------------------------- /tk/widget_type.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import ( 6 | "fmt" 7 | "strings" 8 | ) 9 | 10 | var ( 11 | ErrorInvalidWidgetInfo = fmt.Errorf("invalid widget info") 12 | ErrorNotMatchWidgetInfo = fmt.Errorf("widget info not match") 13 | ) 14 | 15 | type WidgetType int 16 | 17 | const ( 18 | WidgetTypeNone WidgetType = iota 19 | WidgetTypeButton 20 | WidgetTypeCanvas 21 | WidgetTypeCheckButton 22 | WidgetTypeComboBox 23 | WidgetTypeEntry 24 | WidgetTypeFrame 25 | WidgetTypeLabel 26 | WidgetTypeLabelFrame 27 | WidgetTypeListBox 28 | WidgetTypeMenu 29 | WidgetTypeMenuButton 30 | WidgetTypeNotebook 31 | WidgetTypePaned 32 | WidgetTypeProgressBar 33 | WidgetTypeRadioButton 34 | WidgetTypeScale 35 | WidgetTypeScrollBar 36 | WidgetTypeSeparator 37 | WidgetTypeSizeGrip 38 | WidgetTypeSpinBox 39 | WidgetTypeText 40 | WidgetTypeWindow 41 | WidgetTypeTreeView 42 | WidgetTypeLayoutFrame 43 | WidgetTypeLayoutSpacer 44 | WidgetTypeLast 45 | ) 46 | 47 | func (typ WidgetType) MetaClass(theme bool) (typName string, meta *MetaClass, ttk bool) { 48 | mc, ok := typeMetaMap[typ] 49 | if !ok { 50 | panic(fmt.Errorf("error find metaclass type:%v", typ)) 51 | } 52 | if theme && mainTheme != nil && mainTheme.IsTtk() { 53 | if mc.Ttk != nil { 54 | return mc.Type, mc.Ttk, true 55 | } 56 | return mc.Type, mc.Tk, false 57 | } 58 | if mc.Tk != nil { 59 | return mc.Type, mc.Tk, false 60 | } 61 | return mc.Type, mc.Ttk, true 62 | } 63 | 64 | func (typ WidgetType) ThemeConfigure() string { 65 | if mainTheme == nil { 66 | return "" 67 | } 68 | var list []string 69 | attrs := mainTheme.InitAttributes(typ) 70 | _, meta, _ := typ.MetaClass(true) 71 | for _, attr := range attrs { 72 | if !meta.HasAttribute(attr.Key) { 73 | continue 74 | } 75 | list = append(list, fmt.Sprintf("-%v %q", attr.Key, attr.Value)) 76 | } 77 | return strings.Join(list, " ") 78 | } 79 | 80 | type WidgetInfo struct { 81 | Type WidgetType 82 | TypeName string 83 | IsTtk bool 84 | MetaClass *MetaClass 85 | } 86 | 87 | func buildWidgetAttributeScript(meta *MetaClass, ttk bool, attributes []*WidgetAttr) string { 88 | var list []string 89 | for _, attr := range attributes { 90 | if attr == nil { 91 | continue 92 | } 93 | if attr.Key == "padding" { 94 | list = append(list, checkPaddingScript(ttk, attr)) 95 | continue 96 | } 97 | if !meta.HasAttribute(attr.Key) { 98 | continue 99 | } 100 | if strs, ok := attr.Value.([]string); ok { 101 | pname := "atk_tmp_" + attr.Key 102 | setObjTextList(pname, strs) 103 | list = append(list, fmt.Sprintf("-%v $%v", attr.Key, pname)) 104 | continue 105 | } 106 | if s, ok := attr.Value.(string); ok { 107 | pname := "atk_tmp_" + attr.Key 108 | setObjText(pname, s) 109 | list = append(list, fmt.Sprintf("-%v $%v", attr.Key, pname)) 110 | continue 111 | } 112 | list = append(list, fmt.Sprintf("-%v {%v}", attr.Key, attr.Value)) 113 | } 114 | return strings.Join(list, " ") 115 | } 116 | 117 | func CreateWidgetInfo(iid string, typ WidgetType, theme bool, attributes []*WidgetAttr) *WidgetInfo { 118 | typName, meta, isttk := typ.MetaClass(theme) 119 | script := fmt.Sprintf("%v %v", meta.Command, iid) 120 | if theme { 121 | cfg := typ.ThemeConfigure() 122 | if cfg != "" { 123 | script += " " + cfg 124 | } 125 | } 126 | if len(attributes) > 0 { 127 | extra := buildWidgetAttributeScript(meta, isttk, attributes) 128 | if len(extra) > 0 { 129 | script += " " + extra 130 | } 131 | } 132 | err := eval(script) 133 | if err != nil { 134 | return nil 135 | } 136 | return &WidgetInfo{typ, typName, isttk, meta} 137 | } 138 | 139 | func findClassById(id string) string { 140 | if id == "." { 141 | return "Toplevel" 142 | } 143 | s, err := mainInterp.EvalAsString(fmt.Sprintf("winfo class {%v}", id)) 144 | if err != nil { 145 | return "" 146 | } 147 | return s 148 | } 149 | 150 | func FindWidgetInfo(id string) *WidgetInfo { 151 | if id == "" { 152 | return nil 153 | } 154 | class := findClassById(id) 155 | if class == "" { 156 | return nil 157 | } 158 | for k, v := range typeMetaMap { 159 | if v.Tk != nil && v.Tk.Class == class { 160 | return &WidgetInfo{k, v.Type, false, v.Tk} 161 | } 162 | if v.Ttk != nil && v.Ttk.Class == class { 163 | return &WidgetInfo{k, v.Type, true, v.Ttk} 164 | } 165 | } 166 | return nil 167 | } 168 | 169 | func CheckWidgetInfo(id string, typ WidgetType) (*WidgetInfo, error) { 170 | info := FindWidgetInfo(id) 171 | if info == nil { 172 | return nil, ErrorInvalidWidgetInfo 173 | } 174 | if info.Type != typ { 175 | return nil, ErrorNotMatchWidgetInfo 176 | } 177 | return info, nil 178 | } 179 | -------------------------------------------------------------------------------- /tk/window_darwin.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import "fmt" 6 | 7 | // NOTE: update must 8 | func (w *Window) ShowMaximized() error { 9 | return eval(fmt.Sprintf("update\nwm state %v zoomed", w.id)) 10 | } 11 | 12 | func (w *Window) IsMaximized() bool { 13 | r, _ := evalAsString(fmt.Sprintf("wm state %v", w.id)) 14 | return r == "zoomed" 15 | } 16 | -------------------------------------------------------------------------------- /tk/window_linux.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import "fmt" 6 | 7 | func (w *Window) ShowMaximized() error { 8 | if !w.IsVisible() { 9 | w.ShowNormal() 10 | } 11 | return eval(fmt.Sprintf("wm attributes %v -zoomed 1", w.id)) 12 | } 13 | 14 | func (w *Window) IsMaximized() bool { 15 | r, _ := evalAsBool(fmt.Sprintf("wm attributes %v -zoomed", w.id)) 16 | return r 17 | } 18 | -------------------------------------------------------------------------------- /tk/window_windows.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 visualfc. All rights reserved. 2 | 3 | package tk 4 | 5 | import ( 6 | "fmt" 7 | ) 8 | 9 | func (w *Window) ShowMaximized() error { 10 | return eval(fmt.Sprintf("wm state %v zoomed", w.id)) 11 | } 12 | 13 | func (w *Window) IsMaximized() bool { 14 | r, _ := evalAsString(fmt.Sprintf("wm state %v", w.id)) 15 | return r == "zoomed" 16 | } 17 | --------------------------------------------------------------------------------