├── .travis.yml ├── LICENSE ├── README.md ├── VERSION ├── adapter_base.go ├── adapter_item.go ├── alignment.go ├── brush.go ├── bubble_overlay.go ├── button.go ├── canvas.go ├── channeled_event.go ├── code_editor.go ├── code_syntax_layer.go ├── color.go ├── container.go ├── control.go ├── control_list.go ├── debug.go ├── default_adapter.go ├── direction.go ├── driver.go ├── drivers └── gl │ ├── blitter.go │ ├── canvas.go │ ├── check_error.go │ ├── context.go │ ├── debug.go │ ├── debug_js.go │ ├── draw_mode.go │ ├── driver.go │ ├── font.go │ ├── glyph_page.go │ ├── glyph_table.go │ ├── index_buffer.go │ ├── keyboard_translate.go │ ├── mouse_translate.go │ ├── platform │ ├── linux_constants.go │ ├── osx_constants.go │ └── windows_constants.go │ ├── polygon.go │ ├── primitive_type.go │ ├── refcounted.go │ ├── resolution.go │ ├── shader_attribute.go │ ├── shader_data_type.go │ ├── shader_program.go │ ├── shader_uniform.go │ ├── shape.go │ ├── stats.go │ ├── texture.go │ ├── triangulate.go │ ├── triangulate_test.go │ ├── vertex_buffer.go │ ├── vertex_stream.go │ └── viewport.go ├── drop_down_list.go ├── event.go ├── event_base.go ├── event_test.go ├── filtered_list_adapter.go ├── filtered_list_adapter_test.go ├── focus_controller.go ├── focusable.go ├── font.go ├── gxfont ├── droid_sans_mono.go ├── gxfont.go ├── mkfont.go └── roboto_regular.go ├── image.go ├── interval ├── int_data.go ├── int_data_test.go ├── list.go ├── list_test.go └── u64.go ├── keyboard_controller.go ├── keyboard_event.go ├── keyboard_key.go ├── keyboard_modifier.go ├── keystroke_event.go ├── label.go ├── linear_layout.go ├── list.go ├── math ├── constants.go ├── line_intersect.go ├── line_intersect_test.go ├── mat2.go ├── mat2_test.go ├── mat3.go ├── mat3_test.go ├── math.go ├── math_test.go ├── point.go ├── rect.go ├── rect_test.go ├── size.go ├── size_test.go ├── spacing.go ├── vec2.go ├── vec3.go └── vec4.go ├── mixins ├── base │ ├── container.go │ └── control.go ├── bubble_overlay.go ├── button.go ├── code_editor.go ├── code_editor_line.go ├── code_suggestion_adapter.go ├── default_textbox_line.go ├── drop_down_list.go ├── image.go ├── label.go ├── linear_layout.go ├── list.go ├── outer │ ├── attachable.go │ ├── draw.go │ ├── isvisibiler.go │ ├── layout.go │ ├── layout_children.go │ ├── paint_childer.go │ ├── painter.go │ ├── parenter.go │ ├── redrawer.go │ ├── relayouter.go │ └── sized.go ├── panel_holder.go ├── parts │ ├── attachable.go │ ├── background_border_painter.go │ ├── container.go │ ├── draw_paint.go │ ├── focusable.go │ ├── input_event_handler.go │ ├── layoutable.go │ ├── linear_layout.go │ ├── paddable.go │ ├── paint_children.go │ ├── parentable.go │ ├── utils.go │ └── visible.go ├── progress_bar.go ├── scroll_bar.go ├── scroll_layout.go ├── splitter_bar.go ├── splitter_layout.go ├── table_layout.go ├── textbox.go ├── tree.go ├── tree_to_list_adapter.go ├── tree_to_list_node.go ├── tree_to_list_test.go └── window.go ├── mouse_button.go ├── mouse_controller.go ├── mouse_event.go ├── mouse_state.go ├── orientation.go ├── panel_holder.go ├── pen.go ├── polygon.go ├── progress_bar.go ├── samples ├── file_dlg │ ├── main.go │ └── roots │ │ ├── default.go │ │ └── windows.go ├── flags │ └── flags.go ├── fullscreen │ └── main.go ├── hello_world │ └── main.go ├── image_viewer │ └── main.go ├── linear_layout │ └── main.go ├── lists │ └── main.go ├── panels │ └── main.go ├── polygon │ └── main.go ├── progress_bar │ └── main.go ├── table │ └── main.go └── tree │ └── main.go ├── scroll_bar.go ├── scroll_layout.go ├── simple_event.go ├── size_mode.go ├── splitter_layout.go ├── table_layout.go ├── testing └── assert_equals.go ├── text_selection.go ├── text_selection_list.go ├── text_selection_list_test.go ├── textbox.go ├── textbox_controller.go ├── textbox_controller_test.go ├── texture.go ├── theme.go ├── themes ├── basic │ ├── bubble_overlay.go │ ├── button.go │ ├── code_editor.go │ ├── colors.go │ ├── drop_down_list.go │ ├── image.go │ ├── label.go │ ├── linear_layout.go │ ├── list.go │ ├── panel_holder.go │ ├── panel_tab.go │ ├── progress_bar.go │ ├── scroll_bar.go │ ├── scroll_layout.go │ ├── splitter_layout.go │ ├── style.go │ ├── table_layout.go │ ├── textbox.go │ ├── theme.go │ ├── tree.go │ └── window.go ├── dark │ └── theme.go └── light │ └── theme.go ├── tooltip_controller.go ├── tree.go ├── utils.go ├── viewport.go ├── while_attached.go └── window.go /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - 1.4 4 | before_install: 5 | - sudo apt-get update 6 | - sudo apt-get install -qq libxi-dev libxcursor-dev libxrandr-dev libxinerama-dev mesa-common-dev libgl1-mesa-dev libxxf86vm-dev 7 | install: 8 | - go get golang.org/x/tools/cmd/vet 9 | script: 10 | - go get -t -v ./... 11 | - diff -u <(echo -n) <(gofmt -d ./) 12 | - go tool vet -composites=false ./ 13 | - go test -v -race ./... 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | gxui0.1 -------------------------------------------------------------------------------- /adapter_base.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gxui 6 | 7 | type AdapterBase struct { 8 | onDataChanged, onDataReplaced Event 9 | } 10 | 11 | func (a *AdapterBase) DataChanged(recreateControls bool) { 12 | if a.onDataChanged != nil { 13 | a.onDataChanged.Fire(recreateControls) 14 | } 15 | } 16 | 17 | func (a *AdapterBase) DataReplaced() { 18 | if a.onDataReplaced != nil { 19 | a.onDataReplaced.Fire() 20 | } 21 | } 22 | 23 | func (a *AdapterBase) OnDataChanged(f func(recreateControls bool)) EventSubscription { 24 | if a.onDataChanged == nil { 25 | a.onDataChanged = CreateEvent(func(bool) {}) 26 | } 27 | return a.onDataChanged.Listen(f) 28 | } 29 | 30 | func (a *AdapterBase) OnDataReplaced(f func()) EventSubscription { 31 | if a.onDataReplaced == nil { 32 | a.onDataReplaced = CreateEvent(func() {}) 33 | } 34 | return a.onDataReplaced.Listen(f) 35 | } 36 | -------------------------------------------------------------------------------- /adapter_item.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gxui 6 | 7 | // AdapterItem is a user defined type that can be used to uniquely identify a 8 | // single item in an adapter. The type must support equality and be hashable. 9 | type AdapterItem interface{} 10 | -------------------------------------------------------------------------------- /alignment.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gxui 6 | 7 | type HorizontalAlignment int 8 | 9 | const ( 10 | AlignLeft HorizontalAlignment = iota 11 | AlignCenter 12 | AlignRight 13 | ) 14 | 15 | func (a HorizontalAlignment) AlignLeft() bool { return a == AlignLeft } 16 | func (a HorizontalAlignment) AlignCenter() bool { return a == AlignCenter } 17 | func (a HorizontalAlignment) AlignRight() bool { return a == AlignRight } 18 | 19 | type VerticalAlignment int 20 | 21 | const ( 22 | AlignTop VerticalAlignment = iota 23 | AlignMiddle 24 | AlignBottom 25 | ) 26 | 27 | func (a VerticalAlignment) AlignTop() bool { return a == AlignTop } 28 | func (a VerticalAlignment) AlignMiddle() bool { return a == AlignMiddle } 29 | func (a VerticalAlignment) AlignBottom() bool { return a == AlignBottom } 30 | -------------------------------------------------------------------------------- /brush.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gxui 6 | 7 | var WhiteBrush = CreateBrush(White) 8 | var TransparentBrush = CreateBrush(Transparent) 9 | var BlackBrush = CreateBrush(Black) 10 | var DefaultBrush = WhiteBrush 11 | 12 | type Brush struct { 13 | Color Color 14 | } 15 | 16 | func CreateBrush(color Color) Brush { 17 | return Brush{color} 18 | } 19 | -------------------------------------------------------------------------------- /bubble_overlay.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gxui 6 | 7 | import ( 8 | "github.com/google/gxui/math" 9 | ) 10 | 11 | type BubbleOverlay interface { 12 | Control 13 | Show(control Control, target math.Point) 14 | Hide() 15 | } 16 | -------------------------------------------------------------------------------- /button.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gxui 6 | 7 | type ButtonType int 8 | 9 | const ( 10 | PushButton ButtonType = iota 11 | ToggleButton 12 | ) 13 | 14 | type Button interface { 15 | LinearLayout 16 | Text() string 17 | SetText(string) 18 | Type() ButtonType 19 | SetType(ButtonType) 20 | IsChecked() bool 21 | SetChecked(bool) 22 | } 23 | -------------------------------------------------------------------------------- /canvas.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gxui 6 | 7 | import ( 8 | "github.com/google/gxui/math" 9 | ) 10 | 11 | type Canvas interface { 12 | Size() math.Size 13 | IsComplete() bool 14 | Complete() 15 | Push() 16 | Pop() 17 | AddClip(math.Rect) 18 | Clear(Color) 19 | DrawCanvas(c Canvas, position math.Point) 20 | DrawTexture(t Texture, bounds math.Rect) 21 | DrawRunes(font Font, runes []rune, points []math.Point, color Color) 22 | DrawLines(Polygon, Pen) 23 | DrawPolygon(Polygon, Pen, Brush) 24 | DrawRect(math.Rect, Brush) 25 | DrawRoundedRect(rect math.Rect, tl, tr, bl, br float32, p Pen, b Brush) 26 | } 27 | -------------------------------------------------------------------------------- /channeled_event.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gxui 6 | 7 | import ( 8 | "reflect" 9 | "sync" 10 | ) 11 | 12 | type ChanneledEvent struct { 13 | sync.RWMutex 14 | base EventBase 15 | channel chan func() 16 | } 17 | 18 | func CreateChanneledEvent(signature interface{}, channel chan func()) Event { 19 | e := &ChanneledEvent{ 20 | channel: channel, 21 | } 22 | e.base.init(signature) 23 | baseUnlisten := e.base.unlisten 24 | e.base.unlisten = func(id int) { 25 | e.RLock() 26 | baseUnlisten(id) 27 | e.RUnlock() 28 | } 29 | return e 30 | } 31 | 32 | func (e *ChanneledEvent) Fire(args ...interface{}) { 33 | e.base.VerifyArguments(args) 34 | e.channel <- func() { 35 | e.RLock() 36 | e.base.InvokeListeners(args) 37 | e.RUnlock() 38 | } 39 | } 40 | 41 | func (e *ChanneledEvent) Listen(listener interface{}) EventSubscription { 42 | e.Lock() 43 | res := e.base.Listen(listener) 44 | e.Unlock() 45 | return res 46 | } 47 | 48 | func (e *ChanneledEvent) ParameterTypes() []reflect.Type { 49 | return e.base.ParameterTypes() 50 | } 51 | -------------------------------------------------------------------------------- /code_editor.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gxui 6 | 7 | type CodeSuggestion interface { 8 | Name() string 9 | Code() string 10 | } 11 | 12 | type CodeSuggestionProvider interface { 13 | SuggestionsAt(runeIndex int) []CodeSuggestion 14 | } 15 | 16 | type CodeEditor interface { 17 | TextBox 18 | SyntaxLayers() CodeSyntaxLayers 19 | SetSyntaxLayers(CodeSyntaxLayers) 20 | TabWidth() int 21 | SetTabWidth(int) 22 | SuggestionProvider() CodeSuggestionProvider 23 | SetSuggestionProvider(CodeSuggestionProvider) 24 | ShowSuggestionList() 25 | HideSuggestionList() 26 | } 27 | -------------------------------------------------------------------------------- /code_syntax_layer.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gxui 6 | 7 | import ( 8 | "github.com/google/gxui/interval" 9 | "github.com/google/gxui/math" 10 | ) 11 | 12 | type CodeSyntaxLayer struct { 13 | spans interval.IntDataList 14 | color *Color 15 | backgroundColor *Color 16 | borderColor *Color 17 | data interface{} 18 | } 19 | 20 | func CreateCodeSyntaxLayer() *CodeSyntaxLayer { return &CodeSyntaxLayer{} } 21 | 22 | func (l *CodeSyntaxLayer) Clear() { 23 | l.spans = interval.IntDataList{} 24 | } 25 | 26 | func (l *CodeSyntaxLayer) UpdateSpans(runeCount int, edits []TextBoxEdit) { 27 | min := 0 28 | max := runeCount 29 | for _, e := range edits { 30 | if l == nil { 31 | continue 32 | } 33 | for j, s := range l.spans { 34 | at := e.At 35 | start, end := s.Range() 36 | if start >= at { 37 | start = math.Clamp(start+e.Delta, min, max) 38 | } 39 | if end > at { 40 | end = math.Clamp(end+e.Delta, min, max) 41 | } 42 | if end < start { 43 | end = start 44 | } 45 | l.spans[j] = interval.CreateIntData(start, end, s.Data()) 46 | } 47 | } 48 | } 49 | 50 | func (l *CodeSyntaxLayer) Add(start, count int) { 51 | l.AddData(start, count, nil) 52 | } 53 | 54 | func (l *CodeSyntaxLayer) AddData(start, count int, data interface{}) { 55 | span := interval.CreateIntData(start, start+count, data) 56 | interval.Replace(&l.spans, span) 57 | } 58 | 59 | func (l *CodeSyntaxLayer) AddSpan(span interval.IntData) { 60 | interval.Replace(&l.spans, span) 61 | } 62 | 63 | func (l *CodeSyntaxLayer) Spans() interval.IntDataList { 64 | return l.spans 65 | } 66 | 67 | func (l *CodeSyntaxLayer) SpanAt(runeIndex int) *interval.IntData { 68 | idx := interval.IndexOf(&l.spans, uint64(runeIndex)) 69 | if idx >= 0 { 70 | return &l.spans[idx] 71 | } else { 72 | return nil 73 | } 74 | } 75 | 76 | func (l *CodeSyntaxLayer) Color() *Color { 77 | return l.color 78 | } 79 | 80 | func (l *CodeSyntaxLayer) ClearColor() { 81 | l.color = nil 82 | } 83 | 84 | func (l *CodeSyntaxLayer) SetColor(color Color) { 85 | l.color = &color 86 | } 87 | 88 | func (l *CodeSyntaxLayer) BackgroundColor() *Color { 89 | return l.backgroundColor 90 | } 91 | 92 | func (l *CodeSyntaxLayer) ClearBackgroundColor() { 93 | l.backgroundColor = nil 94 | } 95 | 96 | func (l *CodeSyntaxLayer) SetBackgroundColor(color Color) { 97 | l.backgroundColor = &color 98 | } 99 | 100 | func (l *CodeSyntaxLayer) BorderColor() *Color { 101 | return l.borderColor 102 | } 103 | 104 | func (l *CodeSyntaxLayer) ClearBorderColor() { 105 | l.borderColor = nil 106 | } 107 | 108 | func (l *CodeSyntaxLayer) SetBorderColor(color Color) { 109 | l.borderColor = &color 110 | } 111 | 112 | func (l *CodeSyntaxLayer) Data() interface{} { 113 | return l.data 114 | } 115 | 116 | func (l *CodeSyntaxLayer) SetData(data interface{}) { 117 | l.data = data 118 | } 119 | 120 | type CodeSyntaxLayers []*CodeSyntaxLayer 121 | 122 | func (l *CodeSyntaxLayers) Get(idx int) *CodeSyntaxLayer { 123 | if len(*l) <= idx { 124 | old := *l 125 | *l = make(CodeSyntaxLayers, idx+1) 126 | copy(*l, old) 127 | } 128 | layer := (*l)[idx] 129 | if layer == nil { 130 | layer = &CodeSyntaxLayer{} 131 | (*l)[idx] = layer 132 | } 133 | return layer 134 | } 135 | 136 | func (l *CodeSyntaxLayers) Clear() { 137 | *l = CodeSyntaxLayers{} 138 | } 139 | -------------------------------------------------------------------------------- /color.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gxui 6 | 7 | import "github.com/google/gxui/math" 8 | 9 | var Transparent = Color{0.0, 0.0, 0.0, 0.0} 10 | 11 | var Black = Color{0.0, 0.0, 0.0, 1.0} 12 | 13 | var Red10 = Color{0.1, 0.0, 0.0, 1.0} 14 | var Red20 = Color{0.2, 0.0, 0.0, 1.0} 15 | var Red30 = Color{0.3, 0.0, 0.0, 1.0} 16 | var Red40 = Color{0.4, 0.0, 0.0, 1.0} 17 | var Red50 = Color{0.5, 0.0, 0.0, 1.0} 18 | var Red60 = Color{0.6, 0.0, 0.0, 1.0} 19 | var Red70 = Color{0.7, 0.0, 0.0, 1.0} 20 | var Red80 = Color{0.8, 0.0, 0.0, 1.0} 21 | var Red90 = Color{0.9, 0.0, 0.0, 1.0} 22 | var Red = Color{1.0, 0.0, 0.0, 1.0} 23 | 24 | var Green10 = Color{0.0, 0.1, 0.0, 1.0} 25 | var Green20 = Color{0.0, 0.2, 0.0, 1.0} 26 | var Green30 = Color{0.0, 0.3, 0.0, 1.0} 27 | var Green40 = Color{0.0, 0.4, 0.0, 1.0} 28 | var Green50 = Color{0.0, 0.5, 0.0, 1.0} 29 | var Green60 = Color{0.0, 0.6, 0.0, 1.0} 30 | var Green70 = Color{0.0, 0.7, 0.0, 1.0} 31 | var Green80 = Color{0.0, 0.8, 0.0, 1.0} 32 | var Green90 = Color{0.0, 0.9, 0.0, 1.0} 33 | var Green = Color{0.0, 1.0, 0.0, 1.0} 34 | 35 | var Blue10 = Color{0.0, 0.0, 0.1, 1.0} 36 | var Blue20 = Color{0.0, 0.0, 0.2, 1.0} 37 | var Blue30 = Color{0.0, 0.0, 0.3, 1.0} 38 | var Blue40 = Color{0.0, 0.0, 0.4, 1.0} 39 | var Blue50 = Color{0.0, 0.0, 0.5, 1.0} 40 | var Blue60 = Color{0.0, 0.0, 0.6, 1.0} 41 | var Blue70 = Color{0.0, 0.0, 0.7, 1.0} 42 | var Blue80 = Color{0.0, 0.0, 0.8, 1.0} 43 | var Blue90 = Color{0.0, 0.0, 0.9, 1.0} 44 | var Blue = Color{0.0, 0.0, 1.0, 1.0} 45 | 46 | var Gray10 = Color{0.1, 0.1, 0.1, 1.0} 47 | var Gray15 = Color{0.15, 0.15, 0.15, 1.0} 48 | var Gray20 = Color{0.2, 0.2, 0.2, 1.0} 49 | var Gray30 = Color{0.3, 0.3, 0.3, 1.0} 50 | var Gray40 = Color{0.4, 0.4, 0.4, 1.0} 51 | var Gray50 = Color{0.5, 0.5, 0.5, 1.0} 52 | var Gray60 = Color{0.6, 0.6, 0.6, 1.0} 53 | var Gray70 = Color{0.7, 0.7, 0.7, 1.0} 54 | var Gray80 = Color{0.8, 0.8, 0.8, 1.0} 55 | var Gray90 = Color{0.9, 0.9, 0.9, 1.0} 56 | var White = Color{1.0, 1.0, 1.0, 1.0} 57 | 58 | var Yellow = Color{1.0, 1.0, 0.0, 1.0} 59 | 60 | type Color struct { 61 | R, G, B, A float32 62 | } 63 | 64 | func ColorFromHex(hex uint32) Color { 65 | return Color{ 66 | A: float32((hex>>24)&0xFF) / 255.0, 67 | R: float32((hex>>16)&0xFF) / 255.0, 68 | G: float32((hex>>8)&0xFF) / 255.0, 69 | B: float32(hex&0xFF) / 255.0, 70 | } 71 | } 72 | 73 | func (c Color) MulRGB(s float32) Color { 74 | return Color{c.R * s, c.G * s, c.B * s, c.A} 75 | } 76 | 77 | func (c Color) Saturate() Color { 78 | return Color{math.Saturate(c.R), math.Saturate(c.G), math.Saturate(c.B), math.Saturate(c.A)} 79 | } 80 | -------------------------------------------------------------------------------- /container.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gxui 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | 11 | "github.com/google/gxui/math" 12 | ) 13 | 14 | type Child struct { 15 | Control Control 16 | Offset math.Point 17 | } 18 | 19 | type Parent interface { 20 | Children() Children 21 | Relayout() 22 | Redraw() 23 | } 24 | 25 | type Container interface { 26 | Parent 27 | AddChild(child Control) *Child 28 | AddChildAt(index int, child Control) *Child 29 | RemoveChild(child Control) 30 | RemoveChildAt(index int) 31 | RemoveAll() 32 | Padding() math.Spacing 33 | SetPadding(math.Spacing) 34 | } 35 | 36 | // String returns a string describing the child type and bounds. 37 | func (c *Child) String() string { 38 | return fmt.Sprintf("Type: %T, Bounds: %v", c.Control, c.Bounds()) 39 | } 40 | 41 | // Bounds returns the Child bounds relative to the parent. 42 | func (c *Child) Bounds() math.Rect { 43 | return c.Control.Size().Rect().Offset(c.Offset) 44 | } 45 | 46 | // Layout sets the Child size and offset relative to the parent. 47 | // Layout should only be called by the Child's parent. 48 | func (c *Child) Layout(rect math.Rect) { 49 | c.Offset = rect.Min 50 | c.Control.SetSize(rect.Size()) 51 | } 52 | 53 | // Children is a list of Child pointers. 54 | type Children []*Child 55 | 56 | // String returns a string describing the child type and bounds. 57 | func (c Children) String() string { 58 | s := make([]string, len(c)) 59 | for i, c := range c { 60 | s[i] = fmt.Sprintf("%d: %s", i, c.String()) 61 | } 62 | return strings.Join(s, "\n") 63 | } 64 | 65 | // IndexOf returns and returns the index of the child control, or -1 if the 66 | // child is not in this Children list. 67 | func (c Children) IndexOf(control Control) int { 68 | for i, child := range c { 69 | if child.Control == control { 70 | return i 71 | } 72 | } 73 | return -1 74 | } 75 | 76 | // Find returns and returns the Child pointer for the given Control, or nil 77 | // if the child is not in this Children list. 78 | func (c Children) Find(control Control) *Child { 79 | for _, child := range c { 80 | if child.Control == control { 81 | return child 82 | } 83 | } 84 | return nil 85 | } 86 | -------------------------------------------------------------------------------- /control_list.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gxui 6 | 7 | type ControlList []Control 8 | 9 | func (l ControlList) Contains(c Control) bool { 10 | for _, i := range l { 11 | if i == c { 12 | return true 13 | } 14 | } 15 | return false 16 | } 17 | -------------------------------------------------------------------------------- /debug.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gxui 6 | 7 | import ( 8 | "fmt" 9 | "github.com/google/gxui/math" 10 | "reflect" 11 | "runtime" 12 | ) 13 | 14 | func indent(depth int) string { 15 | s := "" 16 | for i := 0; i < depth; i++ { 17 | s += " |" 18 | } 19 | return s 20 | } 21 | 22 | func dump(c interface{}, depth int) string { 23 | s := "" 24 | switch t := c.(type) { 25 | case Control: 26 | s += fmt.Sprintf("(%p) %T Size: %+v Margin: %+v \n", &t, t, t.Size(), t.Margin()) 27 | default: 28 | s += fmt.Sprintf("%T\n", t) 29 | } 30 | switch t := c.(type) { 31 | case Container: 32 | for i, c := range t.Children() { 33 | s += fmt.Sprintf("%s--- Child %d: ", indent(depth), i) 34 | s += dump(c.Control, depth+1) 35 | } 36 | } 37 | return s 38 | } 39 | 40 | func Dump(c interface{}) { 41 | fmt.Printf("%s\n", dump(c, 0)) 42 | } 43 | 44 | func FunctionName(i interface{}) string { 45 | return runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name() 46 | } 47 | 48 | func BreadcrumbsAt(p Container, pnt math.Point) string { 49 | s := reflect.TypeOf(p).String() 50 | for _, c := range p.Children() { 51 | b := c.Control.Size().Rect().Offset(c.Offset) 52 | if b.Contains(pnt) { 53 | switch t := c.Control.(type) { 54 | case Container: 55 | return s + " > " + BreadcrumbsAt(t, pnt.Sub(c.Offset)) 56 | default: 57 | return s + " > " + reflect.TypeOf(c.Control).String() 58 | } 59 | } 60 | } 61 | return s 62 | } 63 | 64 | func Path(p interface{}) string { 65 | if p == nil { 66 | return "nil" 67 | } 68 | 69 | s := reflect.TypeOf(p).String() 70 | 71 | if c, _ := p.(Control); c != nil { 72 | if c.Parent() != nil { 73 | return Path(c.Parent()) + " > " + s 74 | } 75 | } 76 | 77 | return s 78 | } 79 | -------------------------------------------------------------------------------- /direction.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gxui 6 | 7 | import "fmt" 8 | 9 | type Direction int 10 | 11 | const ( 12 | TopToBottom Direction = iota 13 | LeftToRight 14 | BottomToTop 15 | RightToLeft 16 | ) 17 | 18 | func (d Direction) LeftToRight() bool { return d == LeftToRight } 19 | func (d Direction) RightToLeft() bool { return d == RightToLeft } 20 | func (d Direction) TopToBottom() bool { return d == TopToBottom } 21 | func (d Direction) BottomToTop() bool { return d == BottomToTop } 22 | 23 | func (d Direction) Flip() Direction { 24 | switch d { 25 | case TopToBottom: 26 | return BottomToTop 27 | case LeftToRight: 28 | return RightToLeft 29 | case BottomToTop: 30 | return TopToBottom 31 | case RightToLeft: 32 | return LeftToRight 33 | default: 34 | panic(fmt.Errorf("Unknown direction %d", d)) 35 | } 36 | } 37 | 38 | func (d Direction) Orientation() Orientation { 39 | switch d { 40 | case TopToBottom, BottomToTop: 41 | return Vertical 42 | case LeftToRight, RightToLeft: 43 | return Horizontal 44 | default: 45 | panic(fmt.Errorf("Unknown direction %d", d)) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /driver.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gxui 6 | 7 | import ( 8 | "image" 9 | 10 | "github.com/google/gxui/math" 11 | ) 12 | 13 | type Driver interface { 14 | // Call queues f to be run on the UI go-routine, returning before f may have 15 | // been called. Call returns false if the driver has been terminated, in which 16 | // case f may not be called. 17 | Call(f func()) bool 18 | 19 | // CallSync queues and then blocks for f to be run on the UI go-routine. 20 | // Call returns false if the driver has been terminated, in which case f may 21 | // not be called. 22 | CallSync(f func()) bool 23 | 24 | Terminate() 25 | SetClipboard(str string) 26 | GetClipboard() (string, error) 27 | 28 | // CreateFont loads a font from the provided TrueType bytes. 29 | CreateFont(data []byte, size int) (Font, error) 30 | 31 | // CreateWindowedViewport creates a new windowed Viewport with the specified 32 | // width and height in device independent pixels. 33 | CreateWindowedViewport(width, height int, name string) Viewport 34 | 35 | // CreateFullscreenViewport creates a new fullscreen Viewport with the 36 | // specified width and height in device independent pixels. If width or 37 | // height is 0, then the viewport adopts the current screen resolution. 38 | CreateFullscreenViewport(width, height int, name string) Viewport 39 | 40 | CreateCanvas(math.Size) Canvas 41 | CreateTexture(img image.Image, pixelsPerDip float32) Texture 42 | 43 | // Debug function used to verify that the caller is executing on the UI 44 | // go-routine. If the caller is not on the UI go-routine then the function 45 | // panics. 46 | AssertUIGoroutine() 47 | } 48 | -------------------------------------------------------------------------------- /drivers/gl/check_error.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gl 6 | 7 | import ( 8 | "fmt" 9 | 10 | "github.com/goxjs/gl" 11 | ) 12 | 13 | func checkError() { 14 | if v := gl.GetError(); v != 0 { 15 | switch v { 16 | case gl.INVALID_ENUM: 17 | panic("GL returned error GL_INVALID_ENUM") 18 | case gl.INVALID_FRAMEBUFFER_OPERATION: 19 | panic("GL returned error GL_INVALID_FRAMEBUFFER_OPERATION") 20 | case gl.INVALID_OPERATION: 21 | panic("GL returned error GL_INVALID_OPERATION") 22 | case gl.INVALID_VALUE: 23 | panic("GL returned error GL_INVALID_VALUE") 24 | default: 25 | panic(fmt.Errorf("GL returned error 0x%.4x", v)) 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /drivers/gl/debug.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build !js 6 | 7 | package gl 8 | 9 | import ( 10 | "runtime" 11 | "strings" 12 | ) 13 | 14 | // discoverUIGoRoutine finds and stores the program counter of the 15 | // function 'applicationLoop' that must be in the callstack. The 16 | // PC is stored so that AssertUIGoroutine can verify that the call 17 | // came from the application loop (the UI go-routine). 18 | func (d *driver) discoverUIGoRoutine() { 19 | for _, pc := range d.pcs[:runtime.Callers(2, d.pcs)] { 20 | name := runtime.FuncForPC(pc).Name() 21 | if strings.HasSuffix(name, "applicationLoop") { 22 | d.uiPC = pc 23 | return 24 | } 25 | } 26 | panic("applicationLoop was not found in the callstack") 27 | } 28 | 29 | func (d *driver) AssertUIGoroutine() { 30 | for _, pc := range d.pcs[:runtime.Callers(2, d.pcs)] { 31 | if pc == d.uiPC { 32 | return 33 | } 34 | } 35 | panic("AssertUIGoroutine called on a go-routine that was not the UI go-routine") 36 | } 37 | -------------------------------------------------------------------------------- /drivers/gl/debug_js.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build js 6 | 7 | package gl 8 | 9 | func (d *driver) discoverUIGoRoutine() { 10 | println("discoverUIGoRoutine not yet implemented on js architecture") 11 | } 12 | 13 | func (d *driver) AssertUIGoroutine() { 14 | // AssertUIGoroutine not yet implemented on js architecture, so it never panics. 15 | } 16 | -------------------------------------------------------------------------------- /drivers/gl/draw_mode.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gl 6 | 7 | import ( 8 | "fmt" 9 | 10 | "github.com/goxjs/gl" 11 | ) 12 | 13 | type drawMode int 14 | 15 | const ( 16 | dmPoints drawMode = gl.POINTS 17 | dmLineStrip drawMode = gl.LINE_STRIP 18 | dmLineLoop drawMode = gl.LINE_LOOP 19 | dmLines drawMode = gl.LINES 20 | dmTriangleStrip drawMode = gl.TRIANGLE_STRIP 21 | dmTriangleFan drawMode = gl.TRIANGLE_FAN 22 | dmTriangles drawMode = gl.TRIANGLES 23 | ) 24 | 25 | func (d drawMode) primitiveCount(vertexCount int) int { 26 | switch d { 27 | case dmPoints: 28 | return vertexCount 29 | case dmLineStrip: 30 | return vertexCount - 1 31 | case dmLineLoop: 32 | return vertexCount 33 | case dmLines: 34 | return vertexCount / 2 35 | case dmTriangleStrip: 36 | return vertexCount - 2 37 | case dmTriangleFan: 38 | return vertexCount - 2 39 | case dmTriangles: 40 | return vertexCount / 3 41 | default: 42 | panic(fmt.Errorf("Unknown drawMode 0x%.4x", d)) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /drivers/gl/glyph_page.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gl 6 | 7 | import ( 8 | "image" 9 | "image/draw" 10 | "image/png" 11 | "os" 12 | 13 | "github.com/google/gxui/math" 14 | fnt "golang.org/x/image/font" 15 | "golang.org/x/image/math/fixed" 16 | ) 17 | 18 | const ( 19 | dumpGlyphPages = false 20 | glyphPageWidth = 512 21 | glyphPageHeight = 512 22 | glyphSizeAlignment = 8 23 | glyphPadding = 1 24 | ) 25 | 26 | type glyphEntry struct { 27 | offset math.Point 28 | bounds math.Rect 29 | } 30 | 31 | type glyphPage struct { 32 | image *image.Alpha 33 | size math.Size // in pixels 34 | entries map[rune]glyphEntry 35 | rowHeight int 36 | tex *texture 37 | nextPoint math.Point 38 | } 39 | 40 | func point26_6toPoint(p fixed.Point26_6) math.Point { 41 | return math.Point{X: int(p.X) >> 6, Y: int(p.Y) >> 6} 42 | } 43 | 44 | func rectangle26_6toRect(p fixed.Rectangle26_6) math.Rect { 45 | return math.Rect{Min: point26_6toPoint(p.Min), Max: point26_6toPoint(p.Max)} 46 | } 47 | 48 | func align(v, pot int) int { 49 | return (v + pot - 1) & ^(pot - 1) 50 | } 51 | 52 | func newGlyphPage(face fnt.Face, r rune) *glyphPage { 53 | // Start the page big enough to hold the initial rune. 54 | b, _, _ := face.GlyphBounds(r) 55 | bounds := rectangle26_6toRect(b) 56 | size := math.Size{W: glyphPageWidth, H: glyphPageHeight}.Max(bounds.Size()) 57 | size.W = align(size.W, glyphSizeAlignment) 58 | size.H = align(size.H, glyphSizeAlignment) 59 | 60 | page := &glyphPage{ 61 | image: image.NewAlpha(image.Rect(0, 0, size.W, size.H)), 62 | size: size, 63 | entries: make(map[rune]glyphEntry), 64 | rowHeight: 0, 65 | } 66 | page.add(face, r) 67 | return page 68 | } 69 | 70 | func (p *glyphPage) commit() { 71 | if p.tex != nil { 72 | return 73 | } 74 | p.tex = newTexture(p.image, 1.0) 75 | if dumpGlyphPages { 76 | f, _ := os.Create("glyph-page.png") 77 | defer f.Close() 78 | png.Encode(f, p.image) 79 | } 80 | } 81 | 82 | func (p *glyphPage) add(face fnt.Face, r rune) bool { 83 | if _, found := p.entries[r]; found { 84 | panic("Glyph already added to glyph page") 85 | } 86 | 87 | b, mask, maskp, _, _ := face.Glyph(fixed.Point26_6{}, r) 88 | bounds := math.CreateRect(b.Min.X, b.Min.Y, b.Max.X, b.Max.Y) 89 | 90 | w, h := bounds.Size().WH() 91 | x, y := p.nextPoint.X, p.nextPoint.Y 92 | 93 | if x+w > p.size.W { 94 | // Row full, start new line 95 | x = 0 96 | y += p.rowHeight + glyphPadding 97 | p.rowHeight = 0 98 | } 99 | 100 | if y+h > p.size.H { 101 | return false // Page full 102 | } 103 | 104 | draw.Draw(p.image, image.Rect(x, y, x+w, y+h), mask, maskp, draw.Src) 105 | 106 | p.entries[r] = glyphEntry{ 107 | offset: math.Point{X: x, Y: y}.Sub(bounds.Min), 108 | bounds: bounds, 109 | } 110 | p.nextPoint = math.Point{X: x + w + glyphPadding, Y: y} 111 | if h > p.rowHeight { 112 | p.rowHeight = h 113 | } 114 | p.tex = nil 115 | 116 | return true 117 | } 118 | 119 | func (p *glyphPage) texture() *texture { 120 | if p.tex == nil { 121 | p.commit() 122 | } 123 | return p.tex 124 | } 125 | 126 | func (p *glyphPage) get(rune rune) glyphEntry { 127 | return p.entries[rune] 128 | } 129 | -------------------------------------------------------------------------------- /drivers/gl/glyph_table.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gl 6 | 7 | import fnt "golang.org/x/image/font" 8 | 9 | type glyphTable struct { 10 | face fnt.Face 11 | index map[rune]int 12 | pages []*glyphPage 13 | } 14 | 15 | func newGlyphTable(face fnt.Face) *glyphTable { 16 | return &glyphTable{face: face, index: make(map[rune]int)} 17 | } 18 | 19 | func (t *glyphTable) get(r rune) *glyphPage { 20 | if i, found := t.index[r]; found { 21 | return t.pages[i] 22 | } 23 | if len(t.pages) == 0 { 24 | t.pages = append(t.pages, newGlyphPage(t.face, r)) 25 | } else { 26 | page := t.pages[len(t.pages)-1] 27 | if !page.add(t.face, r) { 28 | page = newGlyphPage(t.face, r) 29 | t.pages = append(t.pages, page) 30 | } 31 | } 32 | index := len(t.pages) - 1 33 | t.index[r] = index 34 | return t.pages[index] 35 | } 36 | -------------------------------------------------------------------------------- /drivers/gl/index_buffer.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gl 6 | 7 | import ( 8 | "fmt" 9 | "reflect" 10 | 11 | "github.com/goxjs/gl" 12 | ) 13 | 14 | type indexBuffer struct { 15 | data []byte 16 | ty primitiveType 17 | } 18 | 19 | func newIndexBuffer(ty primitiveType, data16 []uint16) *indexBuffer { 20 | switch ty { 21 | case ptUbyte, ptUshort, ptUint: 22 | if !ty.isArrayOfType(data16) { 23 | panic(fmt.Errorf("Index data is not of type %v", ty)) 24 | } 25 | default: 26 | panic(fmt.Errorf("Index type must be either UBYTE, USHORT or UINT. Got: %v", ty)) 27 | } 28 | 29 | // HACK: Hardcode support for only ptUshort. 30 | data := make([]byte, len(data16)*2) 31 | for i, v := range data16 { 32 | data[2*i+0] = byte(v >> 0) 33 | data[2*i+1] = byte(v >> 8) 34 | } 35 | 36 | ib := &indexBuffer{ 37 | data: data, 38 | ty: ty, 39 | } 40 | return ib 41 | } 42 | 43 | func (b *indexBuffer) newContext() *indexBufferContext { 44 | dataVal := reflect.ValueOf(b.data) 45 | length := dataVal.Len() / 2 // HACK: Hardcode support for only ptUshort. 46 | 47 | buffer := gl.CreateBuffer() 48 | gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer) 49 | gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, b.data, gl.STATIC_DRAW) 50 | gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, gl.Buffer{}) 51 | checkError() 52 | 53 | globalStats.indexBufferContextCount.inc() 54 | return &indexBufferContext{ 55 | buffer: buffer, 56 | ty: b.ty, 57 | length: length, 58 | } 59 | } 60 | 61 | type indexBufferContext struct { 62 | contextResource 63 | buffer gl.Buffer 64 | ty primitiveType 65 | length int 66 | } 67 | 68 | func (c *indexBufferContext) destroy() { 69 | globalStats.indexBufferContextCount.dec() 70 | gl.DeleteBuffer(c.buffer) 71 | c.buffer = gl.Buffer{} 72 | } 73 | 74 | func (c *indexBufferContext) render(drawMode drawMode) { 75 | gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, c.buffer) 76 | gl.DrawElements(gl.Enum(drawMode), c.length, gl.Enum(c.ty), 0) 77 | gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, gl.Buffer{}) 78 | checkError() 79 | } 80 | -------------------------------------------------------------------------------- /drivers/gl/mouse_translate.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gl 6 | 7 | import ( 8 | "fmt" 9 | 10 | "github.com/google/gxui" 11 | 12 | "github.com/goxjs/glfw" 13 | ) 14 | 15 | func translateMouseButton(button glfw.MouseButton) gxui.MouseButton { 16 | switch button { 17 | case glfw.MouseButtonLeft: 18 | return gxui.MouseButtonLeft 19 | case glfw.MouseButtonMiddle: 20 | return gxui.MouseButtonMiddle 21 | case glfw.MouseButtonRight: 22 | return gxui.MouseButtonRight 23 | default: 24 | panic(fmt.Errorf("Unknown mouse button %v", button)) 25 | } 26 | } 27 | 28 | func getMouseState(w *glfw.Window) gxui.MouseState { 29 | var s gxui.MouseState 30 | for _, button := range []glfw.MouseButton{glfw.MouseButtonLeft, glfw.MouseButtonMiddle, glfw.MouseButtonRight} { 31 | if w.GetMouseButton(button) == glfw.Press { 32 | s |= 1 << uint(translateMouseButton(button)) 33 | } 34 | } 35 | return s 36 | } 37 | -------------------------------------------------------------------------------- /drivers/gl/platform/linux_constants.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build linux 6 | 7 | package platform 8 | 9 | const ScrollSpeed = 20.0 10 | -------------------------------------------------------------------------------- /drivers/gl/platform/osx_constants.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build darwin 6 | 7 | package platform 8 | 9 | const ScrollSpeed = 4.0 10 | -------------------------------------------------------------------------------- /drivers/gl/platform/windows_constants.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build windows 6 | 7 | package platform 8 | 9 | const ScrollSpeed = 20.0 10 | -------------------------------------------------------------------------------- /drivers/gl/primitive_type.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gl 6 | 7 | import ( 8 | "fmt" 9 | "reflect" 10 | 11 | "github.com/goxjs/gl" 12 | ) 13 | 14 | type primitiveType int 15 | 16 | const ( 17 | ptFloat primitiveType = gl.FLOAT 18 | ptInt primitiveType = gl.INT 19 | ptUint primitiveType = gl.UNSIGNED_INT 20 | ptUshort primitiveType = gl.UNSIGNED_SHORT 21 | ptUbyte primitiveType = gl.UNSIGNED_BYTE 22 | ) 23 | 24 | func (p primitiveType) sizeInBytes() int { 25 | switch p { 26 | case ptFloat: 27 | return 4 28 | case ptInt: 29 | return 4 30 | case ptUint: 31 | return 4 32 | case ptUshort: 33 | return 2 34 | case ptUbyte: 35 | return 1 36 | default: 37 | panic(fmt.Errorf("Unknown primitiveType 0x%.4x", p)) 38 | } 39 | } 40 | 41 | func (p primitiveType) isArrayOfType(array interface{}) bool { 42 | ty := reflect.TypeOf(array).Elem() 43 | switch p { 44 | case ptFloat: 45 | return ty.Name() == "float32" 46 | case ptInt: 47 | return ty.Name() == "int32" 48 | case ptUint: 49 | return ty.Name() == "uint32" 50 | case ptUshort: 51 | return ty.Name() == "uint16" 52 | case ptUbyte: 53 | return ty.Name() == "uint8" 54 | default: 55 | panic(fmt.Errorf("Unknown primitiveType 0x%.4x", p)) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /drivers/gl/refcounted.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gl 6 | 7 | /* 8 | import ( 9 | "fmt" 10 | "runtime" 11 | "strings" 12 | "sync/atomic" 13 | ) 14 | 15 | const debugTrackReferences = false 16 | 17 | type refCounted struct { 18 | refCount int32 19 | history []string 20 | } 21 | 22 | func verifyRefCountIsZero(r *refCounted) { 23 | if r.alive() { 24 | panic(fmt.Errorf("RefCounted object was garbage collected with a reference count of %d.\n%s", 25 | r.refCount, strings.Join(r.history, "\n"))) 26 | } 27 | } 28 | 29 | func (r *refCounted) init() { 30 | r.refCount = 1 31 | 32 | if debugTrackReferences { 33 | _, file, line, _ := runtime.Caller(1) 34 | r.history = append(r.history, fmt.Sprintf("0 -> 1: %s:%d", file, line)) 35 | runtime.SetFinalizer(r, verifyRefCountIsZero) 36 | } 37 | } 38 | 39 | func (r *refCounted) addRef() { 40 | r.assertAlive("AddRef") 41 | count := atomic.AddInt32(&r.refCount, 1) 42 | 43 | if debugTrackReferences { 44 | _, file, line, _ := runtime.Caller(1) 45 | r.history = append(r.history, fmt.Sprintf("%d -> %d: %s:%d", 46 | count-1, count, file, line)) 47 | } 48 | } 49 | 50 | func (r *refCounted) release() bool { 51 | r.assertAlive("Release") 52 | count := atomic.AddInt32(&r.refCount, -1) 53 | 54 | if debugTrackReferences { 55 | _, file, line, _ := runtime.Caller(2) 56 | r.history = append(r.history, fmt.Sprintf("%d -> %d: %s:%d", 57 | count+1, count, file, line)) 58 | } 59 | return count == 0 60 | } 61 | 62 | func (r *refCounted) alive() bool { 63 | return atomic.LoadInt32(&r.refCount) > 0 64 | } 65 | 66 | func (r *refCounted) assertAlive(funcName string) { 67 | if !r.alive() { 68 | if debugTrackReferences { 69 | panic(fmt.Errorf("Attempting to call %s()) on a fully released object.\n%s", 70 | funcName, strings.Join(r.history, "\n"))) 71 | } else { 72 | panic(fmt.Errorf("Attempting to call %s() on a fully released object. Enable debugTrackReferences for more info.", funcName)) 73 | } 74 | } 75 | } 76 | */ 77 | -------------------------------------------------------------------------------- /drivers/gl/resolution.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gl 6 | 7 | import ( 8 | "fmt" 9 | 10 | "github.com/google/gxui/math" 11 | ) 12 | 13 | // 16:16 fixed point ratio of DIPs to pixels 14 | type resolution uint32 15 | 16 | func (r resolution) String() string { 17 | return fmt.Sprintf("%f", r.dipsToPixels()) 18 | } 19 | 20 | func (r resolution) dipsToPixels() float32 { 21 | return float32(r) / 65536.0 22 | } 23 | 24 | func (r resolution) intDipsToPixels(s int) int { 25 | return (s * int(r)) >> 16 26 | } 27 | 28 | func (r resolution) pointDipsToPixels(s math.Point) math.Point { 29 | return math.Point{ 30 | X: r.intDipsToPixels(s.X), 31 | Y: r.intDipsToPixels(s.Y), 32 | } 33 | } 34 | 35 | func (r resolution) sizeDipsToPixels(s math.Size) math.Size { 36 | return math.Size{ 37 | W: r.intDipsToPixels(s.W), 38 | H: r.intDipsToPixels(s.H), 39 | } 40 | } 41 | 42 | func (r resolution) rectDipsToPixels(s math.Rect) math.Rect { 43 | return math.Rect{ 44 | Min: r.pointDipsToPixels(s.Min), 45 | Max: r.pointDipsToPixels(s.Max), 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /drivers/gl/shader_attribute.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gl 6 | 7 | import "github.com/goxjs/gl" 8 | 9 | type shaderAttribute struct { 10 | name string 11 | size int 12 | ty shaderDataType 13 | location gl.Attrib 14 | } 15 | 16 | func (a shaderAttribute) enableArray() { 17 | gl.EnableVertexAttribArray(a.location) 18 | } 19 | 20 | func (a shaderAttribute) disableArray() { 21 | gl.DisableVertexAttribArray(a.location) 22 | } 23 | 24 | func (a shaderAttribute) attribPointer(size int32, ty uint32, normalized bool, stride int32, offset int) { 25 | gl.VertexAttribPointer(a.location, int(size), gl.Enum(ty), normalized, int(stride), offset) 26 | } 27 | -------------------------------------------------------------------------------- /drivers/gl/shader_data_type.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gl 6 | 7 | import ( 8 | "fmt" 9 | 10 | "github.com/goxjs/gl" 11 | ) 12 | 13 | type shaderDataType int 14 | 15 | const ( 16 | stFloatMat2 shaderDataType = gl.FLOAT_MAT2 17 | stFloatMat3 shaderDataType = gl.FLOAT_MAT3 18 | stFloatMat4 shaderDataType = gl.FLOAT_MAT4 19 | stFloatVec1 shaderDataType = gl.FLOAT 20 | stFloatVec2 shaderDataType = gl.FLOAT_VEC2 21 | stFloatVec3 shaderDataType = gl.FLOAT_VEC3 22 | stFloatVec4 shaderDataType = gl.FLOAT_VEC4 23 | stSampler2d shaderDataType = gl.SAMPLER_2D 24 | ) 25 | 26 | func (s shaderDataType) String() string { 27 | switch s { 28 | case stFloatMat2: 29 | return "mat2" 30 | case stFloatMat3: 31 | return "mat3" 32 | case stFloatMat4: 33 | return "mat4" 34 | case stFloatVec1: 35 | return "float" 36 | case stFloatVec2: 37 | return "vec2" 38 | case stFloatVec3: 39 | return "vec3" 40 | case stFloatVec4: 41 | return "vec4" 42 | case stSampler2d: 43 | return "sampler2D" 44 | default: 45 | return "unknown" 46 | } 47 | } 48 | 49 | func (s shaderDataType) sizeInBytes() int { 50 | return s.vectorElementCount() * s.vectorElementType().sizeInBytes() 51 | } 52 | 53 | func (s shaderDataType) vectorElementCount() int { 54 | switch s { 55 | case stFloatMat2: 56 | return 2 * 2 57 | case stFloatMat3: 58 | return 3 * 3 59 | case stFloatMat4: 60 | return 4 * 4 61 | case stFloatVec1: 62 | return 1 63 | case stFloatVec2: 64 | return 2 65 | case stFloatVec3: 66 | return 3 67 | case stFloatVec4: 68 | return 4 69 | case stSampler2d: 70 | return 1 71 | default: 72 | panic(fmt.Errorf("Unknown shaderDataType 0x%.4x", s)) 73 | } 74 | } 75 | 76 | func (s shaderDataType) vectorElementType() primitiveType { 77 | switch s { 78 | case stFloatMat2: 79 | return ptFloat 80 | case stFloatMat3: 81 | return ptFloat 82 | case stFloatMat4: 83 | return ptFloat 84 | case stFloatVec1: 85 | return ptFloat 86 | case stFloatVec2: 87 | return ptFloat 88 | case stFloatVec3: 89 | return ptFloat 90 | case stFloatVec4: 91 | return ptFloat 92 | case stSampler2d: 93 | return ptInt 94 | default: 95 | panic(fmt.Errorf("Unknown shaderDataType 0x%.4x", s)) 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /drivers/gl/shader_uniform.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gl 6 | 7 | import ( 8 | "fmt" 9 | 10 | "github.com/google/gxui" 11 | "github.com/google/gxui/math" 12 | "github.com/goxjs/gl" 13 | ) 14 | 15 | type shaderUniform struct { 16 | name string 17 | size int 18 | ty shaderDataType 19 | location gl.Uniform 20 | textureUnit int 21 | } 22 | 23 | func (u *shaderUniform) bind(context *context, v interface{}) { 24 | switch u.ty { 25 | case stFloatMat2: 26 | gl.UniformMatrix2fv(u.location, v.([]float32)) 27 | case stFloatMat3: 28 | switch m := v.(type) { 29 | case math.Mat3: 30 | gl.UniformMatrix3fv(u.location, m[:]) 31 | case []float32: 32 | gl.UniformMatrix3fv(u.location, m) 33 | } 34 | case stFloatMat4: 35 | gl.UniformMatrix4fv(u.location, v.([]float32)) 36 | case stFloatVec1: 37 | switch v := v.(type) { 38 | case float32: 39 | gl.Uniform1f(u.location, v) 40 | case []float32: 41 | gl.Uniform1fv(u.location, v) 42 | } 43 | case stFloatVec2: 44 | switch v := v.(type) { 45 | case math.Vec2: 46 | gl.Uniform2fv(u.location, []float32{v.X, v.Y}) 47 | case []float32: 48 | if len(v)%2 != 0 { 49 | panic(fmt.Errorf("Uniform '%s' of type vec2 should be an float32 array with a multiple of two length", u.name)) 50 | } 51 | gl.Uniform2fv(u.location, v) 52 | } 53 | case stFloatVec3: 54 | switch v := v.(type) { 55 | case math.Vec3: 56 | gl.Uniform3fv(u.location, []float32{v.X, v.Y, v.Z}) 57 | case []float32: 58 | if len(v)%3 != 0 { 59 | panic(fmt.Errorf("Uniform '%s' of type vec3 should be an float32 array with a multiple of three length", u.name)) 60 | } 61 | gl.Uniform3fv(u.location, v) 62 | } 63 | case stFloatVec4: 64 | switch v := v.(type) { 65 | case math.Vec4: 66 | gl.Uniform4fv(u.location, []float32{v.X, v.Y, v.Z, v.W}) 67 | case gxui.Color: 68 | gl.Uniform4fv(u.location, []float32{v.R, v.G, v.B, v.A}) 69 | case []float32: 70 | if len(v)%4 != 0 { 71 | panic(fmt.Errorf("Uniform '%s' of type vec4 should be an float32 array with a multiple of four length", u.name)) 72 | } 73 | gl.Uniform4fv(u.location, v) 74 | } 75 | case stSampler2d: 76 | tc := v.(*textureContext) 77 | gl.ActiveTexture(gl.Enum(gl.TEXTURE0 + u.textureUnit)) 78 | gl.BindTexture(gl.TEXTURE_2D, tc.texture) 79 | gl.Uniform1i(u.location, u.textureUnit) 80 | default: 81 | panic(fmt.Errorf("Uniform of unsupported type %s", u.ty)) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /drivers/gl/shape.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gl 6 | 7 | import "github.com/goxjs/gl" 8 | 9 | type shape struct { 10 | vb *vertexBuffer 11 | ib *indexBuffer 12 | drawMode drawMode 13 | } 14 | 15 | func newShape(vb *vertexBuffer, ib *indexBuffer, drawMode drawMode) *shape { 16 | if vb == nil { 17 | panic("VertexBuffer cannot be nil") 18 | } 19 | 20 | s := &shape{ 21 | vb: vb, 22 | ib: ib, 23 | drawMode: drawMode, 24 | } 25 | return s 26 | } 27 | 28 | func newQuadShape() *shape { 29 | pos := newVertexStream("aPosition", stFloatVec2, []float32{ 30 | 0.0, 0.0, 31 | 1.0, 0.0, 32 | 0.0, 1.0, 33 | 1.0, 1.0, 34 | }) 35 | vb := newVertexBuffer(pos) 36 | ib := newIndexBuffer(ptUshort, []uint16{ 37 | 0, 1, 2, 38 | 2, 1, 3, 39 | }) 40 | return newShape(vb, ib, dmTriangles) 41 | } 42 | 43 | func (s shape) draw(ctx *context, shader *shaderProgram, ub uniformBindings) { 44 | shader.bind(ctx, s.vb, ub) 45 | if s.ib != nil { 46 | ctx.getOrCreateIndexBufferContext(s.ib).render(s.drawMode) 47 | } else { 48 | gl.DrawArrays(gl.Enum(s.drawMode), 0, s.vb.count) 49 | } 50 | shader.unbind(ctx) 51 | checkError() 52 | } 53 | -------------------------------------------------------------------------------- /drivers/gl/texture.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gl 6 | 7 | import ( 8 | "image" 9 | 10 | "github.com/google/gxui/math" 11 | "github.com/goxjs/gl" 12 | ) 13 | 14 | type texture struct { 15 | image image.Image 16 | pixelsPerDip float32 17 | flipY bool 18 | } 19 | 20 | func newTexture(img image.Image, pixelsPerDip float32) *texture { 21 | t := &texture{ 22 | image: img, 23 | pixelsPerDip: pixelsPerDip, 24 | } 25 | return t 26 | } 27 | 28 | // gxui.Texture compliance 29 | func (t *texture) Image() image.Image { 30 | return t.image 31 | } 32 | 33 | func (t *texture) Size() math.Size { 34 | return t.SizePixels().ScaleS(1.0 / t.pixelsPerDip) 35 | } 36 | 37 | func (t *texture) SizePixels() math.Size { 38 | s := t.image.Bounds().Size() 39 | return math.Size{W: s.X, H: s.Y} 40 | } 41 | 42 | func (t *texture) FlipY() bool { 43 | return t.flipY 44 | } 45 | 46 | func (t *texture) SetFlipY(flipY bool) { 47 | t.flipY = flipY 48 | } 49 | 50 | func (t *texture) newContext() *textureContext { 51 | var fmt gl.Enum 52 | var data []byte 53 | var pma bool 54 | 55 | switch ty := t.image.(type) { 56 | case *image.RGBA: 57 | fmt = gl.RGBA 58 | data = ty.Pix 59 | pma = true 60 | case *image.NRGBA: 61 | fmt = gl.RGBA 62 | data = ty.Pix 63 | case *image.Alpha: 64 | fmt = gl.ALPHA 65 | data = ty.Pix 66 | default: 67 | panic("Unsupported image type") 68 | } 69 | 70 | texture := gl.CreateTexture() 71 | gl.BindTexture(gl.TEXTURE_2D, texture) 72 | w, h := t.SizePixels().WH() 73 | gl.TexImage2D(gl.TEXTURE_2D, 0, w, h, fmt, gl.UNSIGNED_BYTE, data) 74 | gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) 75 | gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR) 76 | gl.BindTexture(gl.TEXTURE_2D, gl.Texture{}) 77 | checkError() 78 | 79 | globalStats.textureContextCount.inc() 80 | return &textureContext{ 81 | texture: texture, 82 | sizePixels: t.Size(), 83 | flipY: t.flipY, 84 | pma: pma, 85 | } 86 | } 87 | 88 | type textureContext struct { 89 | contextResource 90 | texture gl.Texture 91 | sizePixels math.Size 92 | flipY bool 93 | pma bool 94 | } 95 | 96 | func (c *textureContext) destroy() { 97 | globalStats.textureContextCount.dec() 98 | gl.DeleteTexture(c.texture) 99 | c.texture = gl.Texture{} 100 | } 101 | -------------------------------------------------------------------------------- /drivers/gl/triangulate.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gl 6 | 7 | import ( 8 | "container/list" 9 | "fmt" 10 | "github.com/google/gxui/math" 11 | ) 12 | 13 | const debugTriangulate = false 14 | const epsilon = 0.00001 15 | 16 | func isConcave(edges []math.Vec2, a, b, c int) bool { 17 | return edges[b].Sub(edges[a]).Cross(edges[b].Sub(edges[c])) > -epsilon 18 | } 19 | 20 | func isEar(edges []math.Vec2, a, b, c int) bool { 21 | if isConcave(edges, a, b, c) { 22 | if debugTriangulate { 23 | fmt.Printf("concave: %c, %c, %c\n", 'A'+a, 'A'+b, 'A'+c) 24 | } 25 | return false 26 | } 27 | 28 | plane := [3]math.Vec3{} 29 | for i := 0; i < 3; i++ { 30 | p := [3]int{a, b, c}[i] 31 | q := [3]int{b, c, a}[i] 32 | normal := edges[q].Sub(edges[p]).Normalize().Tangent() 33 | plane[i] = normal.Vec3(-normal.Dot(edges[p])) 34 | } 35 | 36 | for i := 0; i < len(edges); i++ { 37 | if i == a || i == b || i == c { 38 | continue 39 | } 40 | v := edges[i].Vec3(1) 41 | 42 | if v.Dot(plane[0]) > -epsilon && 43 | v.Dot(plane[1]) > -epsilon && 44 | v.Dot(plane[2]) > -epsilon { 45 | if debugTriangulate { 46 | fmt.Printf("non-ear: %c, %c, %c (%c %f:%f:%f)\n", 47 | 'A'+a, 'A'+b, 'A'+c, 48 | 'A'+i, 49 | v.Dot(plane[0]), v.Dot(plane[1]), v.Dot(plane[2]), 50 | ) 51 | } 52 | return false 53 | } 54 | } 55 | if debugTriangulate { 56 | fmt.Printf("ear: %c, %c, %c\n", 57 | 'A'+a, 'A'+b, 'A'+c, 58 | ) 59 | } 60 | return true 61 | } 62 | 63 | func pruneEdgeDuplicates(edges []math.Vec2) []math.Vec2 { 64 | pruned := make([]math.Vec2, 0, len(edges)) 65 | last := math.Vec2{} 66 | for i, v := range edges { 67 | if i == 0 || last.Sub(v).Len() > 0.0001 { 68 | pruned = append(pruned, v) 69 | } 70 | last = v 71 | } 72 | return pruned 73 | } 74 | 75 | func triangulate(edges []math.Vec2) []math.Vec2 { 76 | if debugTriangulate { 77 | fmt.Printf("triangulate()\n") 78 | } 79 | 80 | edges = pruneEdgeDuplicates(edges) 81 | if len(edges) < 3 { 82 | return []math.Vec2{} 83 | } 84 | 85 | l := list.New() 86 | for i := range edges { 87 | l.PushBack(i) 88 | } 89 | 90 | out := []math.Vec2{} 91 | 92 | pruned := true 93 | for pruned { 94 | pruned = false 95 | for a := l.Front(); a != nil; { 96 | b := a.Next() 97 | if b == nil { 98 | b = l.Front() 99 | } 100 | c := b.Next() 101 | if c == nil { 102 | c = l.Front() 103 | } 104 | 105 | ia, ib, ic := a.Value.(int), b.Value.(int), c.Value.(int) 106 | 107 | if isEar(edges, ia, ib, ic) { 108 | l.Remove(b) 109 | out = append(out, edges[ia], edges[ib], edges[ic]) 110 | pruned = true 111 | if l.Len() < 3 { 112 | return out 113 | } 114 | } else { 115 | a = a.Next() 116 | } 117 | } 118 | } 119 | 120 | if debugTriangulate { 121 | fmt.Printf("Failed to prune an ear! edges: %#v, out: %v\n", edges, out) 122 | } 123 | 124 | // assert.True(l.Len() < 3, "Failed to prune an ear! edges: %#v, out: %v", edges, out) 125 | return out 126 | } 127 | -------------------------------------------------------------------------------- /drivers/gl/vertex_buffer.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gl 6 | 7 | import "fmt" 8 | 9 | type vertexBuffer struct { 10 | streams map[string]*vertexStream 11 | count int 12 | } 13 | 14 | func newVertexBuffer(streams ...*vertexStream) *vertexBuffer { 15 | vb := &vertexBuffer{ 16 | streams: map[string]*vertexStream{}, 17 | } 18 | for i, s := range streams { 19 | if i == 0 { 20 | vb.count = s.count 21 | } else { 22 | if vb.count != s.count { 23 | panic(fmt.Errorf("Inconsistent vertex count in vertex buffer. %s has %d vertices, %s has %d", 24 | streams[i-1].name, streams[i-1].count, s.name, s.count)) 25 | } 26 | } 27 | vb.streams[s.name] = s 28 | } 29 | return vb 30 | } 31 | -------------------------------------------------------------------------------- /drivers/gl/vertex_stream.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gl 6 | 7 | import ( 8 | "fmt" 9 | "math" 10 | "reflect" 11 | 12 | "github.com/goxjs/gl" 13 | ) 14 | 15 | type vertexStream struct { 16 | name string 17 | data []byte 18 | ty shaderDataType 19 | count int 20 | } 21 | 22 | func newVertexStream(name string, ty shaderDataType, data32 []float32) *vertexStream { 23 | dataVal := reflect.ValueOf(data32) 24 | dataLen := dataVal.Len() 25 | 26 | if dataLen%ty.vectorElementCount() != 0 { 27 | panic(fmt.Errorf("Incorrect multiple of elements. Got: %d, Requires multiple of %d", 28 | dataLen, ty.vectorElementCount())) 29 | } 30 | if !ty.vectorElementType().isArrayOfType(data32) { 31 | panic("Data is not of the specified type") 32 | } 33 | 34 | // HACK. 35 | data := float32Bytes(data32...) 36 | 37 | vs := &vertexStream{ 38 | name: name, 39 | data: data, 40 | ty: ty, 41 | count: dataLen / ty.vectorElementCount(), 42 | } 43 | return vs 44 | } 45 | 46 | func (s *vertexStream) newContext() *vertexStreamContext { 47 | buffer := gl.CreateBuffer() 48 | gl.BindBuffer(gl.ARRAY_BUFFER, buffer) 49 | gl.BufferData(gl.ARRAY_BUFFER, s.data, gl.STATIC_DRAW) 50 | gl.BindBuffer(gl.ARRAY_BUFFER, gl.Buffer{}) 51 | checkError() 52 | 53 | globalStats.vertexStreamContextCount.inc() 54 | return &vertexStreamContext{buffer: buffer} 55 | } 56 | 57 | type vertexStreamContext struct { 58 | contextResource 59 | buffer gl.Buffer 60 | } 61 | 62 | func (c *vertexStreamContext) bind() { 63 | gl.BindBuffer(gl.ARRAY_BUFFER, c.buffer) 64 | } 65 | 66 | func (c *vertexStreamContext) destroy() { 67 | globalStats.vertexStreamContextCount.dec() 68 | gl.DeleteBuffer(c.buffer) 69 | c.buffer = gl.Buffer{} 70 | } 71 | 72 | // float32Bytes returns the byte representation of float32 values in little endian byte order. 73 | func float32Bytes(values ...float32) []byte { 74 | b := make([]byte, 4*len(values)) 75 | for i, v := range values { 76 | u := math.Float32bits(v) 77 | b[4*i+0] = byte(u >> 0) 78 | b[4*i+1] = byte(u >> 8) 79 | b[4*i+2] = byte(u >> 16) 80 | b[4*i+3] = byte(u >> 24) 81 | } 82 | return b 83 | } 84 | -------------------------------------------------------------------------------- /drop_down_list.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gxui 6 | 7 | type DropDownList interface { 8 | Focusable 9 | Container 10 | SetBubbleOverlay(BubbleOverlay) 11 | BubbleOverlay() BubbleOverlay 12 | Adapter() ListAdapter 13 | SetAdapter(ListAdapter) 14 | BorderPen() Pen 15 | SetBorderPen(Pen) 16 | BackgroundBrush() Brush 17 | SetBackgroundBrush(Brush) 18 | Selected() AdapterItem 19 | Select(AdapterItem) 20 | OnSelectionChanged(func(AdapterItem)) EventSubscription 21 | OnShowList(func()) EventSubscription 22 | OnHideList(func()) EventSubscription 23 | } 24 | -------------------------------------------------------------------------------- /event.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gxui 6 | 7 | import ( 8 | "reflect" 9 | ) 10 | 11 | type EventSubscription interface { 12 | Unlisten() 13 | } 14 | 15 | type Event interface { 16 | Fire(args ...interface{}) 17 | Listen(interface{}) EventSubscription 18 | ParameterTypes() []reflect.Type 19 | } 20 | -------------------------------------------------------------------------------- /filtered_list_adapter.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gxui 6 | 7 | import ( 8 | "sort" 9 | "strings" 10 | ) 11 | 12 | type FilteredListItem struct { 13 | Name string 14 | Data interface{} 15 | } 16 | 17 | type FilteredListAdapter struct { 18 | DefaultAdapter 19 | items []FilteredListItem 20 | } 21 | 22 | func (a *FilteredListAdapter) SetItems(items []FilteredListItem) { 23 | // Clone, as the order can be mutated 24 | a.items = append([]FilteredListItem{}, items...) 25 | a.DefaultAdapter.SetItems(a.items) 26 | } 27 | 28 | func (a *FilteredListAdapter) Sort(partial string) { 29 | partialLower := strings.ToLower(partial) 30 | sorter := flaSorter{items: a.items, scores: make([]int, len(a.items))} 31 | for i, s := range a.items { 32 | sorter.scores[i] = flaScore(s.Name, strings.ToLower(s.Name), partial, partialLower) 33 | } 34 | sort.Sort(sorter) 35 | a.DefaultAdapter.SetItems(a.items) 36 | } 37 | 38 | type flaSorter struct { 39 | items []FilteredListItem 40 | scores []int 41 | } 42 | 43 | func (s flaSorter) Len() int { 44 | return len(s.items) 45 | } 46 | 47 | func (s flaSorter) Less(i, j int) bool { 48 | return s.scores[i] > s.scores[j] 49 | } 50 | 51 | func (s flaSorter) Swap(i, j int) { 52 | items, scores := s.items, s.scores 53 | items[i], items[j] = items[j], items[i] 54 | scores[i], scores[j] = scores[j], scores[i] 55 | } 56 | 57 | func flaScore(str, strLower, partial, partialLower string) int { 58 | l := len(partial) 59 | for i := l; i > 0; i-- { 60 | c := 0 61 | if strings.Contains(str, partial[:i]) { 62 | c = i*i + 1 63 | } else if strings.Contains(strLower, partialLower[:i]) { 64 | c = i * i 65 | } 66 | 67 | if c > 0 { 68 | return c + flaScore(str, strLower, partial[i:], partialLower[i:]) 69 | } 70 | } 71 | return 0 72 | } 73 | -------------------------------------------------------------------------------- /filtered_list_adapter_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gxui 6 | 7 | import test "github.com/google/gxui/testing" 8 | import ( 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | func calcScore(str, partial string) int { 14 | return flaScore(str, strings.ToLower(str), partial, strings.ToLower(partial)) 15 | } 16 | 17 | func TestFilteredListAdapterScore(t *testing.T) { 18 | test.AssertEquals(t, (3*3)+(2*2+1)+(3*3+1), calcScore("a_Mix_Of_Words", "mixOfrds")) 19 | } 20 | -------------------------------------------------------------------------------- /focus_controller.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gxui 6 | 7 | type FocusController struct { 8 | window Window 9 | focus Focusable 10 | setFocusCount int 11 | detachSubscription EventSubscription 12 | } 13 | 14 | func CreateFocusController(window Window) *FocusController { 15 | return &FocusController{ 16 | window: window, 17 | } 18 | } 19 | 20 | func (c *FocusController) SetFocus(f Focusable) { 21 | c.setFocusCount++ 22 | if c.focus == f { 23 | return 24 | } 25 | if c.focus != nil { 26 | o := c.focus 27 | c.focus = nil 28 | c.detachSubscription.Unlisten() 29 | o.LostFocus() 30 | if c.focus != nil { 31 | return // Something in LostFocus() called SetFocus(). Respect their call. 32 | } 33 | } 34 | c.focus = f 35 | if c.focus != nil { 36 | c.detachSubscription = c.focus.OnDetach(func() { c.SetFocus(nil) }) 37 | c.focus.GainedFocus() 38 | } 39 | } 40 | 41 | func (c *FocusController) SetFocusCount() int { 42 | return c.setFocusCount 43 | } 44 | 45 | func (c *FocusController) Focus() Focusable { 46 | return c.focus 47 | } 48 | 49 | func (c *FocusController) FocusNext() { 50 | c.SetFocus(c.NextFocusable(c.focus, true)) 51 | } 52 | 53 | func (c *FocusController) FocusPrev() { 54 | c.SetFocus(c.NextFocusable(c.focus, false)) 55 | } 56 | 57 | func (c *FocusController) NextFocusable(after Control, forwards bool) Focusable { 58 | container, _ := after.(Container) 59 | if container != nil { 60 | f := c.NextChildFocusable(container, nil, forwards) 61 | if f != nil { 62 | return f 63 | } 64 | } 65 | 66 | for after != nil { 67 | parent := after.Parent() 68 | if parent != nil { 69 | f := c.NextChildFocusable(parent, after, forwards) 70 | if f != nil { 71 | return f 72 | } 73 | } 74 | after, _ = parent.(Control) 75 | } 76 | 77 | return c.NextChildFocusable(c.window, nil, forwards) 78 | } 79 | 80 | func (c *FocusController) NextChildFocusable(p Parent, after Control, forwards bool) Focusable { 81 | examineNext := after == nil 82 | children := p.Children() 83 | 84 | i := 0 85 | e := len(children) 86 | if !forwards { 87 | i = len(children) - 1 88 | e = -1 89 | } 90 | 91 | for i != e { 92 | f := children[i] 93 | if forwards { 94 | i++ 95 | } else { 96 | i-- 97 | } 98 | 99 | if !examineNext { 100 | if f.Control == after { 101 | examineNext = true 102 | } 103 | continue 104 | } 105 | 106 | if focusable := c.Focusable(f.Control); focusable != nil { 107 | return focusable 108 | } 109 | 110 | if container, ok := f.Control.(Container); ok { 111 | focusable := c.NextChildFocusable(container, nil, forwards) 112 | if focusable != nil { 113 | return focusable 114 | } 115 | } 116 | } 117 | return nil 118 | } 119 | 120 | func (c *FocusController) Focusable(ctrl Control) Focusable { 121 | focusable, _ := ctrl.(Focusable) 122 | if focusable != nil && focusable.IsFocusable() { 123 | return focusable 124 | } 125 | return nil 126 | } 127 | -------------------------------------------------------------------------------- /focusable.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gxui 6 | 7 | // Focusable is the optional interface implmented by controls that have the 8 | // ability to acquire focus. A control with focus will receive keyboard input 9 | // first. 10 | type Focusable interface { 11 | Control 12 | 13 | // IsFocusable returns true if the control is currently in a state where it 14 | // can acquire focus. 15 | IsFocusable() bool 16 | 17 | // HasFocus returns true when the control has focus. 18 | HasFocus() bool 19 | 20 | // GainedFocus is called when the Focusable gains focus. 21 | // This method is called by the FocusManager should not be called by the user. 22 | GainedFocus() 23 | 24 | // LostFocus is called when the Focusable loses focus. 25 | // This method is called by the FocusManager should not be called by the user. 26 | LostFocus() 27 | 28 | // OnGainedFocus subscribes f to be called whenever the control gains focus. 29 | OnGainedFocus(f func()) EventSubscription 30 | 31 | // OnLostFocus subscribes f to be called whenever the control loses focus. 32 | OnLostFocus(f func()) EventSubscription 33 | } 34 | -------------------------------------------------------------------------------- /font.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gxui 6 | 7 | import ( 8 | "github.com/google/gxui/math" 9 | ) 10 | 11 | // A Font represents a TrueType font loaded by the GXUI driver. 12 | type Font interface { 13 | LoadGlyphs(first, last rune) 14 | Size() int 15 | GlyphMaxSize() math.Size 16 | Measure(*TextBlock) math.Size 17 | Layout(*TextBlock) (offsets []math.Point) 18 | } 19 | 20 | // TextBlock is a sequence of runes to be laid out. 21 | type TextBlock struct { 22 | Runes []rune 23 | AlignRect math.Rect 24 | H HorizontalAlignment 25 | V VerticalAlignment 26 | } 27 | -------------------------------------------------------------------------------- /gxfont/gxfont.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package gxfont provides default fonts. 6 | // 7 | // Note that the Roboto and Droid Sans Mono fonts are owned by 8 | // Google Inc. (one of the Go Authors) and released under the Apache 2 9 | // license. Any notices distributed with applications build with GXUI 10 | // and using this package should include both the GXUI license and the 11 | // fonts license. 12 | package gxfont 13 | 14 | //go:generate go run mkfont.go 15 | 16 | import ( 17 | "bytes" 18 | "compress/flate" 19 | "io/ioutil" 20 | ) 21 | 22 | var ( 23 | // Default is the standard GXUI sans-serif font. 24 | Default []byte = inflate(roboto_regular) 25 | 26 | // Monospace is the standard GXUI fixed-width font. 27 | Monospace []byte = inflate(droid_sans_mono) 28 | ) 29 | 30 | func inflate(src []byte) []byte { 31 | r := bytes.NewReader(src) 32 | b, err := ioutil.ReadAll(flate.NewReader(r)) 33 | if err != nil { 34 | panic(err) 35 | } 36 | return b 37 | } 38 | -------------------------------------------------------------------------------- /gxfont/mkfont.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build ignore 6 | 7 | // Small program to generate roboto_regular.go and droid_sans_mono.go. 8 | // 9 | // TODO(crawshaw): italics, bold. 10 | package main 11 | 12 | import ( 13 | "bytes" 14 | "compress/flate" 15 | "fmt" 16 | "go/format" 17 | "io/ioutil" 18 | "log" 19 | "net/http" 20 | "os" 21 | ) 22 | 23 | var urls = map[string]string{ 24 | "roboto_regular": "https://github.com/google/fonts/raw/master/apache/roboto/Roboto-Regular.ttf", 25 | "droid_sans_mono": "https://github.com/google/fonts/raw/master/apache/droidsansmono/DroidSansMono.ttf", 26 | } 27 | 28 | func write(name, url string) { 29 | resp, err := http.Get(url) 30 | if err != nil { 31 | log.Fatal(err) 32 | } 33 | defer resp.Body.Close() 34 | data, err := ioutil.ReadAll(resp.Body) 35 | if err != nil { 36 | log.Fatalf("error reading %q: %v", url, err) 37 | } 38 | data, err = deflate(data) 39 | if err != nil { 40 | log.Fatalf("error deflating %q: %v", url, err) 41 | } 42 | 43 | w := new(bytes.Buffer) 44 | fmt.Fprint(w, "// GENERATED BY mkfont.go\n\n") 45 | fmt.Fprint(w, "package gxfont\n\n") 46 | fmt.Fprintf(w, "var %s = []byte{\n", name) 47 | 48 | for len(data) > 0 { 49 | n := 16 50 | if n > len(data) { 51 | n = len(data) 52 | } 53 | for _, c := range data[:n] { 54 | fmt.Fprintf(w, "0x%02x,", c) 55 | } 56 | fmt.Fprintf(w, "\n") 57 | data = data[n:] 58 | } 59 | 60 | fmt.Fprint(w, "}") 61 | wbytes := w.Bytes() 62 | 63 | b, err := format.Source(wbytes) 64 | if err != nil { 65 | os.Stderr.Write(wbytes) 66 | log.Fatalf("error formatting: %v", err) 67 | } 68 | 69 | f, err := os.Create(name + ".go") 70 | if err != nil { 71 | log.Fatal(err) 72 | } 73 | if _, err := f.Write(b); err != nil { 74 | log.Fatal(err) 75 | } 76 | if err := f.Close(); err != nil { 77 | log.Fatal(err) 78 | } 79 | } 80 | 81 | func main() { 82 | for name, url := range urls { 83 | write(name, url) 84 | } 85 | } 86 | 87 | func deflate(src []byte) (dst []byte, err error) { 88 | buf := new(bytes.Buffer) 89 | w, err := flate.NewWriter(buf, flate.DefaultCompression) 90 | if err != nil { 91 | return nil, err 92 | } 93 | if _, err := w.Write(src); err != nil { 94 | return nil, err 95 | } 96 | if err := w.Close(); err != nil { 97 | return nil, err 98 | } 99 | return buf.Bytes(), nil 100 | } 101 | -------------------------------------------------------------------------------- /image.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gxui 6 | 7 | import ( 8 | "github.com/google/gxui/math" 9 | ) 10 | 11 | type ScalingMode int 12 | 13 | const ( 14 | Scaling1to1 ScalingMode = iota 15 | ScalingExpandGreedy 16 | ScalingExplicitSize 17 | ) 18 | 19 | type AspectMode int 20 | 21 | const ( 22 | AspectStretch = iota 23 | AspectCorrectLetterbox 24 | AspectCorrectCrop 25 | ) 26 | 27 | type Image interface { 28 | Control 29 | Texture() Texture 30 | SetTexture(Texture) 31 | Canvas() Canvas 32 | SetCanvas(Canvas) 33 | BorderPen() Pen 34 | SetBorderPen(Pen) 35 | BackgroundBrush() Brush 36 | SetBackgroundBrush(Brush) 37 | ScalingMode() ScalingMode 38 | SetScalingMode(ScalingMode) 39 | SetExplicitSize(math.Size) 40 | AspectMode() AspectMode 41 | SetAspectMode(AspectMode) 42 | PixelAt(math.Point) (math.Point, bool) // TODO: Remove 43 | } 44 | -------------------------------------------------------------------------------- /interval/int_data.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package interval 6 | 7 | type IntData struct { 8 | start, end int 9 | data interface{} 10 | } 11 | 12 | type IntDataList []IntData 13 | 14 | func CreateIntData(start, end int, data interface{}) IntData { 15 | return IntData{ 16 | start: start, 17 | end: end, 18 | data: data, 19 | } 20 | } 21 | 22 | func (t IntData) Range() (start, end int) { 23 | return t.start, t.end 24 | } 25 | 26 | func (t IntData) Data() interface{} { 27 | return t.data 28 | } 29 | 30 | func (t IntData) Span() (start, end uint64) { 31 | return uint64(t.start), uint64(t.end) 32 | } 33 | 34 | func (t IntData) Contains(v int) bool { 35 | return v >= t.start && v < t.end 36 | } 37 | 38 | func (l IntDataList) Len() int { 39 | return len(l) 40 | } 41 | 42 | func (l IntDataList) Cap() int { 43 | return cap(l) 44 | } 45 | 46 | func (l *IntDataList) SetLen(len int) { 47 | *l = (*l)[:len] 48 | } 49 | 50 | func (l *IntDataList) GrowTo(length, capacity int) { 51 | old := *l 52 | *l = make(IntDataList, length, capacity) 53 | copy(*l, old) 54 | } 55 | 56 | func (l IntDataList) Copy(to, from, count int) { 57 | copy(l[to:to+count], l[from:from+count]) 58 | } 59 | 60 | func (l IntDataList) GetInterval(index int) (start, end uint64) { 61 | return l[index].Span() 62 | } 63 | 64 | func (l IntDataList) SetInterval(index int, start, end uint64) { 65 | l[index].start = int(start) 66 | l[index].end = int(end) 67 | } 68 | 69 | func (l IntDataList) MergeData(index int, i Node) { 70 | l[index].data = i.(IntData).data 71 | } 72 | 73 | func (l IntDataList) Overlaps(i IntData) IntDataList { 74 | first, count := Intersect(l, i) 75 | return l[first : first+count] 76 | } 77 | -------------------------------------------------------------------------------- /interval/int_data_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package interval 6 | 7 | import test "github.com/google/gxui/testing" 8 | 9 | import "testing" 10 | 11 | func TestIntDataListReplace(t *testing.T) { 12 | l := &IntDataList{ 13 | CreateIntData(10, 30, 1), 14 | CreateIntData(40, 80, 2), 15 | } 16 | Replace(l, CreateIntData(00, 15, 3)) 17 | Replace(l, CreateIntData(25, 45, 4)) 18 | Replace(l, CreateIntData(55, 65, 5)) 19 | Replace(l, CreateIntData(75, 90, 6)) 20 | test.AssertEquals(t, &IntDataList{ 21 | CreateIntData(00, 15, 3), 22 | CreateIntData(15, 25, 1), 23 | CreateIntData(25, 45, 4), 24 | CreateIntData(45, 55, 2), 25 | CreateIntData(55, 65, 5), 26 | CreateIntData(65, 75, 2), 27 | CreateIntData(75, 90, 6), 28 | }, l) 29 | } 30 | 31 | func TestIntDataListMerge(t *testing.T) { 32 | l := &IntDataList{ 33 | CreateIntData(10, 30, 1), 34 | CreateIntData(40, 80, 2), 35 | } 36 | Merge(l, CreateIntData(00, 15, 3)) 37 | Merge(l, CreateIntData(25, 45, 4)) 38 | Merge(l, CreateIntData(55, 65, 5)) 39 | Merge(l, CreateIntData(75, 90, 6)) 40 | test.AssertEquals(t, &IntDataList{ 41 | CreateIntData(00, 90, 6), 42 | }, l) 43 | } 44 | -------------------------------------------------------------------------------- /interval/u64.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package interval 6 | 7 | import ( 8 | "bytes" 9 | "encoding/binary" 10 | "fmt" 11 | "strings" 12 | ) 13 | 14 | type U64 struct { 15 | first uint64 16 | count uint64 17 | } 18 | 19 | type U64List []U64 20 | 21 | func CreateU64(first, count uint64) U64 { 22 | return U64{first, count} 23 | } 24 | 25 | func CreateU64Inc(first, last uint64) U64 { 26 | if last < first { 27 | panic(fmt.Errorf("Attempting to set last before first! [0x%.16x-0x%.16x]", first, last)) 28 | } 29 | return U64{first, 1 + last - first} 30 | } 31 | 32 | func (i U64) Expand(value uint64) U64 { 33 | if i.first > value { 34 | i.first = value 35 | } 36 | if i.Last() < value { 37 | i.count += value - i.Last() 38 | } 39 | return i 40 | } 41 | 42 | func (i U64) Contains(value uint64) bool { 43 | return i.first <= value && value <= i.Last() 44 | } 45 | 46 | func (i U64) Range() (start, end uint64) { 47 | return i.first, i.first + i.count 48 | } 49 | 50 | func (i U64) First() uint64 { 51 | return i.first 52 | } 53 | 54 | func (i U64) Last() uint64 { 55 | return i.first + i.count - 1 56 | } 57 | 58 | func (i U64) Count() uint64 { 59 | return i.count 60 | } 61 | 62 | func (i U64) String() string { 63 | return fmt.Sprintf("[0x%.16x-0x%.16x]", i.first, i.Last()) 64 | } 65 | 66 | func (i U64) Span() (start, end uint64) { 67 | return i.first, i.first + i.count 68 | } 69 | 70 | // encoding.BinaryMarshaler compliance 71 | func (i U64) MarshalBinary() ([]byte, error) { 72 | buf := &bytes.Buffer{} 73 | err := binary.Write(buf, binary.LittleEndian, i.first) 74 | if err != nil { 75 | return nil, err 76 | } 77 | err = binary.Write(buf, binary.LittleEndian, i.count) 78 | if err != nil { 79 | return nil, err 80 | } 81 | return buf.Bytes(), nil 82 | } 83 | 84 | // encoding.BinaryUnmarshaler compliance 85 | func (i *U64) UnmarshalBinary(data []byte) error { 86 | buf := bytes.NewBuffer(data) 87 | err := binary.Read(buf, binary.LittleEndian, &i.first) 88 | if err != nil { 89 | return err 90 | } 91 | err = binary.Read(buf, binary.LittleEndian, &i.count) 92 | return err 93 | } 94 | 95 | func (l U64List) Len() int { 96 | return len(l) 97 | } 98 | 99 | func (l U64List) Cap() int { 100 | return cap(l) 101 | } 102 | 103 | func (l *U64List) SetLen(len int) { 104 | *l = (*l)[:len] 105 | } 106 | 107 | func (l *U64List) GrowTo(length, capacity int) { 108 | old := *l 109 | *l = make(U64List, length, capacity) 110 | copy(*l, old) 111 | } 112 | 113 | func (l U64List) Copy(to, from, count int) { 114 | copy(l[to:to+count], l[from:from+count]) 115 | } 116 | 117 | func (l U64List) GetInterval(index int) (start, end uint64) { 118 | return l[index].Span() 119 | } 120 | 121 | func (l U64List) SetInterval(index int, start, end uint64) { 122 | l[index].first = start 123 | l[index].count = end - start 124 | } 125 | 126 | func (l U64List) Overlaps(i IntData) U64List { 127 | first, count := Intersect(l, i) 128 | return l[first : first+count] 129 | } 130 | 131 | func (l U64List) String() string { 132 | s := make([]string, len(l)) 133 | for i, v := range l { 134 | s[i] = fmt.Sprintf("%d%s", i, v) 135 | } 136 | return "{" + strings.Join(s, ",") + "}" 137 | } 138 | -------------------------------------------------------------------------------- /keyboard_controller.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gxui 6 | 7 | type KeyboardController struct { 8 | window Window 9 | } 10 | 11 | func CreateKeyboardController(w Window) *KeyboardController { 12 | c := &KeyboardController{ 13 | window: w, 14 | } 15 | w.OnKeyDown(c.keyDown) 16 | w.OnKeyUp(c.keyUp) 17 | w.OnKeyRepeat(c.keyPress) 18 | w.OnKeyStroke(c.keyStroke) 19 | return c 20 | } 21 | 22 | func (c *KeyboardController) keyDown(ev KeyboardEvent) { 23 | f := Control(c.window.Focus()) 24 | for f != nil { 25 | f.KeyDown(ev) 26 | f, _ = f.Parent().(Control) 27 | } 28 | c.keyPress(ev) 29 | } 30 | 31 | func (c *KeyboardController) keyUp(ev KeyboardEvent) { 32 | f := Control(c.window.Focus()) 33 | for f != nil { 34 | f.KeyUp(ev) 35 | f, _ = f.Parent().(Control) 36 | } 37 | } 38 | 39 | func (c *KeyboardController) keyPress(ev KeyboardEvent) { 40 | f := Control(c.window.Focus()) 41 | for f != nil { 42 | if f.KeyPress(ev) { 43 | return 44 | } 45 | f, _ = f.Parent().(Control) 46 | } 47 | c.window.KeyPress(ev) 48 | } 49 | 50 | func (c *KeyboardController) keyStroke(ev KeyStrokeEvent) { 51 | f := Control(c.window.Focus()) 52 | for f != nil { 53 | if f.KeyStroke(ev) { 54 | return 55 | } 56 | f, _ = f.Parent().(Control) 57 | } 58 | c.window.KeyStroke(ev) 59 | } 60 | -------------------------------------------------------------------------------- /keyboard_event.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gxui 6 | 7 | type KeyboardEvent struct { 8 | Key KeyboardKey 9 | Modifier KeyboardModifier 10 | } 11 | -------------------------------------------------------------------------------- /keyboard_key.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gxui 6 | 7 | type KeyboardKey int 8 | 9 | const ( 10 | KeyUnknown KeyboardKey = iota 11 | KeySpace 12 | KeyApostrophe 13 | KeyComma 14 | KeyMinus 15 | KeyPeriod 16 | KeySlash 17 | Key0 18 | Key1 19 | Key2 20 | Key3 21 | Key4 22 | Key5 23 | Key6 24 | Key7 25 | Key8 26 | Key9 27 | KeySemicolon 28 | KeyEqual 29 | KeyA 30 | KeyB 31 | KeyC 32 | KeyD 33 | KeyE 34 | KeyF 35 | KeyG 36 | KeyH 37 | KeyI 38 | KeyJ 39 | KeyK 40 | KeyL 41 | KeyM 42 | KeyN 43 | KeyO 44 | KeyP 45 | KeyQ 46 | KeyR 47 | KeyS 48 | KeyT 49 | KeyU 50 | KeyV 51 | KeyW 52 | KeyX 53 | KeyY 54 | KeyZ 55 | KeyLeftBracket 56 | KeyBackslash 57 | KeyRightBracket 58 | KeyGraveAccent 59 | KeyWorld1 60 | KeyWorld2 61 | KeyEscape 62 | KeyEnter 63 | KeyTab 64 | KeyBackspace 65 | KeyInsert 66 | KeyDelete 67 | KeyRight 68 | KeyLeft 69 | KeyDown 70 | KeyUp 71 | KeyPageUp 72 | KeyPageDown 73 | KeyHome 74 | KeyEnd 75 | KeyCapsLock 76 | KeyScrollLock 77 | KeyNumLock 78 | KeyPrintScreen 79 | KeyPause 80 | KeyF1 81 | KeyF2 82 | KeyF3 83 | KeyF4 84 | KeyF5 85 | KeyF6 86 | KeyF7 87 | KeyF8 88 | KeyF9 89 | KeyF10 90 | KeyF11 91 | KeyF12 92 | KeyKp0 93 | KeyKp1 94 | KeyKp2 95 | KeyKp3 96 | KeyKp4 97 | KeyKp5 98 | KeyKp6 99 | KeyKp7 100 | KeyKp8 101 | KeyKp9 102 | KeyKpDecimal 103 | KeyKpDivide 104 | KeyKpMultiply 105 | KeyKpSubtract 106 | KeyKpAdd 107 | KeyKpEnter 108 | KeyKpEqual 109 | KeyLeftShift 110 | KeyLeftControl 111 | KeyLeftAlt 112 | KeyLeftSuper 113 | KeyRightShift 114 | KeyRightControl 115 | KeyRightAlt 116 | KeyRightSuper 117 | KeyMenu 118 | KeyLast 119 | ) 120 | -------------------------------------------------------------------------------- /keyboard_modifier.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gxui 6 | 7 | type KeyboardModifier int 8 | 9 | const ( 10 | ModNone KeyboardModifier = 0 11 | ModShift KeyboardModifier = 1 12 | ModControl KeyboardModifier = 2 13 | ModAlt KeyboardModifier = 4 14 | ModSuper KeyboardModifier = 8 15 | ) 16 | 17 | func (m KeyboardModifier) Shift() bool { 18 | return m&ModShift != 0 19 | } 20 | 21 | func (m KeyboardModifier) Control() bool { 22 | return m&ModControl != 0 23 | } 24 | 25 | func (m KeyboardModifier) Alt() bool { 26 | return m&ModAlt != 0 27 | } 28 | 29 | func (m KeyboardModifier) Super() bool { 30 | return m&ModSuper != 0 31 | } 32 | -------------------------------------------------------------------------------- /keystroke_event.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gxui 6 | 7 | type KeyStrokeEvent struct { 8 | Character rune 9 | Modifier KeyboardModifier 10 | } 11 | -------------------------------------------------------------------------------- /label.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gxui 6 | 7 | type Label interface { 8 | Control 9 | Text() string 10 | SetText(string) 11 | Font() Font 12 | SetFont(Font) 13 | Color() Color 14 | SetColor(Color) 15 | Multiline() bool 16 | SetMultiline(bool) 17 | SetHorizontalAlignment(HorizontalAlignment) 18 | HorizontalAlignment() HorizontalAlignment 19 | SetVerticalAlignment(VerticalAlignment) 20 | VerticalAlignment() VerticalAlignment 21 | } 22 | -------------------------------------------------------------------------------- /linear_layout.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gxui 6 | 7 | // LinearLayout is a Container that lays out its child Controls into a column or 8 | // row. The layout will always start by positioning the first (0'th) child, and 9 | // then depending on the direction, will position each successive child either 10 | // to the left, top, right or bottom of the preceding child Control. 11 | // LinearLayout makes no effort to distribute remaining space evenly between the 12 | // children - an child control that is laid out before others will reduce the 13 | // remaining space given to the later children, even to the point that there is 14 | // zero space remaining. 15 | type LinearLayout interface { 16 | // LinearLayout extends the Control interface. 17 | Control 18 | 19 | // LinearLayout extends the Container interface. 20 | Container 21 | 22 | // Direction returns the direction of layout for this LinearLayout. 23 | Direction() Direction 24 | 25 | // Direction sets the direction of layout for this LinearLayout. 26 | SetDirection(Direction) 27 | 28 | // SizeMode returns the desired size behaviour for this LinearLayout. 29 | SizeMode() SizeMode 30 | 31 | // SetSizeMode sets the desired size behaviour for this LinearLayout. 32 | SetSizeMode(SizeMode) 33 | 34 | // HorizontalAlignment returns the alignment of the child Controls when laying 35 | // out TopToBottom or BottomToTop. It has no effect when the layout direction 36 | // is LeftToRight or RightToLeft. 37 | HorizontalAlignment() HorizontalAlignment 38 | 39 | // SetHorizontalAlignment sets the alignment of the child Controls when laying 40 | // out TopToBottom or BottomToTop. It has no effect when the layout direction 41 | // is LeftToRight or RightToLeft. 42 | SetHorizontalAlignment(HorizontalAlignment) 43 | 44 | // VerticalAlignment returns the alignment of the child Controls when laying 45 | // out LeftToRight or RightToLeft. It has no effect when the layout direction 46 | // is TopToBottom or BottomToTop. 47 | VerticalAlignment() VerticalAlignment 48 | 49 | // SetVerticalAlignment returns the alignment of the child Controls when 50 | // laying out LeftToRight or RightToLeft. It has no effect when the layout 51 | // direction is TopToBottom or BottomToTop. 52 | SetVerticalAlignment(VerticalAlignment) 53 | 54 | // BorderPen returns the Pen used to draw the LinearLayout's border. 55 | BorderPen() Pen 56 | 57 | // SetBorderPen sets the Pen used to draw the LinearLayout's border. 58 | SetBorderPen(Pen) 59 | 60 | // BackgroundBrush returns the Brush used to fill the LinearLayout's 61 | // background. 62 | BackgroundBrush() Brush 63 | 64 | // SetBackgroundBrush sets the Brush used to fill the LinearLayout's 65 | // background. 66 | SetBackgroundBrush(Brush) 67 | } 68 | -------------------------------------------------------------------------------- /list.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gxui 6 | 7 | import "github.com/google/gxui/math" 8 | 9 | type List interface { 10 | Focusable 11 | Parent 12 | Adapter() ListAdapter 13 | SetAdapter(ListAdapter) 14 | SetOrientation(Orientation) 15 | Orientation() Orientation 16 | BorderPen() Pen 17 | SetBorderPen(Pen) 18 | BackgroundBrush() Brush 19 | SetBackgroundBrush(Brush) 20 | ScrollTo(AdapterItem) 21 | IsItemVisible(AdapterItem) bool 22 | ItemControl(AdapterItem) Control 23 | Selected() AdapterItem 24 | Select(AdapterItem) bool 25 | OnSelectionChanged(func(AdapterItem)) EventSubscription 26 | OnItemClicked(func(MouseEvent, AdapterItem)) EventSubscription 27 | } 28 | 29 | // ListAdapter is an interface used to visualize a flat set of items. 30 | // Users of the ListAdapter should presume the data is unchanged until the 31 | // OnDataChanged or OnDataReplaced events are fired. 32 | type ListAdapter interface { 33 | // Count returns the total number of items. 34 | Count() int 35 | 36 | // ItemAt returns the AdapterItem for the item at index i. It is important 37 | // for the Adapter to return consistent AdapterItems for the same data, so 38 | // that selections can be persisted, or re-ordering animations can be played 39 | // when the dataset changes. 40 | // The AdapterItem returned must be equality-unique across all indices. 41 | ItemAt(index int) AdapterItem 42 | 43 | // ItemIndex returns the index of item, or -1 if the adapter does not contain 44 | // item. 45 | ItemIndex(item AdapterItem) int 46 | 47 | // Create returns a Control visualizing the item at the specified index. 48 | Create(theme Theme, index int) Control 49 | 50 | // Size returns the size that each of the item's controls will be displayed 51 | // at for the given theme. 52 | Size(Theme) math.Size 53 | 54 | // OnDataChanged registers f to be called when there is a partial change in 55 | // the items of the adapter. Scroll positions and selections should be 56 | // preserved if possible. 57 | // If recreateControls is true then each of the visible controls should be 58 | // recreated by re-calling Create(). 59 | OnDataChanged(f func(recreateControls bool)) EventSubscription 60 | 61 | // OnDataReplaced registers f to be called when there is a complete 62 | // replacement of items in the adapter. 63 | OnDataReplaced(f func()) EventSubscription 64 | } 65 | -------------------------------------------------------------------------------- /math/constants.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package math 6 | 7 | import ( 8 | "math" 9 | ) 10 | 11 | var ZeroSize Size 12 | var ZeroSpacing Spacing 13 | var ZeroPoint Point 14 | 15 | var MaxSize = Size{0x40000000, 0x40000000} // Picked to avoid integer overflows 16 | 17 | var Mat2Ident Mat2 = CreateMat2(1, 0, 0, 1) 18 | var Mat3Ident Mat3 = CreateMat3(1, 0, 0, 0, 1, 0, 0, 0, 1) 19 | 20 | const Pi = float32(math.Pi) 21 | const TwoPi = Pi * 2.0 22 | 23 | const MaxUint = ^uint(0) 24 | const MinUint = 0 25 | const MaxInt = int(MaxUint >> 1) 26 | const MinInt = -(MaxInt - 1) 27 | -------------------------------------------------------------------------------- /math/line_intersect.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package math 6 | 7 | func linesIntersect(p0, p1, q0, q1 Vec2) bool { 8 | const epsilon = 0.0001 9 | // http://stackoverflow.com/a/565282 10 | // t = (q − p) × s / (r × s) 11 | r, s := p1.Sub(p0), q1.Sub(q0) 12 | PmQ := p0.Sub(q0) 13 | QmP := q0.Sub(p0) 14 | ScR := s.Cross(r) 15 | if Absf(ScR) < 0.0001 { 16 | // Lines are parallel 17 | QmPcR := QmP.Cross(r) 18 | if Absf(QmPcR) < 0.0001 { 19 | // Colinear 20 | QmPdR := QmP.Dot(r) 21 | PmQdS := PmQ.Dot(s) 22 | if (0 <= QmPdR && QmPdR < r.Dot(r)) || 23 | (0 <= PmQdS && PmQdS < s.Dot(s)) { 24 | return true 25 | } 26 | } 27 | return false 28 | } 29 | t := PmQ.Cross(s) / ScR 30 | u := PmQ.Cross(r) / ScR 31 | return t > epsilon && u > epsilon && t < 1-epsilon && u < 1-epsilon 32 | } 33 | -------------------------------------------------------------------------------- /math/line_intersect_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package math 6 | 7 | import test "github.com/google/gxui/testing" 8 | import ( 9 | "testing" 10 | ) 11 | 12 | func v(a, b float32) Vec2 { 13 | return Vec2{X: a, Y: b} 14 | } 15 | 16 | func TestLinesIntersectCrossing(t *testing.T) { 17 | test.AssertEquals(t, true, linesIntersect(v(5, 0), v(5, 10), v(0, 5), v(10, 5))) 18 | } 19 | 20 | func TestLinesIntersectCrossingEnds(t *testing.T) { 21 | test.AssertEquals(t, false, linesIntersect(v(0, 0), v(0, 5), v(0, 0), v(5, 0))) 22 | test.AssertEquals(t, false, linesIntersect(v(0, 0), v(0, 5), v(0, 5), v(5, 5))) 23 | } 24 | func TestLinesIntersectColinearOverlap(t *testing.T) { 25 | test.AssertEquals(t, true, linesIntersect(v(0, 0), v(5, 0), v(0, 0), v(5, 0))) 26 | test.AssertEquals(t, true, linesIntersect(v(0, 0), v(5, 0), v(1, 0), v(4, 0))) 27 | test.AssertEquals(t, true, linesIntersect(v(0, 0), v(5, 0), v(0, 0), v(7, 0))) 28 | } 29 | 30 | func TestLinesIntersectParallel(t *testing.T) { 31 | test.AssertEquals(t, false, linesIntersect(v(5, 0), v(5, 10), v(6, 0), v(6, 10))) 32 | } 33 | 34 | func TestLinesIntersectColinearNonOverlap(t *testing.T) { 35 | test.AssertEquals(t, false, linesIntersect(v(0, 0), v(5, 0), v(5, 0), v(10, 0))) 36 | test.AssertEquals(t, false, linesIntersect(v(0, 0), v(4, 0), v(6, 0), v(10, 0))) 37 | } 38 | -------------------------------------------------------------------------------- /math/mat2.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package math 6 | 7 | import ( 8 | "fmt" 9 | ) 10 | 11 | // ╭ ╮ 12 | // │ M₀ M₁ │ 13 | // │ M₂ M₃ │ 14 | // ╰ ╯ 15 | type Mat2 [4]float32 16 | 17 | func CreateMat2(r0c0, r0c1, r1c0, r1c1 float32) Mat2 { 18 | return Mat2{ 19 | r0c0, r0c1, 20 | r1c0, r1c1, 21 | } 22 | } 23 | 24 | // ╭ ╮ 25 | // │ R₀ │ 26 | // │ R₁ │ 27 | // ╰ ╯ 28 | func CreateMat2FromRows(r0, r1 Vec2) Mat2 { 29 | return Mat2{ 30 | r0.X, r0.Y, 31 | r1.X, r1.Y, 32 | } 33 | } 34 | 35 | func (m Mat2) String() string { 36 | s := make([]string, 4) 37 | l := 0 38 | for i, v := range m { 39 | s[i] = fmt.Sprintf("%.5f", v) 40 | l = Max(l, len(s[i])) 41 | } 42 | for i, _ := range m { 43 | for len(s[i]) < l { 44 | s[i] = " " + s[i] 45 | } 46 | } 47 | p := "" 48 | for i := 0; i < l; i++ { 49 | p += " " 50 | } 51 | return fmt.Sprintf( 52 | "\n╭ %s %s ╮"+ 53 | "\n│ %s %s │"+ 54 | "\n│ %s %s │"+ 55 | "\n╰ %s %s ╯", 56 | p, p, 57 | s[0], s[1], 58 | s[2], s[3], 59 | p, p, 60 | ) 61 | } 62 | 63 | func (m Mat2) Rows() (r0, r1 Vec2) { 64 | return Vec2{m[0], m[1]}, Vec2{m[2], m[3]} 65 | } 66 | 67 | func (m Mat2) Row(i int) Vec2 { 68 | i *= 2 69 | return Vec2{m[i+0], m[i+1]} 70 | } 71 | 72 | func (m Mat2) Invert() Mat2 { 73 | 74 | // ╭ ╮⁻¹ ╭ ╮ 75 | // │ M₀ M₁ │ = 1 │ M₃ -M₁ │ 76 | // │ M₂ M₃ │ ─── │ -M₂ M₀ │ 77 | // ╰ ╯ det ╰ ╯ 78 | // 79 | // Where: det = M₀ • M₃ - M₁ • M₂ 80 | // 81 | det := m[0]*m[3] - m[1]*m[2] 82 | return DivM2S(CreateMat2(m[3], -m[1], -m[2], m[0]), det) 83 | } 84 | 85 | func (m Mat2) Transpose() Mat2 { 86 | return CreateMat2( 87 | m[0], m[2], 88 | m[1], m[3], 89 | ) 90 | } 91 | 92 | // ╭ ╮ 93 | // │ M₀ M₁ │ 94 | // [V₀, V₁] ⨯ │ M₂ M₃ │ = [V₀ • M₀ + V₁ • M₂, V₀ • M₁ + V₁ • M₃] 95 | // ╰ ╯ 96 | func MulVM2(v Vec2, m Mat2) Vec2 { 97 | a := m.Row(0).MulS(v.X) 98 | b := m.Row(1).MulS(v.Y) 99 | return a.Add(b) 100 | } 101 | 102 | func DivM2S(m Mat2, s float32) Mat2 { 103 | return CreateMat2FromRows( 104 | m.Row(0).DivS(s), 105 | m.Row(1).DivS(s), 106 | ) 107 | } 108 | -------------------------------------------------------------------------------- /math/mat2_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package math 6 | 7 | import test "github.com/google/gxui/testing" 8 | import "testing" 9 | 10 | func TestMat2InvertIdent(t *testing.T) { 11 | m := Mat2Ident.Invert() 12 | test.AssertEquals(t, Mat2Ident, m) 13 | } 14 | 15 | func TestMat2InvertSimple(t *testing.T) { 16 | m := CreateMat2(4, 3, 3, 2) 17 | a := m.Invert() 18 | e := CreateMat2(-2, 3, 3, -4) 19 | test.AssertEquals(t, e, a) 20 | } 21 | -------------------------------------------------------------------------------- /math/mat3.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package math 6 | 7 | import ( 8 | "fmt" 9 | ) 10 | 11 | // A 3x3 matrix: 12 | // ╭ ╮ 13 | // │ M₀ M₁ M₂ │ 14 | // │ M₃ M₄ M₅ │ 15 | // │ M₆ M₇ M₈ │ 16 | // ╰ ╯ 17 | type Mat3 [9]float32 18 | 19 | func CreateMat3(r0c0, r0c1, r0c2, r1c0, r1c1, r1c2, r2c0, r2c1, r2c2 float32) Mat3 { 20 | return Mat3{ 21 | r0c0, r0c1, r0c2, 22 | r1c0, r1c1, r1c2, 23 | r2c0, r2c1, r2c2, 24 | } 25 | } 26 | 27 | // Build a 3x3 matrix from 3 row vectors 28 | // ╭ ╮ 29 | // │ R₀ │ 30 | // │ R₁ │ 31 | // │ R₂ │ 32 | // ╰ ╯ 33 | func CreateMat3FromRows(r0, r1, r2 Vec3) Mat3 { 34 | return Mat3{ 35 | r0.X, r0.Y, r0.Z, 36 | r1.X, r1.Y, r1.Z, 37 | r2.X, r2.Y, r2.Z, 38 | } 39 | } 40 | 41 | // A 42 | // ╱ ╲ 43 | // ╱___╲ 44 | // B C 45 | // 46 | // [V₀, V₁, V₂] * M = [λ₀, λ₁, 1] 47 | // λ₂ = 1 - (λ₀ + λ₁) 48 | // 49 | // [V₀, V₁, V₂] = A • λ₀ + B • λ₁ + C • λ₂ 50 | // 51 | // A * M = (1, 0, 1) 52 | // B * M = (0, 1, 1) 53 | // C * M = (0, 0, 1) 54 | func CreateMat3PositionToBarycentric(a, b, c Vec2) Mat3 { 55 | m := CreateMat2FromRows(a.Sub(c), b.Sub(c)).Invert() 56 | o := MulVM2(c, m) 57 | return Mat3{ 58 | m[0], m[1], 0, 59 | m[2], m[3], 0, 60 | -o.X, -o.Y, 1, 61 | } 62 | } 63 | 64 | func (m Mat3) String() string { 65 | s := make([]string, 9) 66 | l := 0 67 | for i, v := range m { 68 | s[i] = fmt.Sprintf("%.5f", v) 69 | l = Max(l, len(s[i])) 70 | } 71 | for i, _ := range m { 72 | for len(s[i]) < l { 73 | s[i] = " " + s[i] 74 | } 75 | } 76 | p := "" 77 | for i := 0; i < l; i++ { 78 | p += " " 79 | } 80 | return fmt.Sprintf( 81 | "\n╭ %s %s %s ╮"+ 82 | "\n│ %s %s %s │"+ 83 | "\n│ %s %s %s │"+ 84 | "\n│ %s %s %s │"+ 85 | "\n╰ %s %s %s ╯", 86 | p, p, p, 87 | s[0], s[1], s[2], 88 | s[3], s[4], s[5], 89 | s[6], s[7], s[8], 90 | p, p, p, 91 | ) 92 | } 93 | 94 | func (m Mat3) Rows() (r0, r1, r2 Vec3) { 95 | return Vec3{m[0], m[1], m[2]}, Vec3{m[3], m[4], m[5]}, Vec3{m[6], m[7], m[8]} 96 | } 97 | 98 | func (m Mat3) Row(i int) Vec3 { 99 | i *= 3 100 | return Vec3{m[i+0], m[i+1], m[i+2]} 101 | } 102 | 103 | func (m Mat3) Invert() Mat3 { 104 | // ╭ ╮ 105 | // 1 │ C₁ ⨯ C₂ │ 106 | // M⁻¹ = ─── │ C₂ ⨯ C₀ │ 107 | // det │ C₀ ⨯ C₁ │ 108 | // ╰ ╯ 109 | // 110 | // Where: det = C₀ • (C₁ ⨯ C₂) 111 | // 112 | C0, C1, C2 := m.Transpose().Rows() 113 | C0C1, C1C2, C2C0 := C0.Cross(C1), C1.Cross(C2), C2.Cross(C0) 114 | det := C0.Dot(C1C2) 115 | inv := CreateMat3FromRows(C1C2, C2C0, C0C1).DivS(det) 116 | return inv 117 | } 118 | 119 | func (m Mat3) Transpose() Mat3 { 120 | return CreateMat3( 121 | m[0], m[3], m[6], 122 | m[1], m[4], m[7], 123 | m[2], m[5], m[8], 124 | ) 125 | } 126 | 127 | func (m Mat3) DivS(s float32) Mat3 { 128 | return CreateMat3FromRows( 129 | m.Row(0).DivS(s), 130 | m.Row(1).DivS(s), 131 | m.Row(2).DivS(s), 132 | ) 133 | } 134 | -------------------------------------------------------------------------------- /math/mat3_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package math 6 | 7 | import test "github.com/google/gxui/testing" 8 | import "testing" 9 | 10 | func TestMat3InvertIdent(t *testing.T) { 11 | m := Mat3Ident.Invert() 12 | test.AssertEquals(t, Mat3Ident, m) 13 | } 14 | 15 | func TestCreateMat3PositionToBarycentric(t *testing.T) { 16 | a := Vec2{+0.0, -1.0} 17 | b := Vec2{-1.0, 1.0} 18 | c := Vec2{+1.0, 1.0} 19 | m := CreateMat3PositionToBarycentric(a, b, c) 20 | test.AssertEquals(t, Vec3{1.0, 0.0, 1.0}, a.Vec3(1).MulM(m)) 21 | test.AssertEquals(t, Vec3{0.0, 1.0, 1.0}, b.Vec3(1).MulM(m)) 22 | test.AssertEquals(t, Vec3{0.0, 0.0, 1.0}, c.Vec3(1).MulM(m)) 23 | } 24 | -------------------------------------------------------------------------------- /math/math.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package math 6 | 7 | import ( 8 | "math" 9 | ) 10 | 11 | func R2D(r float32) float32 { 12 | return 180.0 * r / Pi 13 | } 14 | 15 | func D2R(r float32) float32 { 16 | return Pi * r / 180.0 17 | } 18 | 19 | func Absf(v float32) float32 { 20 | if v < 0 { 21 | return -v 22 | } else { 23 | return v 24 | } 25 | } 26 | 27 | func Round(v float32) int { 28 | if v < 0 { 29 | return int(v - 0.5) 30 | } else { 31 | return int(v + 0.4999999) 32 | } 33 | } 34 | 35 | func Sinf(v float32) float32 { 36 | return float32(math.Sin(float64(v))) 37 | } 38 | 39 | func Cosf(v float32) float32 { 40 | return float32(math.Cos(float64(v))) 41 | } 42 | 43 | func Tanf(v float32) float32 { 44 | return float32(math.Tan(float64(v))) 45 | } 46 | 47 | func Asinf(v float32) float32 { 48 | return float32(math.Asin(float64(v))) 49 | } 50 | 51 | func Acosf(v float32) float32 { 52 | return float32(math.Acos(float64(v))) 53 | } 54 | 55 | func Atanf(v float32) float32 { 56 | return float32(math.Atan(float64(v))) 57 | } 58 | 59 | func Sqrtf(v float32) float32 { 60 | return float32(math.Sqrt(float64(v))) 61 | } 62 | 63 | func Powf(v, e float32) float32 { 64 | return float32(math.Pow(float64(v), float64(e))) 65 | } 66 | 67 | func Lerp(a, b int, s float32) int { 68 | r := float32(b - a) 69 | return a + int(r*s) 70 | } 71 | 72 | func Lerpf(a, b float32, s float32) float32 { 73 | r := b - a 74 | return a + r*s 75 | } 76 | 77 | func Ramp(s float32, a, b float32) float32 { 78 | return (s - a) / (b - a) 79 | } 80 | 81 | func RampSat(s float32, a, b float32) float32 { 82 | return Saturate((s - a) / (b - a)) 83 | } 84 | 85 | func Saturate(x float32) float32 { 86 | return Clampf(x, 0, 1) 87 | } 88 | 89 | func SmoothStep(s float32, a, b float32) float32 { 90 | x := RampSat(s, a, b) 91 | return x * x * (3 - 2*x) 92 | } 93 | 94 | func Clamp(x, min, max int) int { 95 | switch { 96 | case x < min: 97 | return min 98 | case x > max: 99 | return max 100 | default: 101 | return x 102 | } 103 | } 104 | 105 | func Clampf(x, min, max float32) float32 { 106 | switch { 107 | case x < min: 108 | return min 109 | case x > max: 110 | return max 111 | default: 112 | return x 113 | } 114 | } 115 | 116 | func Min(values ...int) int { 117 | m := MaxInt 118 | for _, v := range values { 119 | if v < m { 120 | m = v 121 | } 122 | } 123 | return m 124 | } 125 | 126 | func Minf(values ...float32) float32 { 127 | m := float32(math.MaxFloat32) 128 | for _, v := range values { 129 | if v < m { 130 | m = v 131 | } 132 | } 133 | return m 134 | } 135 | 136 | func Max(values ...int) int { 137 | m := MinInt 138 | for _, v := range values { 139 | if v > m { 140 | m = v 141 | } 142 | } 143 | return m 144 | } 145 | 146 | func Maxf(values ...float32) float32 { 147 | m := float32(-math.MaxFloat32) 148 | for _, v := range values { 149 | if v > m { 150 | m = v 151 | } 152 | } 153 | return m 154 | } 155 | 156 | func Mod(a, b int) int { 157 | x := a % b 158 | if x < 0 { 159 | return x + b 160 | } else { 161 | return x 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /math/math_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package math 6 | 7 | import "testing" 8 | 9 | func TestRound(t *testing.T) { 10 | check := []struct { 11 | value float32 12 | expected int 13 | }{ 14 | {-1.1, -1}, 15 | {-0.9, -1}, 16 | {-0.5, -1}, 17 | {-0.1, 0}, 18 | {0.1, 0}, 19 | {0.5, 0}, 20 | {0.9, 1}, 21 | {1.1, 1}, 22 | } 23 | 24 | for _, v := range check { 25 | got := Round(v.value) 26 | if got != v.expected { 27 | t.Errorf("Round(%v) returned unexpected value. Expected: %v, Got: %v", v.value, v.expected, got) 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /math/point.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package math 6 | 7 | type Point struct { 8 | X, Y int 9 | } 10 | 11 | func NewPoint(X, Y int) Point { 12 | return Point{X: X, Y: Y} 13 | } 14 | 15 | func (p Point) Add(o Point) Point { 16 | return Point{p.X + o.X, p.Y + o.Y} 17 | } 18 | 19 | func (p Point) AddX(o int) Point { 20 | return Point{p.X + o, p.Y} 21 | } 22 | 23 | func (p Point) AddY(o int) Point { 24 | return Point{p.X, p.Y + o} 25 | } 26 | 27 | func (p Point) Sub(o Point) Point { 28 | return Point{p.X - o.X, p.Y - o.Y} 29 | } 30 | 31 | func (p Point) Neg() Point { 32 | return Point{-p.X, -p.Y} 33 | } 34 | 35 | func (p Point) SqrLen() int { 36 | return p.Dot(p) 37 | } 38 | 39 | func (p Point) Len() float32 { 40 | return Sqrtf(float32(p.SqrLen())) 41 | } 42 | 43 | func (p Point) Dot(o Point) int { 44 | return p.X*o.X + p.Y*o.Y 45 | } 46 | 47 | func (p Point) XY() (x, y int) { 48 | return p.X, p.Y 49 | } 50 | 51 | func (p Point) Vec2() Vec2 { 52 | return Vec2{float32(p.X), float32(p.Y)} 53 | } 54 | 55 | func (p Point) Vec3(z float32) Vec3 { 56 | return Vec3{float32(p.X), float32(p.Y), z} 57 | } 58 | 59 | func (p Point) Scale(s Vec2) Point { 60 | return Point{int(float32(p.X) * s.X), int(float32(p.Y) * s.Y)} 61 | } 62 | 63 | func (p Point) ScaleS(s float32) Point { 64 | return Point{int(float32(p.X) * s), int(float32(p.Y) * s)} 65 | } 66 | 67 | func (p Point) ScaleX(s float32) Point { 68 | return Point{int(float32(p.X) * s), p.Y} 69 | } 70 | 71 | func (p Point) ScaleY(s float32) Point { 72 | return Point{p.X, int(float32(p.Y) * s)} 73 | } 74 | 75 | func (p Point) Size() Size { 76 | return Size{p.X, p.Y} 77 | } 78 | 79 | func (p Point) Min(o Point) Point { 80 | return Point{Min(p.X, o.X), Min(p.Y, o.Y)} 81 | } 82 | 83 | func (p Point) Max(o Point) Point { 84 | return Point{Max(p.X, o.X), Max(p.Y, o.Y)} 85 | } 86 | 87 | func (p Point) Clamp(min, max Point) Point { 88 | return p.Min(max).Max(min) 89 | } 90 | 91 | func (p Point) Remap(from, to Rect) Point { 92 | return p.Sub(from.Min). 93 | ScaleX(float32(to.W()) / float32(from.W())). 94 | ScaleY(float32(to.H()) / float32(from.H())). 95 | Add(to.Min) 96 | } 97 | -------------------------------------------------------------------------------- /math/rect_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package math 6 | 7 | import test "github.com/google/gxui/testing" 8 | import "testing" 9 | 10 | func TestRectConstrainWithin(t *testing.T) { 11 | r1 := CreateRect(0, 0, 100, 100) 12 | r2 := CreateRect(40, 40, 60, 60) 13 | test.AssertEquals(t, r2, r2.Constrain(r1)) 14 | } 15 | 16 | func TestRectConstrainOutMin(t *testing.T) { 17 | r1 := CreateRect(0, 0, 100, 100) 18 | r2 := CreateRect(-20, -20, 20, 20) 19 | test.AssertEquals(t, CreateRect(0, 0, 40, 40), r2.Constrain(r1)) 20 | } 21 | 22 | func TestRectConstrainOutMax(t *testing.T) { 23 | r1 := CreateRect(0, 0, 100, 100) 24 | r2 := CreateRect(80, 80, 120, 120) 25 | test.AssertEquals(t, CreateRect(60, 60, 100, 100), r2.Constrain(r1)) 26 | } 27 | -------------------------------------------------------------------------------- /math/size.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package math 6 | 7 | import ( 8 | "math" 9 | ) 10 | 11 | type Size struct { 12 | W, H int 13 | } 14 | 15 | func (s Size) Point() Point { 16 | return Point{s.W, s.H} 17 | } 18 | 19 | func (s Size) Vec2() Vec2 { 20 | return Vec2{float32(s.W), float32(s.H)} 21 | } 22 | 23 | func (s Size) Rect() Rect { 24 | return CreateRect(0, 0, s.W, s.H) 25 | } 26 | 27 | func (s Size) CenteredRect() Rect { 28 | return CreateRect(-s.W/2, -s.H/2, s.W/2, s.H/2) 29 | } 30 | 31 | func (s Size) Scale(v Vec2) Size { 32 | return Size{ 33 | int(math.Ceil(float64(s.W) * float64(v.X))), 34 | int(math.Ceil(float64(s.H) * float64(v.Y))), 35 | } 36 | } 37 | func (s Size) ScaleS(v float32) Size { 38 | return Size{ 39 | int(math.Ceil(float64(s.W) * float64(v))), 40 | int(math.Ceil(float64(s.H) * float64(v))), 41 | } 42 | } 43 | 44 | func (s Size) Expand(sp Spacing) Size { 45 | return Size{s.W + sp.W(), s.H + sp.H()} 46 | } 47 | 48 | func (s Size) Contract(sp Spacing) Size { 49 | return Size{s.W - sp.W(), s.H - sp.H()} 50 | } 51 | 52 | func (s Size) Add(o Size) Size { 53 | return Size{s.W + o.W, s.H + o.H} 54 | } 55 | 56 | func (s Size) Sub(o Size) Size { 57 | return Size{s.W - o.W, s.H - o.H} 58 | } 59 | 60 | func (s Size) Min(o Size) Size { 61 | return Size{Min(s.W, o.W), Min(s.H, o.H)} 62 | } 63 | 64 | func (s Size) Max(o Size) Size { 65 | return Size{Max(s.W, o.W), Max(s.H, o.H)} 66 | } 67 | 68 | func (s Size) Clamp(min, max Size) Size { 69 | return Size{Clamp(s.W, min.W, max.W), Clamp(s.H, min.H, max.H)} 70 | } 71 | 72 | func (s Size) WH() (w, h int) { 73 | return s.W, s.H 74 | } 75 | 76 | func (s Size) Area() int { 77 | return s.W * s.H 78 | } 79 | 80 | func (s Size) EdgeAlignedFit(outer Rect, edgePoint Point) Rect { 81 | r := s.CenteredRect().Offset(edgePoint).Constrain(outer) 82 | if topFits := edgePoint.Y+s.H < outer.Max.Y; topFits { 83 | return r.OffsetY(edgePoint.Y - r.Min.Y) 84 | } 85 | if bottomFits := edgePoint.Y-s.H >= outer.Min.Y; bottomFits { 86 | return r.OffsetY(edgePoint.Y - r.Max.Y) 87 | } 88 | if leftFits := edgePoint.X+s.W < outer.Max.X; leftFits { 89 | return r.OffsetX(edgePoint.X - r.Min.X) 90 | } 91 | if rightFits := edgePoint.X-s.W >= outer.Min.X; rightFits { 92 | return r.OffsetX(edgePoint.X - r.Max.X) 93 | } 94 | return r 95 | } 96 | -------------------------------------------------------------------------------- /math/size_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package math 6 | 7 | import test "github.com/google/gxui/testing" 8 | import "testing" 9 | 10 | func TestSizeEdgeAlignedFitTopEdge(t *testing.T) { 11 | outer := CreateRect(0, 0, 100, 100) 12 | s := Size{10, 10} 13 | p := Point{50, 50} 14 | test.AssertEquals(t, CreateRect(45, 50, 55, 60), s.EdgeAlignedFit(outer, p)) 15 | } 16 | 17 | func TestSizeEdgeAlignedFitBottomEdge(t *testing.T) { 18 | outer := CreateRect(0, 0, 100, 100) 19 | s := Size{10, 10} 20 | p := Point{50, 95} 21 | test.AssertEquals(t, CreateRect(45, 85, 55, 95), s.EdgeAlignedFit(outer, p)) 22 | } 23 | 24 | func TestSizeEdgeAlignedFitLeftEdge(t *testing.T) { 25 | outer := CreateRect(0, 0, 100, 100) 26 | s := Size{10, 80} 27 | p := Point{5, 50} 28 | test.AssertEquals(t, CreateRect(5, 10, 15, 90), s.EdgeAlignedFit(outer, p)) 29 | } 30 | 31 | func TestSizeEdgeAlignedFitRightEdge(t *testing.T) { 32 | outer := CreateRect(0, 0, 100, 100) 33 | s := Size{10, 80} 34 | p := Point{95, 50} 35 | test.AssertEquals(t, CreateRect(85, 10, 95, 90), s.EdgeAlignedFit(outer, p)) 36 | } 37 | -------------------------------------------------------------------------------- /math/spacing.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package math 6 | 7 | type Spacing struct { 8 | L, T, R, B int 9 | } 10 | 11 | func CreateSpacing(s int) Spacing { 12 | return Spacing{s, s, s, s} 13 | } 14 | 15 | func (s Spacing) LT() Point { 16 | return Point{s.L, s.T} 17 | } 18 | 19 | func (s Spacing) W() int { 20 | return s.L + s.R 21 | } 22 | 23 | func (s Spacing) H() int { 24 | return s.T + s.B 25 | } 26 | 27 | func (s Spacing) Size() Size { 28 | return Size{s.W(), s.H()} 29 | } 30 | 31 | func (s Spacing) Add(o Spacing) Spacing { 32 | return Spacing{s.L + o.L, s.T + o.T, s.R + o.R, s.B + o.B} 33 | } 34 | 35 | func (s Spacing) Sub(o Spacing) Spacing { 36 | return Spacing{s.L - o.L, s.T - o.T, s.R - o.R, s.B - o.B} 37 | } 38 | 39 | func (s Spacing) Min(o Spacing) Spacing { 40 | return Spacing{Min(s.L, o.L), Min(s.T, o.T), Min(s.R, o.R), Min(s.B, o.B)} 41 | } 42 | 43 | func (s Spacing) Max(o Spacing) Spacing { 44 | return Spacing{Max(s.L, o.L), Max(s.T, o.T), Max(s.R, o.R), Max(s.B, o.B)} 45 | } 46 | -------------------------------------------------------------------------------- /math/vec2.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package math 6 | 7 | type Vec2 struct { 8 | X, Y float32 9 | } 10 | 11 | func (v Vec2) SqrLen() float32 { 12 | return v.Dot(v) 13 | } 14 | 15 | func (v Vec2) Len() float32 { 16 | return Sqrtf(v.SqrLen()) 17 | } 18 | 19 | func (v Vec2) ZeroLength() bool { 20 | return v.X == 0 && v.Y == 0 21 | } 22 | 23 | func (v Vec2) Normalize() Vec2 { 24 | l := v.Len() 25 | if l == 0 { 26 | return Vec2{0, 0} 27 | } else { 28 | return v.MulS(1.0 / v.Len()) 29 | } 30 | } 31 | 32 | func (v Vec2) Neg() Vec2 { 33 | return Vec2{-v.X, -v.Y} 34 | } 35 | 36 | func (v Vec2) Tangent() Vec2 { 37 | return Vec2{-v.Y, v.X} 38 | } 39 | 40 | func (v Vec2) Point() Point { 41 | return Point{Round(v.X), Round(v.Y)} 42 | } 43 | 44 | func (v Vec2) Vec3(z float32) Vec3 { 45 | return Vec3{v.X, v.Y, z} 46 | } 47 | 48 | func (v Vec2) Vec4(z, w float32) Vec4 { 49 | return Vec4{v.X, v.Y, z, w} 50 | } 51 | 52 | func (v Vec2) XY() (x, y float32) { 53 | return v.X, v.Y 54 | } 55 | 56 | func (v Vec2) Add(o Vec2) Vec2 { 57 | return Vec2{v.X + o.X, v.Y + o.Y} 58 | } 59 | 60 | func (v Vec2) Sub(o Vec2) Vec2 { 61 | return Vec2{v.X - o.X, v.Y - o.Y} 62 | } 63 | 64 | func (v Vec2) Mul(o Vec2) Vec2 { 65 | return Vec2{v.X * o.X, v.Y * o.Y} 66 | } 67 | 68 | func (v Vec2) Div(o Vec2) Vec2 { 69 | return Vec2{v.X / o.X, v.Y / o.Y} 70 | } 71 | 72 | func (v Vec2) Dot(o Vec2) float32 { 73 | return v.X*o.X + v.Y*o.Y 74 | } 75 | 76 | func (v Vec2) Cross(o Vec2) float32 { 77 | return v.X*o.Y - v.Y*o.X 78 | } 79 | 80 | func (v Vec2) MulS(s float32) Vec2 { 81 | return Vec2{v.X * s, v.Y * s} 82 | } 83 | 84 | func (v Vec2) DivS(s float32) Vec2 { 85 | return Vec2{v.X / s, v.Y / s} 86 | } 87 | -------------------------------------------------------------------------------- /math/vec3.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package math 6 | 7 | import ( 8 | "fmt" 9 | ) 10 | 11 | type Vec3 struct { 12 | X, Y, Z float32 13 | } 14 | 15 | func (v Vec3) String() string { 16 | return fmt.Sprintf("(%.5f, %.5f, %.5f)", v.X, v.Y, v.Z) 17 | } 18 | 19 | func (v Vec3) SqrLen() float32 { 20 | return v.Dot(v) 21 | } 22 | 23 | func (v Vec3) Len() float32 { 24 | return Sqrtf(v.SqrLen()) 25 | } 26 | 27 | func (v Vec3) Normalize() Vec3 { 28 | l := v.Len() 29 | if l == 0 { 30 | return Vec3{0, 0, 0} 31 | } else { 32 | return v.MulS(1.0 / v.Len()) 33 | } 34 | } 35 | 36 | func (v Vec3) Neg() Vec3 { 37 | return Vec3{-v.X, -v.Y, -v.Z} 38 | } 39 | 40 | func (v Vec3) XY() Vec2 { 41 | return Vec2{v.X, v.Y} 42 | } 43 | 44 | func (v Vec3) Add(o Vec3) Vec3 { 45 | return Vec3{v.X + o.X, v.Y + o.Y, v.Z + o.Z} 46 | } 47 | 48 | func (v Vec3) Sub(o Vec3) Vec3 { 49 | return Vec3{v.X - o.X, v.Y - o.Y, v.Z - o.Z} 50 | } 51 | 52 | func (v Vec3) Mul(o Vec3) Vec3 { 53 | return Vec3{v.X * o.X, v.Y * o.Y, v.Z * o.Z} 54 | } 55 | 56 | func (v Vec3) Div(o Vec3) Vec3 { 57 | return Vec3{v.X / o.X, v.Y / o.Y, v.Z / o.Z} 58 | } 59 | 60 | func (v Vec3) Dot(o Vec3) float32 { 61 | return v.X*o.X + v.Y*o.Y + v.Z*o.Z 62 | } 63 | 64 | func (v Vec3) Cross(o Vec3) Vec3 { 65 | return Vec3{ 66 | v.Y*o.Z - v.Z*o.Y, 67 | v.Z*o.X - v.X*o.Z, 68 | v.X*o.Y - v.Y*o.X, 69 | } 70 | } 71 | 72 | // ╭ ╮ 73 | // │ M₀ M₁ M₂ │ 74 | // [V₀, V₁, V₂] ⨯ │ M₃ M₄ M₅ │ = [R₀, R₁, R₂] 75 | // │ M₆ M₇ M₈ │ 76 | // ╰ ╯ 77 | // R₀ = V₀ • M₀ + V₁ • M₃ + V₂ • M₆ 78 | // R₁ = V₀ • M₁ + V₁ • M₄ + V₂ • M₇ 79 | // R₂ = V₀ • M₂ + V₁ • M₅ + V₂ • M₈ 80 | func (v Vec3) MulM(m Mat3) Vec3 { 81 | a := m.Row(0).MulS(v.X) 82 | b := m.Row(1).MulS(v.Y) 83 | c := m.Row(2).MulS(v.Z) 84 | return a.Add(b).Add(c) 85 | } 86 | 87 | func (v Vec3) MulS(s float32) Vec3 { 88 | return Vec3{v.X * s, v.Y * s, v.Z * s} 89 | } 90 | 91 | func (v Vec3) DivS(s float32) Vec3 { 92 | return Vec3{v.X / s, v.Y / s, v.Z / s} 93 | } 94 | -------------------------------------------------------------------------------- /math/vec4.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package math 6 | 7 | import ( 8 | "fmt" 9 | ) 10 | 11 | type Vec4 struct { 12 | X, Y, Z, W float32 13 | } 14 | 15 | func (v Vec4) String() string { 16 | return fmt.Sprintf("(%.5f, %.5f, %.5f, %.5f)", v.X, v.Y, v.Z, v.W) 17 | } 18 | 19 | func (v Vec4) SqrLen() float32 { 20 | return v.Dot(v) 21 | } 22 | 23 | func (v Vec4) Len() float32 { 24 | return Sqrtf(v.SqrLen()) 25 | } 26 | 27 | func (v Vec4) Normalize() Vec4 { 28 | l := v.Len() 29 | if l == 0 { 30 | return Vec4{0, 0, 0, 0} 31 | } else { 32 | return v.MulS(1.0 / v.Len()) 33 | } 34 | } 35 | 36 | func (v Vec4) Neg() Vec4 { 37 | return Vec4{-v.X, -v.Y, -v.Z, -v.W} 38 | } 39 | 40 | func (v Vec4) XY() Vec2 { 41 | return Vec2{v.X, v.Y} 42 | } 43 | 44 | func (v Vec4) Add(o Vec4) Vec4 { 45 | return Vec4{v.X + o.X, v.Y + o.Y, v.Z + o.Z, v.W + o.W} 46 | } 47 | 48 | func (v Vec4) Sub(o Vec4) Vec4 { 49 | return Vec4{v.X - o.X, v.Y - o.Y, v.Z - o.Z, v.W - o.W} 50 | } 51 | 52 | func (v Vec4) Mul(o Vec4) Vec4 { 53 | return Vec4{v.X * o.X, v.Y * o.Y, v.Z * o.Z, v.W * o.W} 54 | } 55 | 56 | func (v Vec4) Div(o Vec4) Vec4 { 57 | return Vec4{v.X / o.X, v.Y / o.Y, v.Z / o.Z, v.W / o.W} 58 | } 59 | 60 | func (v Vec4) Dot(o Vec4) float32 { 61 | return v.X*o.X + v.Y*o.Y + v.Z*o.Z + v.W*o.W 62 | } 63 | 64 | func (v Vec4) MulS(s float32) Vec4 { 65 | return Vec4{v.X * s, v.Y * s, v.Z * s, v.W * s} 66 | } 67 | 68 | func (v Vec4) DivS(s float32) Vec4 { 69 | return Vec4{v.X / s, v.Y / s, v.Z / s, v.W / s} 70 | } 71 | -------------------------------------------------------------------------------- /mixins/base/container.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package base 6 | 7 | import ( 8 | "github.com/google/gxui" 9 | "github.com/google/gxui/mixins/outer" 10 | "github.com/google/gxui/mixins/parts" 11 | ) 12 | 13 | type ContainerNoControlOuter interface { 14 | gxui.Container 15 | outer.PaintChilder 16 | outer.Painter 17 | outer.LayoutChildren 18 | } 19 | 20 | type ContainerOuter interface { 21 | ContainerNoControlOuter 22 | gxui.Control 23 | } 24 | 25 | type Container struct { 26 | parts.Attachable 27 | parts.Container 28 | parts.DrawPaint 29 | parts.InputEventHandler 30 | parts.Layoutable 31 | parts.Paddable 32 | parts.PaintChildren 33 | parts.Parentable 34 | parts.Visible 35 | } 36 | 37 | func (c *Container) Init(outer ContainerOuter, theme gxui.Theme) { 38 | c.Attachable.Init(outer) 39 | c.Container.Init(outer) 40 | c.DrawPaint.Init(outer, theme) 41 | c.InputEventHandler.Init(outer) 42 | c.Layoutable.Init(outer, theme) 43 | c.Paddable.Init(outer) 44 | c.PaintChildren.Init(outer) 45 | c.Parentable.Init(outer) 46 | c.Visible.Init(outer) 47 | 48 | // Interface compliance test 49 | _ = gxui.Container(c) 50 | } 51 | -------------------------------------------------------------------------------- /mixins/base/control.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package base 6 | 7 | import ( 8 | "github.com/google/gxui" 9 | "github.com/google/gxui/math" 10 | "github.com/google/gxui/mixins/outer" 11 | "github.com/google/gxui/mixins/parts" 12 | ) 13 | 14 | type ControlOuter interface { 15 | gxui.Control 16 | outer.Painter 17 | outer.Redrawer 18 | outer.Relayouter 19 | } 20 | 21 | type Control struct { 22 | parts.Attachable 23 | parts.DrawPaint 24 | parts.InputEventHandler 25 | parts.Layoutable 26 | parts.Parentable 27 | parts.Visible 28 | } 29 | 30 | func (c *Control) Init(outer ControlOuter, theme gxui.Theme) { 31 | c.Attachable.Init(outer) 32 | c.DrawPaint.Init(outer, theme) 33 | c.Layoutable.Init(outer, theme) 34 | c.InputEventHandler.Init(outer) 35 | c.Parentable.Init(outer) 36 | c.Visible.Init(outer) 37 | 38 | // Interface compliance test 39 | _ = gxui.Control(c) 40 | } 41 | 42 | func (c *Control) DesiredSize(min, max math.Size) math.Size { 43 | return max 44 | } 45 | 46 | func (c *Control) ContainsPoint(p math.Point) bool { 47 | return c.IsVisible() && c.Size().Rect().Contains(p) 48 | } 49 | -------------------------------------------------------------------------------- /mixins/button.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package mixins 6 | 7 | import ( 8 | "github.com/google/gxui" 9 | "github.com/google/gxui/math" 10 | "github.com/google/gxui/mixins/parts" 11 | ) 12 | 13 | type ButtonOuter interface { 14 | LinearLayoutOuter 15 | IsChecked() bool 16 | SetChecked(bool) 17 | } 18 | 19 | type Button struct { 20 | LinearLayout 21 | parts.Focusable 22 | 23 | outer ButtonOuter 24 | theme gxui.Theme 25 | label gxui.Label 26 | buttonType gxui.ButtonType 27 | checked bool 28 | } 29 | 30 | func (b *Button) Init(outer ButtonOuter, theme gxui.Theme) { 31 | b.LinearLayout.Init(outer, theme) 32 | b.Focusable.Init(outer) 33 | 34 | b.buttonType = gxui.PushButton 35 | b.theme = theme 36 | b.outer = outer 37 | 38 | // Interface compliance test 39 | _ = gxui.Button(b) 40 | } 41 | 42 | func (b *Button) Label() gxui.Label { 43 | return b.label 44 | } 45 | 46 | func (b *Button) Text() string { 47 | if b.label != nil { 48 | return b.label.Text() 49 | } else { 50 | return "" 51 | } 52 | } 53 | 54 | func (b *Button) SetText(text string) { 55 | if b.Text() == text { 56 | return 57 | } 58 | if text == "" { 59 | if b.label != nil { 60 | b.RemoveChild(b.label) 61 | b.label = nil 62 | } 63 | } else { 64 | if b.label == nil { 65 | b.label = b.theme.CreateLabel() 66 | b.label.SetMargin(math.ZeroSpacing) 67 | b.AddChild(b.label) 68 | } 69 | b.label.SetText(text) 70 | } 71 | } 72 | 73 | func (b *Button) Type() gxui.ButtonType { 74 | return b.buttonType 75 | } 76 | 77 | func (b *Button) SetType(buttonType gxui.ButtonType) { 78 | if buttonType != b.buttonType { 79 | b.buttonType = buttonType 80 | b.outer.Redraw() 81 | } 82 | } 83 | 84 | func (b *Button) IsChecked() bool { 85 | return b.checked 86 | } 87 | 88 | func (b *Button) SetChecked(checked bool) { 89 | if checked != b.checked { 90 | b.checked = checked 91 | b.outer.Redraw() 92 | } 93 | } 94 | 95 | // InputEventHandler override 96 | func (b *Button) Click(ev gxui.MouseEvent) (consume bool) { 97 | if ev.Button == gxui.MouseButtonLeft { 98 | if b.buttonType == gxui.ToggleButton { 99 | b.outer.SetChecked(!b.outer.IsChecked()) 100 | } 101 | b.LinearLayout.Click(ev) 102 | return true 103 | } 104 | return b.LinearLayout.Click(ev) 105 | } 106 | 107 | func (b *Button) KeyPress(ev gxui.KeyboardEvent) (consume bool) { 108 | consume = b.LinearLayout.KeyPress(ev) 109 | if ev.Key == gxui.KeySpace || ev.Key == gxui.KeyEnter { 110 | me := gxui.MouseEvent{ 111 | Button: gxui.MouseButtonLeft, 112 | } 113 | return b.Click(me) 114 | } 115 | return 116 | } 117 | -------------------------------------------------------------------------------- /mixins/code_suggestion_adapter.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package mixins 6 | 7 | import ( 8 | "github.com/google/gxui" 9 | ) 10 | 11 | type SuggestionAdapter struct { 12 | gxui.FilteredListAdapter 13 | } 14 | 15 | func (a *SuggestionAdapter) SetSuggestions(suggestions []gxui.CodeSuggestion) { 16 | items := make([]gxui.FilteredListItem, len(suggestions)) 17 | for i, s := range suggestions { 18 | items[i].Name = s.Name() 19 | items[i].Data = s 20 | } 21 | a.SetItems(items) 22 | } 23 | 24 | func (a *SuggestionAdapter) Suggestion(item gxui.AdapterItem) gxui.CodeSuggestion { 25 | return item.(gxui.FilteredListItem).Data.(gxui.CodeSuggestion) 26 | } 27 | -------------------------------------------------------------------------------- /mixins/label.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package mixins 6 | 7 | import ( 8 | "strings" 9 | 10 | "github.com/google/gxui" 11 | "github.com/google/gxui/math" 12 | "github.com/google/gxui/mixins/base" 13 | ) 14 | 15 | type LabelOuter interface { 16 | base.ControlOuter 17 | } 18 | 19 | type Label struct { 20 | base.Control 21 | 22 | outer LabelOuter 23 | font gxui.Font 24 | color gxui.Color 25 | horizontalAlignment gxui.HorizontalAlignment 26 | verticalAlignment gxui.VerticalAlignment 27 | multiline bool 28 | text string 29 | } 30 | 31 | func (l *Label) Init(outer LabelOuter, theme gxui.Theme, font gxui.Font, color gxui.Color) { 32 | if font == nil { 33 | panic("Cannot create a label with a nil font") 34 | } 35 | l.Control.Init(outer, theme) 36 | l.outer = outer 37 | l.font = font 38 | l.color = color 39 | l.horizontalAlignment = gxui.AlignLeft 40 | l.verticalAlignment = gxui.AlignMiddle 41 | // Interface compliance test 42 | _ = gxui.Label(l) 43 | } 44 | 45 | func (l *Label) Text() string { 46 | return l.text 47 | } 48 | 49 | func (l *Label) SetText(text string) { 50 | if l.text != text { 51 | l.text = text 52 | l.outer.Relayout() 53 | } 54 | } 55 | 56 | func (l *Label) Font() gxui.Font { 57 | return l.font 58 | } 59 | 60 | func (l *Label) SetFont(font gxui.Font) { 61 | if l.font != font { 62 | l.font = font 63 | l.Relayout() 64 | } 65 | } 66 | 67 | func (l *Label) Color() gxui.Color { 68 | return l.color 69 | } 70 | 71 | func (l *Label) SetColor(color gxui.Color) { 72 | if l.color != color { 73 | l.color = color 74 | l.outer.Redraw() 75 | } 76 | } 77 | 78 | func (l *Label) Multiline() bool { 79 | return l.multiline 80 | } 81 | 82 | func (l *Label) SetMultiline(multiline bool) { 83 | if l.multiline != multiline { 84 | l.multiline = multiline 85 | l.outer.Relayout() 86 | } 87 | } 88 | 89 | func (l *Label) DesiredSize(min, max math.Size) math.Size { 90 | t := l.text 91 | if !l.multiline { 92 | t = strings.Replace(t, "\n", " ", -1) 93 | } 94 | s := l.font.Measure(&gxui.TextBlock{Runes: []rune(t)}) 95 | return s.Clamp(min, max) 96 | } 97 | 98 | func (l *Label) SetHorizontalAlignment(horizontalAlignment gxui.HorizontalAlignment) { 99 | if l.horizontalAlignment != horizontalAlignment { 100 | l.horizontalAlignment = horizontalAlignment 101 | l.Redraw() 102 | } 103 | } 104 | 105 | func (l *Label) HorizontalAlignment() gxui.HorizontalAlignment { 106 | return l.horizontalAlignment 107 | } 108 | 109 | func (l *Label) SetVerticalAlignment(verticalAlignment gxui.VerticalAlignment) { 110 | if l.verticalAlignment != verticalAlignment { 111 | l.verticalAlignment = verticalAlignment 112 | l.Redraw() 113 | } 114 | } 115 | 116 | func (l *Label) VerticalAlignment() gxui.VerticalAlignment { 117 | return l.verticalAlignment 118 | } 119 | 120 | // parts.DrawPaint overrides 121 | func (l *Label) Paint(c gxui.Canvas) { 122 | r := l.outer.Size().Rect() 123 | t := l.text 124 | if !l.multiline { 125 | t = strings.Replace(t, "\n", " ", -1) 126 | } 127 | 128 | runes := []rune(t) 129 | offsets := l.font.Layout(&gxui.TextBlock{ 130 | Runes: runes, 131 | AlignRect: r, 132 | H: l.horizontalAlignment, 133 | V: l.verticalAlignment, 134 | }) 135 | c.DrawRunes(l.font, runes, offsets, l.color) 136 | } 137 | -------------------------------------------------------------------------------- /mixins/linear_layout.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package mixins 6 | 7 | import ( 8 | "github.com/google/gxui" 9 | "github.com/google/gxui/mixins/base" 10 | "github.com/google/gxui/mixins/parts" 11 | ) 12 | 13 | type LinearLayoutOuter interface { 14 | base.ContainerOuter 15 | } 16 | 17 | type LinearLayout struct { 18 | base.Container 19 | parts.LinearLayout 20 | parts.BackgroundBorderPainter 21 | } 22 | 23 | func (l *LinearLayout) Init(outer LinearLayoutOuter, theme gxui.Theme) { 24 | l.Container.Init(outer, theme) 25 | l.LinearLayout.Init(outer) 26 | l.BackgroundBorderPainter.Init(outer) 27 | l.SetMouseEventTarget(true) 28 | l.SetBackgroundBrush(gxui.TransparentBrush) 29 | l.SetBorderPen(gxui.TransparentPen) 30 | 31 | // Interface compliance test 32 | _ = gxui.LinearLayout(l) 33 | } 34 | 35 | func (l *LinearLayout) Paint(c gxui.Canvas) { 36 | r := l.Size().Rect() 37 | l.BackgroundBorderPainter.PaintBackground(c, r) 38 | l.PaintChildren.Paint(c) 39 | l.BackgroundBorderPainter.PaintBorder(c, r) 40 | } 41 | -------------------------------------------------------------------------------- /mixins/outer/attachable.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package outer 6 | 7 | import ( 8 | "github.com/google/gxui" 9 | ) 10 | 11 | type Attachable interface { 12 | Attached() bool 13 | Attach() 14 | Detach() 15 | OnAttach(func()) gxui.EventSubscription 16 | OnDetach(func()) gxui.EventSubscription 17 | } 18 | -------------------------------------------------------------------------------- /mixins/outer/draw.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package outer 6 | 7 | import ( 8 | "github.com/google/gxui" 9 | ) 10 | 11 | type Draw interface { 12 | Draw() gxui.Canvas 13 | } 14 | -------------------------------------------------------------------------------- /mixins/outer/isvisibiler.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package outer 6 | 7 | type IsVisibler interface { 8 | IsVisible() bool 9 | } 10 | -------------------------------------------------------------------------------- /mixins/outer/layout.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package outer 6 | 7 | import ( 8 | "github.com/google/gxui/math" 9 | ) 10 | 11 | type Layout interface { 12 | Layout(math.Rect) 13 | } 14 | -------------------------------------------------------------------------------- /mixins/outer/layout_children.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package outer 6 | 7 | type LayoutChildren interface { 8 | LayoutChildren() 9 | } 10 | -------------------------------------------------------------------------------- /mixins/outer/paint_childer.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package outer 6 | 7 | import ( 8 | "github.com/google/gxui" 9 | ) 10 | 11 | type PaintChilder interface { 12 | PaintChild(c gxui.Canvas, child *gxui.Child, idx int) 13 | } 14 | -------------------------------------------------------------------------------- /mixins/outer/painter.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package outer 6 | 7 | import ( 8 | "github.com/google/gxui" 9 | ) 10 | 11 | type Painter interface { 12 | Paint(gxui.Canvas) 13 | } 14 | -------------------------------------------------------------------------------- /mixins/outer/parenter.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package outer 6 | 7 | import ( 8 | "github.com/google/gxui" 9 | ) 10 | 11 | type Parenter interface { 12 | Parent() gxui.Parent 13 | } 14 | -------------------------------------------------------------------------------- /mixins/outer/redrawer.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package outer 6 | 7 | type Redrawer interface { 8 | Redraw() 9 | } 10 | -------------------------------------------------------------------------------- /mixins/outer/relayouter.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package outer 6 | 7 | type Relayouter interface { 8 | Relayout() 9 | } 10 | -------------------------------------------------------------------------------- /mixins/outer/sized.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package outer 6 | 7 | import ( 8 | "github.com/google/gxui/math" 9 | ) 10 | 11 | type Sized interface { 12 | Size() math.Size 13 | SetSize(math.Size) 14 | } 15 | -------------------------------------------------------------------------------- /mixins/parts/attachable.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package parts 6 | 7 | import ( 8 | "github.com/google/gxui" 9 | "github.com/google/gxui/mixins/outer" 10 | ) 11 | 12 | type AttachableOuter interface { 13 | outer.Relayouter 14 | } 15 | 16 | type Attachable struct { 17 | outer AttachableOuter 18 | onAttach gxui.Event 19 | onDetach gxui.Event 20 | attached bool 21 | } 22 | 23 | func (a *Attachable) Init(outer AttachableOuter) { 24 | a.outer = outer 25 | } 26 | 27 | func (a *Attachable) Attached() bool { 28 | return a.attached 29 | } 30 | 31 | func (a *Attachable) Attach() { 32 | if a.attached { 33 | panic("Control already attached") 34 | } 35 | a.attached = true 36 | if a.onAttach != nil { 37 | a.onAttach.Fire() 38 | } 39 | } 40 | 41 | func (a *Attachable) Detach() { 42 | if !a.attached { 43 | panic("Control already detached") 44 | } 45 | a.attached = false 46 | if a.onDetach != nil { 47 | a.onDetach.Fire() 48 | } 49 | } 50 | 51 | func (a *Attachable) OnAttach(f func()) gxui.EventSubscription { 52 | if a.onAttach == nil { 53 | a.onAttach = gxui.CreateEvent(func() {}) 54 | } 55 | return a.onAttach.Listen(f) 56 | } 57 | 58 | func (a *Attachable) OnDetach(f func()) gxui.EventSubscription { 59 | if a.onDetach == nil { 60 | a.onDetach = gxui.CreateEvent(func() {}) 61 | } 62 | return a.onDetach.Listen(f) 63 | } 64 | -------------------------------------------------------------------------------- /mixins/parts/background_border_painter.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package parts 6 | 7 | import ( 8 | "github.com/google/gxui" 9 | "github.com/google/gxui/math" 10 | "github.com/google/gxui/mixins/outer" 11 | ) 12 | 13 | type BackgroundBorderPainterOuter interface { 14 | outer.Redrawer 15 | } 16 | 17 | type BackgroundBorderPainter struct { 18 | outer BackgroundBorderPainterOuter 19 | brush gxui.Brush 20 | pen gxui.Pen 21 | } 22 | 23 | func (b *BackgroundBorderPainter) Init(outer BackgroundBorderPainterOuter) { 24 | b.outer = outer 25 | b.brush = gxui.DefaultBrush 26 | b.pen = gxui.DefaultPen 27 | } 28 | 29 | func (b *BackgroundBorderPainter) PaintBackground(c gxui.Canvas, r math.Rect) { 30 | if b.brush.Color.A != 0 { 31 | w := b.pen.Width 32 | c.DrawRoundedRect(r, w, w, w, w, gxui.TransparentPen, b.brush) 33 | } 34 | } 35 | 36 | func (b *BackgroundBorderPainter) PaintBorder(c gxui.Canvas, r math.Rect) { 37 | if b.pen.Color.A != 0 && b.pen.Width != 0 { 38 | w := b.pen.Width 39 | c.DrawRoundedRect(r, w, w, w, w, b.pen, gxui.TransparentBrush) 40 | } 41 | } 42 | 43 | func (b *BackgroundBorderPainter) BackgroundBrush() gxui.Brush { 44 | return b.brush 45 | } 46 | 47 | func (b *BackgroundBorderPainter) SetBackgroundBrush(brush gxui.Brush) { 48 | if b.brush != brush { 49 | b.brush = brush 50 | b.outer.Redraw() 51 | } 52 | } 53 | 54 | func (b *BackgroundBorderPainter) BorderPen() gxui.Pen { 55 | return b.pen 56 | } 57 | 58 | func (b *BackgroundBorderPainter) SetBorderPen(pen gxui.Pen) { 59 | if b.pen != pen { 60 | b.pen = pen 61 | b.outer.Redraw() 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /mixins/parts/draw_paint.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package parts 6 | 7 | import ( 8 | "fmt" 9 | "runtime" 10 | 11 | "github.com/google/gxui" 12 | "github.com/google/gxui/mixins/outer" 13 | ) 14 | 15 | const debugVerifyDetachOnGC = false 16 | 17 | type DrawPaintOuter interface { 18 | outer.Attachable 19 | outer.Painter 20 | outer.Parenter 21 | outer.Sized 22 | } 23 | 24 | type DrawPaint struct { 25 | outer DrawPaintOuter 26 | driver gxui.Driver 27 | canvas gxui.Canvas 28 | dirty bool 29 | redrawRequested bool 30 | } 31 | 32 | func verifyDetach(o DrawPaintOuter) { 33 | if o.Attached() { 34 | panic(fmt.Errorf("%T garbage collected while still attached", o)) 35 | } 36 | } 37 | 38 | func (d *DrawPaint) Init(outer DrawPaintOuter, theme gxui.Theme) { 39 | d.outer = outer 40 | d.driver = theme.Driver() 41 | 42 | if debugVerifyDetachOnGC { 43 | runtime.SetFinalizer(d.outer, verifyDetach) 44 | } 45 | } 46 | 47 | func (d *DrawPaint) Redraw() { 48 | d.driver.AssertUIGoroutine() 49 | if !d.redrawRequested { 50 | if p := d.outer.Parent(); p != nil { 51 | d.redrawRequested = true 52 | p.Redraw() 53 | } 54 | } 55 | } 56 | 57 | func (d *DrawPaint) Draw() gxui.Canvas { 58 | if !d.outer.Attached() { 59 | panic(fmt.Errorf("Attempting to draw a non-attached control %T", d.outer)) 60 | } 61 | 62 | s := d.outer.Size() 63 | if s.Area() == 0 { 64 | return nil // No area to draw in 65 | } 66 | if d.canvas == nil || d.canvas.Size() != s || d.redrawRequested { 67 | d.canvas = d.driver.CreateCanvas(s) 68 | d.redrawRequested = false 69 | d.outer.Paint(d.canvas) 70 | d.canvas.Complete() 71 | } 72 | return d.canvas 73 | } 74 | -------------------------------------------------------------------------------- /mixins/parts/focusable.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package parts 6 | 7 | import ( 8 | "github.com/google/gxui" 9 | ) 10 | 11 | type FocusableOuter interface{} 12 | 13 | type Focusable struct { 14 | outer FocusableOuter 15 | focusable bool 16 | hasFocus bool 17 | onGainedFocus gxui.Event 18 | onLostFocus gxui.Event 19 | } 20 | 21 | func (f *Focusable) Init(outer FocusableOuter) { 22 | f.outer = outer 23 | f.focusable = true 24 | } 25 | 26 | // gxui.Control compliance 27 | func (f *Focusable) IsFocusable() bool { 28 | return f.focusable 29 | } 30 | 31 | func (f *Focusable) HasFocus() bool { 32 | return f.hasFocus 33 | } 34 | 35 | func (f *Focusable) SetFocusable(bool) { 36 | f.focusable = true 37 | } 38 | 39 | func (f *Focusable) OnGainedFocus(l func()) gxui.EventSubscription { 40 | if f.onGainedFocus == nil { 41 | f.onGainedFocus = gxui.CreateEvent(f.GainedFocus) 42 | } 43 | return f.onGainedFocus.Listen(l) 44 | } 45 | 46 | func (f *Focusable) OnLostFocus(l func()) gxui.EventSubscription { 47 | if f.onLostFocus == nil { 48 | f.onLostFocus = gxui.CreateEvent(f.LostFocus) 49 | } 50 | return f.onLostFocus.Listen(l) 51 | } 52 | 53 | func (f *Focusable) GainedFocus() { 54 | f.hasFocus = true 55 | if f.onGainedFocus != nil { 56 | f.onGainedFocus.Fire() 57 | } 58 | } 59 | 60 | func (f *Focusable) LostFocus() { 61 | f.hasFocus = false 62 | if f.onLostFocus != nil { 63 | f.onLostFocus.Fire() 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /mixins/parts/layoutable.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package parts 6 | 7 | import ( 8 | "fmt" 9 | 10 | "github.com/google/gxui" 11 | "github.com/google/gxui/math" 12 | "github.com/google/gxui/mixins/outer" 13 | ) 14 | 15 | type LayoutableOuter interface { 16 | outer.Parenter 17 | outer.Redrawer 18 | } 19 | 20 | type Layoutable struct { 21 | outer LayoutableOuter 22 | driver gxui.Driver 23 | margin math.Spacing 24 | size math.Size 25 | relayoutRequested bool 26 | inLayoutChildren bool // True when calling LayoutChildren 27 | } 28 | 29 | func (l *Layoutable) Init(outer LayoutableOuter, theme gxui.Theme) { 30 | l.outer = outer 31 | l.driver = theme.Driver() 32 | } 33 | 34 | func (l *Layoutable) SetMargin(m math.Spacing) { 35 | l.margin = m 36 | if p := l.outer.Parent(); p != nil { 37 | p.Relayout() 38 | } 39 | } 40 | 41 | func (l *Layoutable) Margin() math.Spacing { 42 | return l.margin 43 | } 44 | 45 | func (l *Layoutable) Size() math.Size { 46 | return l.size 47 | } 48 | 49 | func (l *Layoutable) SetSize(size math.Size) { 50 | if size.W < 0 { 51 | panic(fmt.Errorf("SetSize() called with a negative width. Size: %v", size)) 52 | } 53 | if size.H < 0 { 54 | panic(fmt.Errorf("SetSize() called with a negative height. Size: %v", size)) 55 | } 56 | 57 | sizeChanged := l.size != size 58 | l.size = size 59 | if l.relayoutRequested || sizeChanged { 60 | l.relayoutRequested = false 61 | l.inLayoutChildren = true 62 | callLayoutChildrenIfSupported(l.outer) 63 | l.inLayoutChildren = false 64 | l.outer.Redraw() 65 | } 66 | } 67 | 68 | func (l *Layoutable) Relayout() { 69 | l.driver.AssertUIGoroutine() 70 | if l.inLayoutChildren { 71 | panic("Cannot call Relayout() while in LayoutChildren") 72 | } 73 | if !l.relayoutRequested { 74 | if p := l.outer.Parent(); p != nil { 75 | l.relayoutRequested = true 76 | p.Relayout() 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /mixins/parts/paddable.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package parts 6 | 7 | import ( 8 | "github.com/google/gxui/math" 9 | "github.com/google/gxui/mixins/outer" 10 | ) 11 | 12 | type PaddableOuter interface { 13 | outer.LayoutChildren 14 | outer.Redrawer 15 | } 16 | 17 | type Paddable struct { 18 | outer PaddableOuter 19 | padding math.Spacing 20 | } 21 | 22 | func (p *Paddable) Init(outer PaddableOuter) { 23 | p.outer = outer 24 | } 25 | 26 | func (p *Paddable) SetPadding(m math.Spacing) { 27 | p.padding = m 28 | p.outer.LayoutChildren() 29 | p.outer.Redraw() 30 | } 31 | 32 | func (p *Paddable) Padding() math.Spacing { 33 | return p.padding 34 | } 35 | -------------------------------------------------------------------------------- /mixins/parts/paint_children.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package parts 6 | 7 | import ( 8 | "github.com/google/gxui" 9 | "github.com/google/gxui/mixins/outer" 10 | ) 11 | 12 | type PaintChildrenOuter interface { 13 | gxui.Container 14 | outer.PaintChilder 15 | outer.Sized 16 | } 17 | 18 | type PaintChildren struct { 19 | outer PaintChildrenOuter 20 | } 21 | 22 | func (p *PaintChildren) Init(outer PaintChildrenOuter) { 23 | p.outer = outer 24 | } 25 | 26 | func (p *PaintChildren) Paint(c gxui.Canvas) { 27 | for i, v := range p.outer.Children() { 28 | if v.Control.IsVisible() { 29 | c.Push() 30 | c.AddClip(v.Control.Size().Rect().Offset(v.Offset)) 31 | p.outer.PaintChild(c, v, i) 32 | c.Pop() 33 | } 34 | } 35 | } 36 | 37 | func (p *PaintChildren) PaintChild(c gxui.Canvas, child *gxui.Child, idx int) { 38 | if canvas := child.Control.Draw(); canvas != nil { 39 | c.DrawCanvas(canvas, child.Offset) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /mixins/parts/parentable.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package parts 6 | 7 | import ( 8 | "github.com/google/gxui" 9 | ) 10 | 11 | type ParentableOuter interface{} 12 | 13 | type Parentable struct { 14 | outer ParentableOuter 15 | parent gxui.Parent 16 | } 17 | 18 | func (p *Parentable) Init(outer ParentableOuter) { 19 | p.outer = outer 20 | } 21 | 22 | func (p *Parentable) Parent() gxui.Parent { 23 | return p.parent 24 | } 25 | 26 | func (p *Parentable) SetParent(parent gxui.Parent) { 27 | p.parent = parent 28 | } 29 | -------------------------------------------------------------------------------- /mixins/parts/utils.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package parts 6 | 7 | import ( 8 | "github.com/google/gxui/mixins/outer" 9 | ) 10 | 11 | func callLayoutChildrenIfSupported(i interface{}) { 12 | switch ty := i.(type) { 13 | case outer.LayoutChildren: 14 | ty.LayoutChildren() 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /mixins/parts/visible.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package parts 6 | 7 | import ( 8 | "github.com/google/gxui/mixins/outer" 9 | ) 10 | 11 | type VisibleOuter interface { 12 | outer.Redrawer 13 | outer.Parenter 14 | } 15 | 16 | type Visible struct { 17 | outer VisibleOuter 18 | visible bool 19 | } 20 | 21 | func (v *Visible) Init(outer VisibleOuter) { 22 | v.outer = outer 23 | v.visible = true 24 | } 25 | 26 | func (v *Visible) IsVisible() bool { 27 | return v.visible 28 | } 29 | 30 | func (v *Visible) SetVisible(visible bool) { 31 | if v.visible != visible { 32 | v.visible = visible 33 | if p := v.outer.Parent(); p != nil { 34 | p.Redraw() 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /mixins/progress_bar.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package mixins 6 | 7 | import ( 8 | "github.com/google/gxui" 9 | "github.com/google/gxui/math" 10 | "github.com/google/gxui/mixins/base" 11 | "github.com/google/gxui/mixins/parts" 12 | ) 13 | 14 | type ProgressBarOuter interface { 15 | base.ControlOuter 16 | PaintProgress(gxui.Canvas, math.Rect, float32) 17 | } 18 | 19 | type ProgressBar struct { 20 | base.Control 21 | parts.BackgroundBorderPainter 22 | 23 | outer ProgressBarOuter 24 | desiredSize math.Size 25 | progress, target int 26 | } 27 | 28 | func (b *ProgressBar) Init(outer ProgressBarOuter, theme gxui.Theme) { 29 | b.outer = outer 30 | b.Control.Init(outer, theme) 31 | b.BackgroundBorderPainter.Init(outer) 32 | b.desiredSize = math.MaxSize 33 | b.target = 100 34 | 35 | // Interface compliance test 36 | _ = gxui.ProgressBar(b) 37 | } 38 | 39 | func (b *ProgressBar) Paint(c gxui.Canvas) { 40 | frac := math.Saturate(float32(b.progress) / float32(b.target)) 41 | r := b.outer.Size().Rect() 42 | b.PaintBackground(c, r) 43 | b.outer.PaintProgress(c, r, frac) 44 | b.PaintBorder(c, r) 45 | } 46 | 47 | func (b *ProgressBar) PaintProgress(c gxui.Canvas, r math.Rect, frac float32) { 48 | r.Max.X = math.Lerp(r.Min.X, r.Max.X, frac) 49 | c.DrawRect(r, gxui.CreateBrush(gxui.Gray50)) 50 | } 51 | 52 | func (b *ProgressBar) DesiredSize(min, max math.Size) math.Size { 53 | return b.desiredSize.Clamp(min, max) 54 | } 55 | 56 | // gxui.ProgressBar compliance 57 | func (b *ProgressBar) SetDesiredSize(size math.Size) { 58 | b.desiredSize = size 59 | b.Relayout() 60 | } 61 | 62 | func (b *ProgressBar) SetProgress(progress int) { 63 | if b.progress != progress { 64 | b.progress = progress 65 | b.Redraw() 66 | } 67 | } 68 | 69 | func (b *ProgressBar) Progress() int { 70 | return b.progress 71 | } 72 | 73 | func (b *ProgressBar) SetTarget(target int) { 74 | if b.target != target { 75 | b.target = target 76 | b.Redraw() 77 | } 78 | } 79 | 80 | func (b *ProgressBar) Target() int { 81 | return b.target 82 | } 83 | -------------------------------------------------------------------------------- /mixins/splitter_bar.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package mixins 6 | 7 | import ( 8 | "github.com/google/gxui" 9 | "github.com/google/gxui/math" 10 | "github.com/google/gxui/mixins/base" 11 | ) 12 | 13 | type SplitterBarOuter interface { 14 | base.ControlOuter 15 | } 16 | 17 | type SplitterBar struct { 18 | base.Control 19 | 20 | onDrag func(wndPnt math.Point) 21 | outer SplitterBarOuter 22 | theme gxui.Theme 23 | onDragStart gxui.Event 24 | onDragEnd gxui.Event 25 | backgroundColor gxui.Color 26 | foregroundColor gxui.Color 27 | isDragging bool 28 | } 29 | 30 | func (b *SplitterBar) Init(outer SplitterBarOuter, theme gxui.Theme) { 31 | b.Control.Init(outer, theme) 32 | 33 | b.outer = outer 34 | b.theme = theme 35 | b.onDragStart = gxui.CreateEvent(func(gxui.MouseEvent) {}) 36 | b.onDragEnd = gxui.CreateEvent(func(gxui.MouseEvent) {}) 37 | b.backgroundColor = gxui.Red 38 | b.foregroundColor = gxui.Green 39 | } 40 | 41 | func (b *SplitterBar) SetBackgroundColor(c gxui.Color) { 42 | b.backgroundColor = c 43 | } 44 | 45 | func (b *SplitterBar) SetForegroundColor(c gxui.Color) { 46 | b.foregroundColor = c 47 | } 48 | 49 | func (b *SplitterBar) OnSplitterDragged(f func(wndPnt math.Point)) { 50 | b.onDrag = f 51 | } 52 | 53 | func (b *SplitterBar) IsDragging() bool { 54 | return b.isDragging 55 | } 56 | 57 | func (b *SplitterBar) OnDragStart(f func(gxui.MouseEvent)) gxui.EventSubscription { 58 | return b.onDragStart.Listen(f) 59 | } 60 | 61 | func (b *SplitterBar) OnDragEnd(f func(gxui.MouseEvent)) gxui.EventSubscription { 62 | return b.onDragEnd.Listen(f) 63 | } 64 | 65 | // parts.DrawPaint overrides 66 | func (b *SplitterBar) Paint(c gxui.Canvas) { 67 | r := b.outer.Size().Rect() 68 | c.DrawRect(r, gxui.CreateBrush(b.backgroundColor)) 69 | if b.foregroundColor != b.backgroundColor { 70 | c.DrawRect(r.ContractI(1), gxui.CreateBrush(b.foregroundColor)) 71 | } 72 | } 73 | 74 | // InputEventHandler overrides 75 | func (b *SplitterBar) MouseDown(e gxui.MouseEvent) { 76 | b.isDragging = true 77 | b.onDragStart.Fire(e) 78 | var mms, mus gxui.EventSubscription 79 | mms = e.Window.OnMouseMove(func(we gxui.MouseEvent) { 80 | if b.onDrag != nil { 81 | b.onDrag(we.WindowPoint) 82 | } 83 | }) 84 | mus = e.Window.OnMouseUp(func(we gxui.MouseEvent) { 85 | mms.Unlisten() 86 | mus.Unlisten() 87 | b.isDragging = false 88 | b.onDragEnd.Fire(we) 89 | }) 90 | 91 | b.InputEventHandler.MouseDown(e) 92 | } 93 | -------------------------------------------------------------------------------- /mixins/table_layout.go: -------------------------------------------------------------------------------- 1 | package mixins 2 | 3 | import ( 4 | "github.com/google/gxui" 5 | "github.com/google/gxui/math" 6 | "github.com/google/gxui/mixins/base" 7 | ) 8 | 9 | type Cell struct { 10 | x, y, w, h int 11 | } 12 | 13 | func (c Cell) AtColumn(x int) bool { 14 | return c.x <= x && c.x+c.w >= x 15 | } 16 | 17 | func (c Cell) AtRow(y int) bool { 18 | return c.y <= y && c.y+c.h >= y 19 | } 20 | 21 | type TableLayoutOuter interface { 22 | base.ContainerOuter 23 | } 24 | 25 | type TableLayout struct { 26 | base.Container 27 | 28 | outer TableLayoutOuter 29 | 30 | grid map[gxui.Control]Cell 31 | rows int 32 | columns int 33 | } 34 | 35 | func (l *TableLayout) Init(outer TableLayoutOuter, theme gxui.Theme) { 36 | l.Container.Init(outer, theme) 37 | l.outer = outer 38 | l.grid = make(map[gxui.Control]Cell) 39 | 40 | // Interface compliance test 41 | _ = gxui.TableLayout(l) 42 | } 43 | 44 | func (l *TableLayout) LayoutChildren() { 45 | s := l.outer.Size().Contract(l.outer.Padding()) 46 | o := l.outer.Padding().LT() 47 | 48 | cw, ch := s.W/l.columns, s.H/l.rows 49 | 50 | var cr math.Rect 51 | 52 | for _, c := range l.outer.Children() { 53 | cm := c.Control.Margin() 54 | cell := l.grid[c.Control] 55 | 56 | x, y := cell.x*cw, cell.y*ch 57 | w, h := x+cell.w*cw, y+cell.h*ch 58 | 59 | cr = math.CreateRect(x+cm.L, y+cm.T, w-cm.R, h-cm.B) 60 | 61 | c.Layout(cr.Offset(o).Canon()) 62 | } 63 | } 64 | 65 | func (l *TableLayout) DesiredSize(min, max math.Size) math.Size { 66 | return max 67 | } 68 | 69 | func (l *TableLayout) SetGrid(columns, rows int) { 70 | if l.columns != columns { 71 | if l.columns > columns { 72 | for c := l.columns; c > columns; c-- { 73 | for _, cell := range l.grid { 74 | if cell.AtColumn(c) { 75 | panic("Can't remove column with cells") 76 | } 77 | } 78 | l.columns-- 79 | } 80 | } else { 81 | l.columns = columns 82 | } 83 | } 84 | 85 | if l.rows != rows { 86 | if l.rows > rows { 87 | for r := l.rows; r > rows; r-- { 88 | for _, cell := range l.grid { 89 | if cell.AtRow(r) { 90 | panic("Can't remove row with cells") 91 | } 92 | } 93 | l.rows-- 94 | } 95 | } else { 96 | l.rows = rows 97 | } 98 | } 99 | 100 | if l.rows != rows || l.columns != columns { 101 | l.LayoutChildren() 102 | } 103 | } 104 | 105 | func (l *TableLayout) SetChildAt(x, y, w, h int, child gxui.Control) *gxui.Child { 106 | if x+w > l.columns || y+h > l.rows { 107 | panic("Cell is out of grid") 108 | } 109 | 110 | for _, c := range l.grid { 111 | if c.x+c.w > x && c.x < x+w && c.y+c.h > y && c.y < y+h { 112 | panic("Cell already has a child") 113 | } 114 | } 115 | 116 | l.grid[child] = Cell{x, y, w, h} 117 | return l.Container.AddChild(child) 118 | } 119 | 120 | func (l *TableLayout) RemoveChild(child gxui.Control) { 121 | delete(l.grid, child) 122 | l.Container.RemoveChild(child) 123 | } 124 | -------------------------------------------------------------------------------- /mouse_button.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gxui 6 | 7 | type MouseButton int 8 | 9 | const ( 10 | MouseButtonLeft MouseButton = iota 11 | MouseButtonMiddle 12 | MouseButtonRight 13 | ) 14 | -------------------------------------------------------------------------------- /mouse_event.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gxui 6 | 7 | import ( 8 | "github.com/google/gxui/math" 9 | ) 10 | 11 | type MouseEvent struct { 12 | Button MouseButton 13 | State MouseState 14 | Point math.Point // Local to the event receiver 15 | WindowPoint math.Point 16 | Window Window 17 | ScrollX, ScrollY int 18 | Modifier KeyboardModifier 19 | } 20 | -------------------------------------------------------------------------------- /mouse_state.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gxui 6 | 7 | type MouseState int 8 | 9 | func (s MouseState) IsDown(b MouseButton) bool { 10 | return s&(1<= from { 17 | start = transform(start) 18 | } 19 | if end >= from { 20 | end = transform(end) 21 | } 22 | interval.Merge(&res, TextSelection{start, end, s.caretAtStart}) 23 | } 24 | return res 25 | } 26 | 27 | func (l TextSelectionList) TransformCarets(from int, transform func(i int) int) TextSelectionList { 28 | res := TextSelectionList{} 29 | for _, s := range l { 30 | if s.caretAtStart && s.start >= from { 31 | s.start = transform(s.start) 32 | } else if s.end >= from { 33 | s.end = transform(s.end) 34 | } 35 | if s.start > s.end { 36 | tmp := s.start 37 | s.start = s.end 38 | s.end = tmp 39 | s.caretAtStart = !s.caretAtStart 40 | } 41 | interval.Merge(&res, s) 42 | } 43 | return res 44 | } 45 | 46 | func (l TextSelectionList) Len() int { 47 | return len(l) 48 | } 49 | 50 | func (l TextSelectionList) Cap() int { 51 | return cap(l) 52 | } 53 | 54 | func (l *TextSelectionList) SetLen(len int) { 55 | *l = (*l)[:len] 56 | } 57 | 58 | func (l *TextSelectionList) GrowTo(length, capacity int) { 59 | old := *l 60 | *l = make(TextSelectionList, length, capacity) 61 | copy(*l, old) 62 | } 63 | 64 | func (l TextSelectionList) Copy(to, from, count int) { 65 | copy(l[to:to+count], l[from:from+count]) 66 | } 67 | 68 | func (l TextSelectionList) GetInterval(index int) (start, end uint64) { 69 | return l[index].Span() 70 | } 71 | 72 | func (l TextSelectionList) SetInterval(index int, start, end uint64) { 73 | l[index].start = int(start) 74 | l[index].end = int(end) 75 | } 76 | 77 | func (l TextSelectionList) MergeData(index int, i interval.Node) { 78 | l[index].caretAtStart = i.(TextSelection).caretAtStart 79 | } 80 | -------------------------------------------------------------------------------- /textbox.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gxui 6 | 7 | import ( 8 | "github.com/google/gxui/math" 9 | ) 10 | 11 | type TextBox interface { 12 | Focusable 13 | OnSelectionChanged(func()) EventSubscription 14 | OnTextChanged(func([]TextBoxEdit)) EventSubscription 15 | Padding() math.Spacing 16 | SetPadding(math.Spacing) 17 | Runes() []rune 18 | Text() string 19 | SetText(string) 20 | Font() Font 21 | SetFont(Font) 22 | Multiline() bool 23 | SetMultiline(bool) 24 | DesiredWidth() int 25 | SetDesiredWidth(desiredWidth int) 26 | TextColor() Color 27 | SetTextColor(Color) 28 | Select(TextSelectionList) 29 | SelectAll() 30 | Carets() []int 31 | RuneIndexAt(p math.Point) (idx int, found bool) 32 | TextAt(s, e int) string 33 | WordAt(runeIndex int) string 34 | ScrollToLine(int) 35 | ScrollToRune(int) 36 | LineIndex(runeIndex int) int 37 | LineStart(line int) int 38 | LineEnd(line int) int 39 | } 40 | -------------------------------------------------------------------------------- /texture.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gxui 6 | 7 | import ( 8 | "image" 9 | 10 | "github.com/google/gxui/math" 11 | ) 12 | 13 | type Texture interface { 14 | Image() image.Image 15 | Size() math.Size 16 | SizePixels() math.Size 17 | FlipY() bool 18 | SetFlipY(bool) 19 | } 20 | -------------------------------------------------------------------------------- /theme.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gxui 6 | 7 | type Theme interface { 8 | Driver() Driver 9 | DefaultFont() Font 10 | SetDefaultFont(Font) 11 | DefaultMonospaceFont() Font 12 | SetDefaultMonospaceFont(Font) 13 | CreateBubbleOverlay() BubbleOverlay 14 | CreateButton() Button 15 | CreateCodeEditor() CodeEditor 16 | CreateDropDownList() DropDownList 17 | CreateImage() Image 18 | CreateLabel() Label 19 | CreateLinearLayout() LinearLayout 20 | CreateList() List 21 | CreatePanelHolder() PanelHolder 22 | CreateProgressBar() ProgressBar 23 | CreateScrollBar() ScrollBar 24 | CreateScrollLayout() ScrollLayout 25 | CreateSplitterLayout() SplitterLayout 26 | CreateTableLayout() TableLayout 27 | CreateTextBox() TextBox 28 | CreateTree() Tree 29 | CreateWindow(width, height int, title string) Window 30 | } 31 | -------------------------------------------------------------------------------- /themes/basic/bubble_overlay.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package basic 6 | 7 | import ( 8 | "github.com/google/gxui" 9 | "github.com/google/gxui/math" 10 | "github.com/google/gxui/mixins" 11 | ) 12 | 13 | type BubbleOverlay struct { 14 | mixins.BubbleOverlay 15 | theme *Theme 16 | } 17 | 18 | func CreateBubbleOverlay(theme *Theme) gxui.BubbleOverlay { 19 | b := &BubbleOverlay{} 20 | b.Init(b, theme) 21 | b.SetMargin(math.Spacing{L: 3, T: 3, R: 3, B: 3}) 22 | b.SetPadding(math.Spacing{L: 5, T: 5, R: 5, B: 5}) 23 | b.SetPen(theme.BubbleOverlayStyle.Pen) 24 | b.SetBrush(theme.BubbleOverlayStyle.Brush) 25 | b.theme = theme 26 | return b 27 | } 28 | -------------------------------------------------------------------------------- /themes/basic/button.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package basic 6 | 7 | import ( 8 | "github.com/google/gxui" 9 | "github.com/google/gxui/math" 10 | "github.com/google/gxui/mixins" 11 | ) 12 | 13 | type Button struct { 14 | mixins.Button 15 | theme *Theme 16 | } 17 | 18 | func CreateButton(theme *Theme) gxui.Button { 19 | b := &Button{} 20 | b.Init(b, theme) 21 | b.theme = theme 22 | b.SetPadding(math.Spacing{L: 3, T: 3, R: 3, B: 3}) 23 | b.SetMargin(math.Spacing{L: 3, T: 3, R: 3, B: 3}) 24 | b.SetBackgroundBrush(b.theme.ButtonDefaultStyle.Brush) 25 | b.SetBorderPen(b.theme.ButtonDefaultStyle.Pen) 26 | b.OnMouseEnter(func(gxui.MouseEvent) { b.Redraw() }) 27 | b.OnMouseExit(func(gxui.MouseEvent) { b.Redraw() }) 28 | b.OnMouseDown(func(gxui.MouseEvent) { b.Redraw() }) 29 | b.OnMouseUp(func(gxui.MouseEvent) { b.Redraw() }) 30 | b.OnGainedFocus(b.Redraw) 31 | b.OnLostFocus(b.Redraw) 32 | return b 33 | } 34 | 35 | // Button internal overrides 36 | func (b *Button) Paint(c gxui.Canvas) { 37 | pen := b.Button.BorderPen() 38 | brush := b.Button.BackgroundBrush() 39 | fontColor := b.theme.ButtonDefaultStyle.FontColor 40 | 41 | switch { 42 | case b.IsMouseDown(gxui.MouseButtonLeft) && b.IsMouseOver(): 43 | pen = b.theme.ButtonPressedStyle.Pen 44 | brush = b.theme.ButtonPressedStyle.Brush 45 | fontColor = b.theme.ButtonPressedStyle.FontColor 46 | case b.IsMouseOver(): 47 | pen = b.theme.ButtonOverStyle.Pen 48 | brush = b.theme.ButtonOverStyle.Brush 49 | fontColor = b.theme.ButtonOverStyle.FontColor 50 | } 51 | 52 | if l := b.Label(); l != nil { 53 | l.SetColor(fontColor) 54 | } 55 | 56 | r := b.Size().Rect() 57 | 58 | c.DrawRoundedRect(r, 2, 2, 2, 2, gxui.TransparentPen, brush) 59 | 60 | b.PaintChildren.Paint(c) 61 | 62 | c.DrawRoundedRect(r, 2, 2, 2, 2, pen, gxui.TransparentBrush) 63 | 64 | if b.IsChecked() { 65 | pen = b.theme.HighlightStyle.Pen 66 | brush = b.theme.HighlightStyle.Brush 67 | c.DrawRoundedRect(r, 2.0, 2.0, 2.0, 2.0, pen, brush) 68 | } 69 | 70 | if b.HasFocus() { 71 | pen = b.theme.FocusedStyle.Pen 72 | brush = b.theme.FocusedStyle.Brush 73 | c.DrawRoundedRect(r.ContractI(int(pen.Width)), 3.0, 3.0, 3.0, 3.0, pen, brush) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /themes/basic/code_editor.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package basic 6 | 7 | import ( 8 | "github.com/google/gxui" 9 | "github.com/google/gxui/math" 10 | "github.com/google/gxui/mixins" 11 | ) 12 | 13 | type CodeEditor struct { 14 | mixins.CodeEditor 15 | theme *Theme 16 | } 17 | 18 | func CreateCodeEditor(theme *Theme) gxui.CodeEditor { 19 | t := &CodeEditor{} 20 | t.theme = theme 21 | t.Init(t, theme.Driver(), theme, theme.DefaultMonospaceFont()) 22 | t.SetTextColor(theme.TextBoxDefaultStyle.FontColor) 23 | t.SetMargin(math.Spacing{L: 3, T: 3, R: 3, B: 3}) 24 | t.SetPadding(math.Spacing{L: 3, T: 3, R: 3, B: 3}) 25 | t.SetBorderPen(gxui.TransparentPen) 26 | 27 | return t 28 | } 29 | 30 | // mixins.CodeEditor overrides 31 | func (t *CodeEditor) Paint(c gxui.Canvas) { 32 | t.CodeEditor.Paint(c) 33 | 34 | if t.HasFocus() { 35 | r := t.Size().Rect() 36 | c.DrawRoundedRect(r, 3, 3, 3, 3, t.theme.FocusedStyle.Pen, t.theme.FocusedStyle.Brush) 37 | } 38 | } 39 | 40 | func (t *CodeEditor) CreateSuggestionList() gxui.List { 41 | l := t.theme.CreateList() 42 | l.SetBackgroundBrush(t.theme.CodeSuggestionListStyle.Brush) 43 | l.SetBorderPen(t.theme.CodeSuggestionListStyle.Pen) 44 | return l 45 | } 46 | -------------------------------------------------------------------------------- /themes/basic/colors.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package basic 6 | 7 | import ( 8 | "github.com/google/gxui" 9 | ) 10 | 11 | var Blue30 = gxui.Color{R: 0.0, G: 0.0, B: 0.3, A: 1.0} 12 | -------------------------------------------------------------------------------- /themes/basic/drop_down_list.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package basic 6 | 7 | import ( 8 | "github.com/google/gxui" 9 | "github.com/google/gxui/math" 10 | "github.com/google/gxui/mixins" 11 | ) 12 | 13 | type DropDownList struct { 14 | mixins.DropDownList 15 | theme *Theme 16 | } 17 | 18 | func CreateDropDownList(theme *Theme) gxui.DropDownList { 19 | l := &DropDownList{} 20 | l.Init(l, theme) 21 | l.OnGainedFocus(l.Redraw) 22 | l.OnLostFocus(l.Redraw) 23 | l.List().OnAttach(l.Redraw) 24 | l.List().OnDetach(l.Redraw) 25 | l.OnMouseEnter(func(gxui.MouseEvent) { 26 | l.SetBorderPen(theme.DropDownListOverStyle.Pen) 27 | }) 28 | l.OnMouseExit(func(gxui.MouseEvent) { 29 | l.SetBorderPen(theme.DropDownListDefaultStyle.Pen) 30 | }) 31 | l.SetPadding(math.CreateSpacing(2)) 32 | l.SetBorderPen(theme.DropDownListDefaultStyle.Pen) 33 | l.SetBackgroundBrush(theme.DropDownListDefaultStyle.Brush) 34 | l.theme = theme 35 | return l 36 | } 37 | 38 | // mixin.List overrides 39 | func (l *DropDownList) Paint(c gxui.Canvas) { 40 | l.DropDownList.Paint(c) 41 | if l.HasFocus() || l.ListShowing() { 42 | r := l.Size().Rect().ContractI(1) 43 | c.DrawRoundedRect(r, 3.0, 3.0, 3.0, 3.0, l.theme.FocusedStyle.Pen, l.theme.FocusedStyle.Brush) 44 | } 45 | } 46 | 47 | func (l *DropDownList) DrawSelection(c gxui.Canvas, r math.Rect) { 48 | c.DrawRoundedRect(r, 2.0, 2.0, 2.0, 2.0, l.theme.HighlightStyle.Pen, l.theme.HighlightStyle.Brush) 49 | } 50 | -------------------------------------------------------------------------------- /themes/basic/image.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package basic 6 | 7 | import ( 8 | "github.com/google/gxui" 9 | "github.com/google/gxui/mixins" 10 | ) 11 | 12 | func CreateImage(theme *Theme) gxui.Image { 13 | i := &mixins.Image{} 14 | i.Init(i, theme) 15 | return i 16 | } 17 | -------------------------------------------------------------------------------- /themes/basic/label.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package basic 6 | 7 | import ( 8 | "github.com/google/gxui" 9 | "github.com/google/gxui/math" 10 | "github.com/google/gxui/mixins" 11 | ) 12 | 13 | func CreateLabel(theme *Theme) gxui.Label { 14 | l := &mixins.Label{} 15 | l.Init(l, theme, theme.DefaultFont(), theme.LabelStyle.FontColor) 16 | l.SetMargin(math.Spacing{L: 3, T: 3, R: 3, B: 3}) 17 | return l 18 | } 19 | -------------------------------------------------------------------------------- /themes/basic/linear_layout.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package basic 6 | 7 | import ( 8 | "github.com/google/gxui" 9 | "github.com/google/gxui/mixins" 10 | ) 11 | 12 | func CreateLinearLayout(theme *Theme) gxui.LinearLayout { 13 | l := &mixins.LinearLayout{} 14 | l.Init(l, theme) 15 | return l 16 | } 17 | -------------------------------------------------------------------------------- /themes/basic/list.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package basic 6 | 7 | import ( 8 | "github.com/google/gxui" 9 | "github.com/google/gxui/math" 10 | "github.com/google/gxui/mixins" 11 | ) 12 | 13 | type List struct { 14 | mixins.List 15 | theme *Theme 16 | } 17 | 18 | func CreateList(theme *Theme) gxui.List { 19 | l := &List{} 20 | l.Init(l, theme) 21 | l.OnGainedFocus(l.Redraw) 22 | l.OnLostFocus(l.Redraw) 23 | l.SetPadding(math.CreateSpacing(2)) 24 | l.SetBorderPen(gxui.TransparentPen) 25 | l.theme = theme 26 | return l 27 | } 28 | 29 | // mixin.List overrides 30 | func (l *List) Paint(c gxui.Canvas) { 31 | l.List.Paint(c) 32 | if l.HasFocus() { 33 | r := l.Size().Rect().ContractI(1) 34 | c.DrawRoundedRect(r, 3.0, 3.0, 3.0, 3.0, l.theme.FocusedStyle.Pen, l.theme.FocusedStyle.Brush) 35 | } 36 | } 37 | 38 | func (l *List) PaintSelection(c gxui.Canvas, r math.Rect) { 39 | c.DrawRoundedRect(r, 2.0, 2.0, 2.0, 2.0, l.theme.HighlightStyle.Pen, l.theme.HighlightStyle.Brush) 40 | } 41 | 42 | func (l *List) PaintMouseOverBackground(c gxui.Canvas, r math.Rect) { 43 | c.DrawRoundedRect(r, 2.0, 2.0, 2.0, 2.0, gxui.TransparentPen, gxui.CreateBrush(gxui.Gray15)) 44 | } 45 | -------------------------------------------------------------------------------- /themes/basic/panel_holder.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package basic 6 | 7 | import ( 8 | "github.com/google/gxui" 9 | "github.com/google/gxui/math" 10 | "github.com/google/gxui/mixins" 11 | ) 12 | 13 | type PanelHolder struct { 14 | mixins.PanelHolder 15 | theme *Theme 16 | } 17 | 18 | func CreatePanelHolder(theme *Theme) gxui.PanelHolder { 19 | p := &PanelHolder{} 20 | p.PanelHolder.Init(p, theme) 21 | p.theme = theme 22 | p.SetMargin(math.Spacing{L: 0, T: 2, R: 0, B: 0}) 23 | return p 24 | } 25 | 26 | func (p *PanelHolder) CreatePanelTab() mixins.PanelTab { 27 | return CreatePanelTab(p.theme) 28 | } 29 | 30 | func (p *PanelHolder) Paint(c gxui.Canvas) { 31 | panel := p.SelectedPanel() 32 | if panel != nil { 33 | bounds := p.Children().Find(panel).Bounds() 34 | c.DrawRoundedRect(bounds, 0.0, 0.0, 3.0, 3.0, p.theme.PanelBackgroundStyle.Pen, p.theme.PanelBackgroundStyle.Brush) 35 | } 36 | p.PanelHolder.Paint(c) 37 | } 38 | -------------------------------------------------------------------------------- /themes/basic/panel_tab.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package basic 6 | 7 | import ( 8 | "github.com/google/gxui" 9 | "github.com/google/gxui/math" 10 | "github.com/google/gxui/mixins" 11 | ) 12 | 13 | type PanelTab struct { 14 | mixins.Button 15 | theme *Theme 16 | active bool 17 | } 18 | 19 | func CreatePanelTab(theme *Theme) mixins.PanelTab { 20 | t := &PanelTab{} 21 | t.Button.Init(t, theme) 22 | t.theme = theme 23 | t.SetPadding(math.Spacing{L: 5, T: 3, R: 5, B: 3}) 24 | t.OnMouseEnter(func(gxui.MouseEvent) { t.Redraw() }) 25 | t.OnMouseExit(func(gxui.MouseEvent) { t.Redraw() }) 26 | t.OnMouseDown(func(gxui.MouseEvent) { t.Redraw() }) 27 | t.OnMouseUp(func(gxui.MouseEvent) { t.Redraw() }) 28 | t.OnGainedFocus(t.Redraw) 29 | t.OnLostFocus(t.Redraw) 30 | return t 31 | } 32 | 33 | func (t *PanelTab) SetActive(active bool) { 34 | t.active = active 35 | t.Redraw() 36 | } 37 | 38 | func (t *PanelTab) Paint(c gxui.Canvas) { 39 | s := t.Size() 40 | var style Style 41 | switch { 42 | case t.IsMouseDown(gxui.MouseButtonLeft) && t.IsMouseOver(): 43 | style = t.theme.TabPressedStyle 44 | case t.IsMouseOver(): 45 | style = t.theme.TabOverStyle 46 | default: 47 | style = t.theme.TabDefaultStyle 48 | } 49 | if l := t.Label(); l != nil { 50 | l.SetColor(style.FontColor) 51 | } 52 | 53 | c.DrawRoundedRect(s.Rect(), 5.0, 5.0, 0.0, 0.0, style.Pen, style.Brush) 54 | 55 | if t.HasFocus() { 56 | style = t.theme.FocusedStyle 57 | r := math.CreateRect(1, 1, s.W-1, s.H-1) 58 | c.DrawRoundedRect(r, 4.0, 4.0, 0.0, 0.0, style.Pen, style.Brush) 59 | } 60 | 61 | if t.active { 62 | style = t.theme.TabActiveHighlightStyle 63 | r := math.CreateRect(1, s.H-1, s.W-1, s.H) 64 | c.DrawRect(r, style.Brush) 65 | } 66 | 67 | t.Button.Paint(c) 68 | } 69 | -------------------------------------------------------------------------------- /themes/basic/progress_bar.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package basic 6 | 7 | import ( 8 | "time" 9 | 10 | "github.com/google/gxui" 11 | "github.com/google/gxui/math" 12 | "github.com/google/gxui/mixins" 13 | ) 14 | 15 | type ProgressBar struct { 16 | mixins.ProgressBar 17 | theme *Theme 18 | ticker *time.Ticker 19 | chevrons gxui.Canvas 20 | chevronWidth int 21 | scroll int 22 | } 23 | 24 | func CreateProgressBar(theme *Theme) gxui.ProgressBar { 25 | b := &ProgressBar{} 26 | b.Init(b, theme) 27 | b.theme = theme 28 | b.chevronWidth = 10 29 | 30 | b.OnAttach(func() { 31 | driver := theme.Driver() 32 | b.ticker = time.NewTicker(time.Millisecond * 50) 33 | go func() { 34 | for _ = range b.ticker.C { 35 | if !driver.Call(b.animationTick) { 36 | return 37 | } 38 | } 39 | }() 40 | }) 41 | 42 | b.OnDetach(func() { 43 | if b.chevrons != nil { 44 | b.chevrons = nil 45 | b.ticker.Stop() 46 | b.ticker = nil 47 | } 48 | }) 49 | b.SetBackgroundBrush(gxui.CreateBrush(gxui.Gray10)) 50 | b.SetBorderPen(gxui.CreatePen(1, gxui.Gray40)) 51 | return b 52 | } 53 | 54 | func (b *ProgressBar) animationTick() { 55 | if b.Attached() { 56 | b.scroll = (b.scroll + 1) % (b.chevronWidth * 2) 57 | b.Redraw() 58 | } 59 | } 60 | 61 | func (b *ProgressBar) SetSize(size math.Size) { 62 | b.ProgressBar.SetSize(size) 63 | 64 | b.chevrons = nil 65 | if size.Area() > 0 { 66 | b.chevrons = b.theme.Driver().CreateCanvas(size) 67 | b.chevronWidth = size.H / 2 68 | cw := b.chevronWidth 69 | for x := -cw * 2; x < size.W; x += cw * 2 { 70 | // x0 x2 71 | // | x1 | x3 72 | // | | 73 | // A-----B - y0 74 | // \ \ 75 | // \ \ 76 | // F C - y1 77 | // / / 78 | // / / 79 | // E-----D - y2 80 | y0, y1, y2 := 0, size.H/2, size.H 81 | x0, x1 := x, x+cw/2 82 | x2, x3 := x0+cw, x1+cw 83 | var chevron = gxui.Polygon{ 84 | /* A */ gxui.PolygonVertex{Position: math.Point{X: x0, Y: y0}}, 85 | /* B */ gxui.PolygonVertex{Position: math.Point{X: x2, Y: y0}}, 86 | /* C */ gxui.PolygonVertex{Position: math.Point{X: x3, Y: y1}}, 87 | /* D */ gxui.PolygonVertex{Position: math.Point{X: x2, Y: y2}}, 88 | /* E */ gxui.PolygonVertex{Position: math.Point{X: x0, Y: y2}}, 89 | /* F */ gxui.PolygonVertex{Position: math.Point{X: x1, Y: y1}}, 90 | } 91 | b.chevrons.DrawPolygon(chevron, gxui.TransparentPen, gxui.CreateBrush(gxui.Gray30)) 92 | } 93 | b.chevrons.Complete() 94 | } 95 | } 96 | 97 | func (b *ProgressBar) PaintProgress(c gxui.Canvas, r math.Rect, frac float32) { 98 | r.Max.X = math.Lerp(r.Min.X, r.Max.X, frac) 99 | c.DrawRect(r, gxui.CreateBrush(gxui.Gray50)) 100 | c.Push() 101 | c.AddClip(r) 102 | c.DrawCanvas(b.chevrons, math.Point{X: b.scroll}) 103 | c.Pop() 104 | } 105 | -------------------------------------------------------------------------------- /themes/basic/scroll_bar.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package basic 6 | 7 | import ( 8 | "github.com/google/gxui" 9 | "github.com/google/gxui/mixins" 10 | ) 11 | 12 | type ScrollBar struct { 13 | mixins.ScrollBar 14 | } 15 | 16 | func CreateScrollBar(theme *Theme) gxui.ScrollBar { 17 | s := &ScrollBar{} 18 | s.ScrollBar.Init(s, theme) 19 | s.SetBarBrush(theme.ScrollBarBarDefaultStyle.Brush) 20 | s.SetBarPen(theme.ScrollBarBarDefaultStyle.Pen) 21 | s.SetRailBrush(theme.ScrollBarRailDefaultStyle.Brush) 22 | s.SetRailPen(theme.ScrollBarRailDefaultStyle.Pen) 23 | updateColors := func() { 24 | switch { 25 | case s.IsMouseOver(): 26 | s.SetBarBrush(theme.ScrollBarBarOverStyle.Brush) 27 | s.SetBarPen(theme.ScrollBarBarOverStyle.Pen) 28 | s.SetRailBrush(theme.ScrollBarRailOverStyle.Brush) 29 | s.SetRailPen(theme.ScrollBarRailOverStyle.Pen) 30 | default: 31 | s.SetBarBrush(theme.ScrollBarBarDefaultStyle.Brush) 32 | s.SetBarPen(theme.ScrollBarBarDefaultStyle.Pen) 33 | s.SetRailBrush(theme.ScrollBarRailDefaultStyle.Brush) 34 | s.SetRailPen(theme.ScrollBarRailDefaultStyle.Pen) 35 | } 36 | s.Redraw() 37 | } 38 | s.OnMouseEnter(func(gxui.MouseEvent) { updateColors() }) 39 | s.OnMouseExit(func(gxui.MouseEvent) { updateColors() }) 40 | return s 41 | } 42 | -------------------------------------------------------------------------------- /themes/basic/scroll_layout.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package basic 6 | 7 | import ( 8 | "github.com/google/gxui" 9 | "github.com/google/gxui/mixins" 10 | ) 11 | 12 | func CreateScrollLayout(theme *Theme) gxui.ScrollLayout { 13 | l := &mixins.ScrollLayout{} 14 | l.Init(l, theme) 15 | return l 16 | } 17 | -------------------------------------------------------------------------------- /themes/basic/splitter_layout.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package basic 6 | 7 | import ( 8 | "github.com/google/gxui" 9 | "github.com/google/gxui/math" 10 | "github.com/google/gxui/mixins" 11 | ) 12 | 13 | type SplitterLayout struct { 14 | mixins.SplitterLayout 15 | theme *Theme 16 | } 17 | 18 | func CreateSplitterLayout(theme *Theme) gxui.SplitterLayout { 19 | l := &SplitterLayout{} 20 | l.theme = theme 21 | l.Init(l, theme) 22 | return l 23 | } 24 | 25 | // mixins.SplitterLayout overrides 26 | func (l *SplitterLayout) CreateSplitterBar() gxui.Control { 27 | b := &mixins.SplitterBar{} 28 | b.Init(b, l.theme) 29 | b.SetBackgroundColor(l.theme.SplitterBarDefaultStyle.Brush.Color) 30 | b.SetForegroundColor(l.theme.SplitterBarDefaultStyle.Pen.Color) 31 | b.OnSplitterDragged(func(wndPnt math.Point) { l.SplitterDragged(b, wndPnt) }) 32 | updateForegroundColor := func() { 33 | switch { 34 | case b.IsDragging(): 35 | b.SetForegroundColor(l.theme.HighlightStyle.Pen.Color) 36 | case b.IsMouseOver(): 37 | b.SetForegroundColor(l.theme.SplitterBarOverStyle.Pen.Color) 38 | default: 39 | b.SetForegroundColor(l.theme.SplitterBarDefaultStyle.Pen.Color) 40 | } 41 | b.Redraw() 42 | } 43 | b.OnDragStart(func(gxui.MouseEvent) { updateForegroundColor() }) 44 | b.OnDragEnd(func(gxui.MouseEvent) { updateForegroundColor() }) 45 | b.OnDragStart(func(gxui.MouseEvent) { updateForegroundColor() }) 46 | b.OnMouseEnter(func(gxui.MouseEvent) { updateForegroundColor() }) 47 | b.OnMouseExit(func(gxui.MouseEvent) { updateForegroundColor() }) 48 | return b 49 | } 50 | -------------------------------------------------------------------------------- /themes/basic/style.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package basic 6 | 7 | import ( 8 | "github.com/google/gxui" 9 | ) 10 | 11 | type Style struct { 12 | FontColor gxui.Color 13 | Brush gxui.Brush 14 | Pen gxui.Pen 15 | } 16 | 17 | func CreateStyle(fontColor, brushColor, penColor gxui.Color, penWidth float32) Style { 18 | return Style{ 19 | FontColor: fontColor, 20 | Pen: gxui.CreatePen(penWidth, penColor), 21 | Brush: gxui.CreateBrush(brushColor), 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /themes/basic/table_layout.go: -------------------------------------------------------------------------------- 1 | package basic 2 | 3 | import ( 4 | "github.com/google/gxui" 5 | "github.com/google/gxui/mixins" 6 | ) 7 | 8 | func CreateTableLayout(theme *Theme) gxui.TableLayout { 9 | l := &mixins.TableLayout{} 10 | l.Init(l, theme) 11 | return l 12 | } 13 | -------------------------------------------------------------------------------- /themes/basic/textbox.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package basic 6 | 7 | import ( 8 | "github.com/google/gxui" 9 | "github.com/google/gxui/math" 10 | "github.com/google/gxui/mixins" 11 | ) 12 | 13 | type TextBox struct { 14 | mixins.TextBox 15 | theme *Theme 16 | } 17 | 18 | func CreateTextBox(theme *Theme) gxui.TextBox { 19 | t := &TextBox{} 20 | t.Init(t, theme.Driver(), theme, theme.DefaultFont()) 21 | t.SetTextColor(theme.TextBoxDefaultStyle.FontColor) 22 | t.SetMargin(math.Spacing{L: 3, T: 3, R: 3, B: 3}) 23 | t.SetPadding(math.Spacing{L: 3, T: 3, R: 3, B: 3}) 24 | t.SetBackgroundBrush(theme.TextBoxDefaultStyle.Brush) 25 | t.SetBorderPen(theme.TextBoxDefaultStyle.Pen) 26 | t.OnMouseEnter(func(gxui.MouseEvent) { 27 | t.SetBackgroundBrush(theme.TextBoxOverStyle.Brush) 28 | t.SetBorderPen(theme.TextBoxOverStyle.Pen) 29 | }) 30 | t.OnMouseExit(func(gxui.MouseEvent) { 31 | t.SetBackgroundBrush(theme.TextBoxDefaultStyle.Brush) 32 | t.SetBorderPen(theme.TextBoxDefaultStyle.Pen) 33 | }) 34 | 35 | t.theme = theme 36 | 37 | return t 38 | } 39 | 40 | // mixins.TextBox overrides 41 | func (t *TextBox) Paint(c gxui.Canvas) { 42 | t.TextBox.Paint(c) 43 | 44 | if t.HasFocus() { 45 | r := t.Size().Rect() 46 | s := t.theme.FocusedStyle 47 | c.DrawRoundedRect(r, 3, 3, 3, 3, s.Pen, s.Brush) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /themes/basic/window.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package basic 6 | 7 | import ( 8 | "github.com/google/gxui" 9 | "github.com/google/gxui/mixins" 10 | ) 11 | 12 | type Window struct { 13 | mixins.Window 14 | } 15 | 16 | func CreateWindow(theme *Theme, width, height int, title string) gxui.Window { 17 | w := &Window{} 18 | w.Window.Init(w, theme.Driver(), width, height, title) 19 | w.SetBackgroundBrush(gxui.CreateBrush(theme.WindowBackground)) 20 | return w 21 | } 22 | -------------------------------------------------------------------------------- /tooltip_controller.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gxui 6 | 7 | import ( 8 | "github.com/google/gxui/math" 9 | "time" 10 | ) 11 | 12 | type ToolTipCreator func(math.Point) Control 13 | 14 | type toolTipTracker struct { 15 | creator ToolTipCreator 16 | control Control 17 | onEnterES EventSubscription 18 | onExitES EventSubscription 19 | onMoveES EventSubscription 20 | lastPosition math.Point 21 | } 22 | 23 | type ToolTipController struct { 24 | driver Driver 25 | timer *time.Timer 26 | bubbleOverlay BubbleOverlay 27 | trackers []*toolTipTracker 28 | showing *toolTipTracker 29 | } 30 | 31 | func (c *ToolTipController) beginTimer(tracker *toolTipTracker, timeout time.Duration) { 32 | if c.timer != nil { 33 | c.timer.Stop() 34 | c.timer = nil 35 | } 36 | if timeout > 0 { 37 | c.timer = time.AfterFunc(timeout, func() { 38 | c.driver.Call(func() { c.showToolTipForTracker(tracker) }) 39 | }) 40 | } else { 41 | c.showToolTipForTracker(tracker) 42 | } 43 | } 44 | 45 | func (c *ToolTipController) showToolTipForTracker(tracker *toolTipTracker) { 46 | toolTip := tracker.creator(tracker.lastPosition) 47 | if toolTip != nil { 48 | at := TransformCoordinate(tracker.lastPosition, tracker.control, c.bubbleOverlay) 49 | c.ShowToolTip(toolTip, at) 50 | c.showing = tracker 51 | } else { 52 | c.hideToolTipForTracker(tracker) 53 | } 54 | } 55 | 56 | func (c *ToolTipController) hideToolTipForTracker(tracker *toolTipTracker) { 57 | if c.showing == tracker { 58 | c.bubbleOverlay.Hide() 59 | c.showing = nil 60 | } 61 | } 62 | 63 | func CreateToolTipController(bubbleOverlay BubbleOverlay, driver Driver) *ToolTipController { 64 | return &ToolTipController{ 65 | driver: driver, 66 | bubbleOverlay: bubbleOverlay, 67 | } 68 | } 69 | 70 | func (c *ToolTipController) AddToolTip(control Control, delaySeconds float32, creator ToolTipCreator) { 71 | tracker := &toolTipTracker{ 72 | control: control, 73 | creator: creator, 74 | } 75 | duration := time.Duration(delaySeconds * float32(time.Second)) 76 | bind := func() { 77 | tracker.onEnterES = control.OnMouseEnter(func(ev MouseEvent) { 78 | tracker.lastPosition = ev.Point 79 | c.beginTimer(tracker, duration) 80 | }) 81 | tracker.onExitES = control.OnMouseExit(func(ev MouseEvent) { 82 | if c.timer != nil { 83 | c.timer.Stop() 84 | c.timer = nil 85 | } 86 | c.hideToolTipForTracker(tracker) 87 | }) 88 | tracker.onMoveES = control.OnMouseMove(func(ev MouseEvent) { 89 | tracker.lastPosition = ev.Point 90 | c.beginTimer(tracker, duration) 91 | }) 92 | } 93 | control.OnAttach(bind) 94 | control.OnDetach(func() { 95 | if c.timer != nil { 96 | c.timer.Stop() 97 | c.timer = nil 98 | } 99 | tracker.onEnterES.Unlisten() 100 | tracker.onExitES.Unlisten() 101 | tracker.onMoveES.Unlisten() 102 | }) 103 | if control.Attached() { 104 | bind() 105 | } 106 | } 107 | 108 | func (c *ToolTipController) ShowToolTip(toolTip Control, at math.Point) { 109 | c.bubbleOverlay.Show(toolTip, at) 110 | } 111 | -------------------------------------------------------------------------------- /tree.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gxui 6 | 7 | import "github.com/google/gxui/math" 8 | 9 | // Tree is the interface of all controls that visualize a hierarchical tree 10 | // structure of items. 11 | type Tree interface { 12 | Focusable 13 | 14 | // SetAdapter binds the specified TreeAdapter to this Tree control, replacing 15 | // any previously bound adapter. 16 | SetAdapter(TreeAdapter) 17 | 18 | // TreeAdapter returns the currently bound adapter. 19 | Adapter() TreeAdapter 20 | 21 | // Show makes the specified item visible, expanding the tree if necessary. 22 | Show(AdapterItem) 23 | 24 | // ExpandAll expands all tree nodes. 25 | ExpandAll() 26 | 27 | // CollapseAll collapses all tree nodes. 28 | CollapseAll() 29 | 30 | // Selected returns the currently selected item. 31 | Selected() AdapterItem 32 | 33 | // Select makes the specified item selected. The tree will not automatically 34 | // expand to the newly selected item. If the Tree does not contain the 35 | // specified item, then Select returns false and the previous selection 36 | // remains unaltered. 37 | Select(AdapterItem) bool 38 | 39 | // OnSelectionChanged registers the function f to be called when the selection 40 | // changes. 41 | OnSelectionChanged(f func(AdapterItem)) EventSubscription 42 | } 43 | 44 | // TreeNodeContainer is the interface used by nodes that can hold sub-nodes in the tree. 45 | type TreeNodeContainer interface { 46 | // Count returns the number of immediate child nodes. 47 | Count() int 48 | 49 | // Node returns the i'th child TreeNode. 50 | NodeAt(i int) TreeNode 51 | 52 | // ItemIndex returns the index of the child equal to item, or the index of the 53 | // child that indirectly contains item, or if the item is not found under this 54 | // node, -1. 55 | ItemIndex(item AdapterItem) int 56 | } 57 | 58 | // TreeNode is the interface used by nodes in the tree. 59 | type TreeNode interface { 60 | TreeNodeContainer 61 | 62 | // Item returns the AdapterItem this node. 63 | // It is important for the TreeNode to return consistent AdapterItems for 64 | // the same data, so that selections can be persisted, or re-ordering 65 | // animations can be played when the dataset changes. 66 | // The AdapterItem returned must be equality-unique across the entire Adapter. 67 | Item() AdapterItem 68 | 69 | // Create returns a Control visualizing this node. 70 | Create(theme Theme) Control 71 | } 72 | 73 | // TreeAdapter is an interface used to visualize a set of hierarchical items. 74 | // Users of the TreeAdapter should presume the data is unchanged until the 75 | // OnDataChanged or OnDataReplaced events are fired. 76 | type TreeAdapter interface { 77 | TreeNodeContainer 78 | 79 | // Size returns the size that each of the item's controls will be displayed 80 | // at for the given theme. 81 | Size(Theme) math.Size 82 | 83 | // OnDataChanged registers f to be called when there is a partial change in 84 | // the items of the adapter. Scroll positions and selections should be 85 | // preserved if possible. 86 | // If recreateControls is true then each of the visible controls should be 87 | // recreated by re-calling Create(). 88 | OnDataChanged(f func(recreateControls bool)) EventSubscription 89 | 90 | // OnDataReplaced registers f to be called when there is a complete 91 | // replacement of items in the adapter. 92 | OnDataReplaced(f func()) EventSubscription 93 | } 94 | -------------------------------------------------------------------------------- /while_attached.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gxui 6 | 7 | import ( 8 | "fmt" 9 | "reflect" 10 | ) 11 | 12 | // WhileAttached binds the function callback to event for the duration of c 13 | // being attached. 14 | // event can either be: 15 | // • A event of the signature: event(callback) 16 | // • A function of the signature: func(callback) EventSubscription 17 | func WhileAttached(c Control, event, callback interface{}) { 18 | if err := verifyWhileAttachedSignature(event, callback); err != nil { 19 | panic(err) 20 | } 21 | var s EventSubscription 22 | bind := func() { 23 | if e, ok := event.(Event); ok { 24 | s = e.Listen(callback) 25 | } else { 26 | params := []reflect.Value{reflect.ValueOf(callback)} 27 | res := reflect.ValueOf(event).Call(params)[0] 28 | s = res.Interface().(EventSubscription) 29 | } 30 | } 31 | if c.Attached() { 32 | bind() 33 | } 34 | c.OnAttach(bind) 35 | c.OnDetach(func() { s.Unlisten() }) 36 | } 37 | 38 | func verifyWhileAttachedSignature(event, callback interface{}) error { 39 | if _, ok := event.(Event); ok { 40 | return nil // Leave validation up to Event 41 | } 42 | e, c := reflect.TypeOf(event), reflect.TypeOf(callback) 43 | if e.Kind() != reflect.Func { 44 | return fmt.Errorf("event must be of type Event or func, got type %T", event) 45 | } 46 | if c.Kind() != reflect.Func { 47 | return fmt.Errorf("callback must be of type func, got type %T", callback) 48 | } 49 | if c := e.NumIn(); c != 1 { 50 | return fmt.Errorf("event as func must only take 1 parameter, got %d", c) 51 | } 52 | if got := e.In(0); got != c { 53 | return fmt.Errorf("event as func must only take 1 parameter of type callback, got type %s, callback type: %s", 54 | got, c) 55 | } 56 | if c := e.NumOut(); c != 1 { 57 | return fmt.Errorf("event as func must only return 1 value, got %d", c) 58 | } 59 | if got, expected := e.Out(0), reflect.TypeOf((*EventSubscription)(nil)).Elem(); got != expected { 60 | return fmt.Errorf("event as func must only return 1 value of type %s, got type %s", 61 | expected, got) 62 | } 63 | return nil 64 | } 65 | -------------------------------------------------------------------------------- /window.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gxui 6 | 7 | import ( 8 | "github.com/google/gxui/math" 9 | ) 10 | 11 | type Window interface { 12 | Container 13 | 14 | // Title returns the title of the window. 15 | // This is usually the text displayed at the top of the window. 16 | Title() string 17 | 18 | // SetTitle changes the title of the window. 19 | SetTitle(string) 20 | 21 | // Scale returns the display scaling for this window. 22 | // A scale of 1 is unscaled, 2 is twice the regular scaling. 23 | Scale() float32 24 | 25 | // SetScale alters the display scaling for this window. 26 | // A scale of 1 is unscaled, 2 is twice the regular scaling. 27 | SetScale(float32) 28 | 29 | // Position returns position of the window. 30 | Position() math.Point 31 | 32 | // SetPosition changes position of the window. 33 | SetPosition(math.Point) 34 | 35 | // Fullscreen returns true if the window is currently full-screen. 36 | Fullscreen() bool 37 | 38 | // SetFullscreen makes the window either full-screen or windowed. 39 | SetFullscreen(bool) 40 | 41 | // Show makes the window visible. 42 | Show() 43 | 44 | // Hide makes the window invisible. 45 | Hide() 46 | 47 | // Close destroys the window. 48 | // Once the window is closed, no further calls should be made to it. 49 | Close() 50 | 51 | // Focus returns the control currently with focus. 52 | Focus() Focusable 53 | 54 | // SetFocus gives the specified control Focus, returning true on success or 55 | // false if the control cannot be given focus. 56 | SetFocus(Control) bool 57 | 58 | // BackgroundBrush returns the brush used to draw the window background. 59 | BackgroundBrush() Brush 60 | 61 | // SetBackgroundBrush sets the brush used to draw the window background. 62 | SetBackgroundBrush(Brush) 63 | 64 | // BorderPen returns the pen used to draw the window border. 65 | BorderPen() Pen 66 | 67 | // SetBorderPen sets the pen used to draw the window border. 68 | SetBorderPen(Pen) 69 | 70 | Click(MouseEvent) 71 | DoubleClick(MouseEvent) 72 | KeyPress(KeyboardEvent) 73 | KeyStroke(KeyStrokeEvent) 74 | 75 | // Events 76 | OnClose(func()) EventSubscription 77 | OnResize(func()) EventSubscription 78 | OnClick(func(MouseEvent)) EventSubscription 79 | OnDoubleClick(func(MouseEvent)) EventSubscription 80 | OnMouseMove(func(MouseEvent)) EventSubscription 81 | OnMouseEnter(func(MouseEvent)) EventSubscription 82 | OnMouseExit(func(MouseEvent)) EventSubscription 83 | OnMouseDown(func(MouseEvent)) EventSubscription 84 | OnMouseUp(func(MouseEvent)) EventSubscription 85 | OnMouseScroll(func(MouseEvent)) EventSubscription 86 | OnKeyDown(func(KeyboardEvent)) EventSubscription 87 | OnKeyUp(func(KeyboardEvent)) EventSubscription 88 | OnKeyRepeat(func(KeyboardEvent)) EventSubscription 89 | OnKeyStroke(func(KeyStrokeEvent)) EventSubscription 90 | } 91 | --------------------------------------------------------------------------------