From ac58a432207a7b76c050af885678b18a23bbb3fb Mon Sep 17 00:00:00 2001 From: Sasha Koshka Date: Tue, 18 Apr 2023 18:37:50 -0400 Subject: [PATCH] Half-done implementation of file elements --- elements/directory.go | 114 ++++++++++ elements/{notdone => }/file.go | 155 +++++++------- elements/{notdone => }/fs.go | 2 +- elements/list.go | 40 ++-- elements/notdone/directory.go | 378 --------------------------------- examples/fileBrowser/main.go | 36 ++-- theme.go | 2 +- 7 files changed, 237 insertions(+), 490 deletions(-) create mode 100644 elements/directory.go rename elements/{notdone => }/file.go (67%) rename elements/{notdone => }/fs.go (95%) delete mode 100644 elements/notdone/directory.go diff --git a/elements/directory.go b/elements/directory.go new file mode 100644 index 0000000..f5a1221 --- /dev/null +++ b/elements/directory.go @@ -0,0 +1,114 @@ +package elements + +import "image" +import "path/filepath" +import "git.tebibyte.media/sashakoshka/tomo" + +// TODO: base on flow implementation of list. also be able to switch to a table +// variant for a more information dense view. + +type historyEntry struct { + location string + filesystem ReadDirStatFS +} + +// Directory displays a list of files within a particular directory and +// file system. +type Directory struct { + *List + history []historyEntry + historyIndex int + onChoose func (file string) +} + +// NewDirectory creates a new directory view. If within is nil, it will use +// the OS file system. +func NewDirectory ( + location string, + within ReadDirStatFS, +) ( + element *Directory, + err error, +) { + element = &Directory { + List: NewList(8), + } + err = element.SetLocation(location, within) + return +} + +// Location returns the directory's location and filesystem. +func (element *Directory) Location () (string, ReadDirStatFS) { + if len(element.history) < 1 { return "", nil } + current := element.history[element.historyIndex] + return current.location, current.filesystem +} + +// SetLocation sets the directory's location and filesystem. If within is nil, +// it will use the OS file system. +func (element *Directory) SetLocation ( + location string, + within ReadDirStatFS, +) error { + if within == nil { + within = defaultFS { } + } + element.scroll = image.Point { } + + if element.history != nil { + element.historyIndex ++ + } + element.history = append ( + element.history[:element.historyIndex], + historyEntry { location, within }) + return element.Update() +} + +// Backward goes back a directory in history +func (element *Directory) Backward () (bool, error) { + if element.historyIndex > 1 { + element.historyIndex -- + return true, element.Update() + } else { + return false, nil + } +} + +// Forward goes forward a directory in history +func (element *Directory) Forward () (bool, error) { + if element.historyIndex < len(element.history) - 1 { + element.historyIndex ++ + return true, element.Update() + } else { + return false, nil + } +} + +// Update refreshes the directory's contents. +func (element *Directory) Update () error { + location, filesystem := element.Location() + entries, err := filesystem.ReadDir(location) + + children := make([]tomo.Element, len(entries)) + for index, entry := range entries { + filePath := filepath.Join(location, entry.Name()) + file, _ := NewFile(filePath, filesystem) + file.OnChoose (func () { + if element.onChoose != nil { + element.onChoose(filePath) + } + }) + + children[index] = file + } + + element.DisownAll() + element.Adopt(children...) + return err +} + +// OnChoose sets a function to be called when the user double-clicks a file or +// sub-directory within the directory view. +func (element *Directory) OnChoose (callback func (file string)) { + element.onChoose = callback +} diff --git a/elements/notdone/file.go b/elements/file.go similarity index 67% rename from elements/notdone/file.go rename to elements/file.go index 62e6e4d..6a758ce 100644 --- a/elements/notdone/file.go +++ b/elements/file.go @@ -1,4 +1,4 @@ -package fileElements +package elements import "time" import "io/fs" @@ -6,27 +6,29 @@ import "image" import "git.tebibyte.media/sashakoshka/tomo" import "git.tebibyte.media/sashakoshka/tomo/input" import "git.tebibyte.media/sashakoshka/tomo/artist" -import "git.tebibyte.media/sashakoshka/tomo/elements/core" +import "git.tebibyte.media/sashakoshka/tomo/canvas" import "git.tebibyte.media/sashakoshka/tomo/default/theme" import "git.tebibyte.media/sashakoshka/tomo/default/config" +type fileEntity interface { + tomo.SelectableEntity + tomo.FocusableEntity +} + // File displays an interactive visual representation of a file within any // file system. type File struct { - *core.Core - *core.FocusableCore - core core.CoreControl - focusableControl core.FocusableCoreControl + entity fileEntity config config.Wrapped theme theme.Wrapped lastClick time.Time pressed bool + enabled bool iconID tomo.Icon filesystem fs.StatFS location string - selected bool onChoose func () } @@ -40,15 +42,44 @@ func NewFile ( element *File, err error, ) { - element = &File { } + element = &File { enabled: true } element.theme.Case = tomo.C("files", "file") - element.Core, element.core = core.NewCore(element, element.drawAll) - element.FocusableCore, - element.focusableControl = core.NewFocusableCore(element.core, element.drawAndPush) + element.entity = tomo.NewEntity(element).(fileEntity) err = element.SetLocation(location, within) return } +// Entity returns this element's entity. +func (element *File) Entity () tomo.Entity { + return element.entity +} + +// Draw causes the element to draw to the specified destination canvas. +func (element *File) Draw (destination canvas.Canvas) { + // background + state := element.state() + bounds := element.entity.Bounds() + sink := element.theme.Sink(tomo.PatternButton) + element.theme. + Pattern(tomo.PatternButton, state). + Draw(destination, bounds) + + // icon + icon := element.icon() + if icon != nil { + iconBounds := icon.Bounds() + offset := image.Pt ( + (bounds.Dx() - iconBounds.Dx()) / 2, + (bounds.Dy() - iconBounds.Dy()) / 2) + if element.pressed { + offset = offset.Add(sink) + } + icon.Draw ( + destination, + element.theme.Color(tomo.ColorForeground, state), + bounds.Min.Add(offset)) + } +} // Location returns the file's location and filesystem. func (element *File) Location () (string, fs.StatFS) { return element.location, element.filesystem @@ -82,55 +113,66 @@ func (element *File) Update () error { } element.updateMinimumSize() - element.drawAndPush() + element.entity.Invalidate() return err } -func (element *File) Selected () bool { - return element.selected -} - -func (element *File) SetSelected (selected bool) { - if element.selected == selected { return } - element.selected = selected - element.drawAndPush() -} - func (element *File) HandleKeyDown (key input.Key, modifiers input.Modifiers) { if !element.Enabled() { return } if key == input.KeyEnter { element.pressed = true - element.drawAndPush() + element.entity.Invalidate() } } func (element *File) HandleKeyUp(key input.Key, modifiers input.Modifiers) { if key == input.KeyEnter && element.pressed { element.pressed = false - element.drawAndPush() if !element.Enabled() { return } + element.entity.Invalidate() if element.onChoose != nil { element.onChoose() } } } +func (element *File) HandleFocusChange () { + element.entity.Invalidate() +} + func (element *File) OnChoose (callback func ()) { element.onChoose = callback } +// Focus gives this element input focus. +func (element *File) Focus () { + if !element.entity.Focused() { element.entity.Focus() } +} + +// Enabled returns whether this file is enabled or not. +func (element *File) Enabled () bool { + return element.enabled +} + +// SetEnabled sets whether this file is enabled or not. +func (element *File) SetEnabled (enabled bool) { + if element.enabled == enabled { return } + element.enabled = enabled + element.entity.Invalidate() +} + func (element *File) HandleMouseDown (x, y int, button input.Button) { if !element.Enabled() { return } - if !element.Focused() { element.Focus() } + if !element.entity.Focused() { element.Focus() } if button != input.ButtonLeft { return } element.pressed = true - element.drawAndPush() + element.entity.Invalidate() } func (element *File) HandleMouseUp (x, y int, button input.Button) { if button != input.ButtonLeft { return } element.pressed = false - within := image.Point { x, y }.In(element.Bounds()) + within := image.Point { x, y }.In(element.entity.Bounds()) if time.Since(element.lastClick) < element.config.DoubleClickDelay() { if element.Enabled() && within && element.onChoose != nil { element.onChoose() @@ -138,29 +180,29 @@ func (element *File) HandleMouseUp (x, y int, button input.Button) { } else { element.lastClick = time.Now() } - element.drawAndPush() + element.entity.Invalidate() } // SetTheme sets the element's theme. -func (element *File) SetTheme (new tomo.Theme) { - if new == element.theme.Theme { return } - element.theme.Theme = new - element.drawAndPush() +func (element *File) SetTheme (theme tomo.Theme) { + if theme == element.theme.Theme { return } + element.theme.Theme = theme + element.entity.Invalidate() } // SetConfig sets the element's configuration. -func (element *File) SetConfig (new tomo.Config) { - if new == element.config.Config { return } - element.config.Config = new - element.drawAndPush() +func (element *File) SetConfig (config tomo.Config) { + if config == element.config.Config { return } + element.config.Config = config + element.entity.Invalidate() } func (element *File) state () tomo.State { return tomo.State { Disabled: !element.Enabled(), - Focused: element.Focused(), + Focused: element.entity.Focused(), Pressed: element.pressed, - On: element.selected, + On: element.entity.Selected(), } } @@ -172,44 +214,11 @@ func (element *File) updateMinimumSize () { padding := element.theme.Padding(tomo.PatternButton) icon := element.icon() if icon == nil { - element.core.SetMinimumSize ( + element.entity.SetMinimumSize ( padding.Horizontal(), padding.Vertical()) } else { bounds := padding.Inverse().Apply(icon.Bounds()) - element.core.SetMinimumSize(bounds.Dx(), bounds.Dy()) - } -} - -func (element *File) drawAndPush () { - if element.core.HasImage() { - element.drawAll() - element.core.DamageAll() - } -} - -func (element *File) drawAll () { - // background - state := element.state() - bounds := element.Bounds() - sink := element.theme.Sink(tomo.PatternButton) - element.theme. - Pattern(tomo.PatternButton, state). - Draw(element.core, bounds) - - // icon - icon := element.icon() - if icon != nil { - iconBounds := icon.Bounds() - offset := image.Pt ( - (bounds.Dx() - iconBounds.Dx()) / 2, - (bounds.Dy() - iconBounds.Dy()) / 2) - if element.pressed { - offset = offset.Add(sink) - } - icon.Draw ( - element.core, - element.theme.Color(tomo.ColorForeground, state), - bounds.Min.Add(offset)) + element.entity.SetMinimumSize(bounds.Dx(), bounds.Dy()) } } diff --git a/elements/notdone/fs.go b/elements/fs.go similarity index 95% rename from elements/notdone/fs.go rename to elements/fs.go index 85572e3..88d9965 100644 --- a/elements/notdone/fs.go +++ b/elements/fs.go @@ -1,4 +1,4 @@ -package fileElements +package elements import "os" import "io/fs" diff --git a/elements/list.go b/elements/list.go index 6926835..dcc9b19 100644 --- a/elements/list.go +++ b/elements/list.go @@ -7,6 +7,15 @@ import "git.tebibyte.media/sashakoshka/tomo/canvas" import "git.tebibyte.media/sashakoshka/tomo/artist" import "git.tebibyte.media/sashakoshka/tomo/default/theme" +// TODO: make hidden variants: +// vertical: one column. +// flow: acts like DocumentContainer with all inline elements. +// create wrapper elements for making a plain version of each of these, but keep +// the implementations private (but with public methods) so they can be included +// in other elements. +// have table be a very tabular thing with named columns that can be sorted, +// resized, etc. + type listEntity interface { tomo.ContainerEntity tomo.ScrollableEntity @@ -36,10 +45,7 @@ func NewList (columns int, children ...tomo.Element) (element *List) { element.columnSizes = make([]int, columns) element.theme.Case = tomo.C("tomo", "list") element.entity = tomo.NewEntity(element).(listEntity) - - for _, child := range children { - element.Adopt(child) - } + element.Adopt(children...) return } @@ -117,23 +123,27 @@ func (element *List) Layout () { } } -func (element *List) Adopt (child tomo.Element) { - element.entity.Adopt(child) - element.scratch[child] = scratchEntry { } +func (element *List) Adopt (children ...tomo.Element) { + for _, child := range children { + element.entity.Adopt(child) + element.scratch[child] = scratchEntry { } + } element.updateMinimumSize() element.entity.Invalidate() element.entity.InvalidateLayout() } -func (element *List) Disown (child tomo.Element) { - index := element.entity.IndexOf(child) - if index < 0 { return } - if index == element.selected { - element.selected = -1 - element.entity.SelectChild(index, false) +func (element *List) Disown (children ...tomo.Element) { + for _, child := range children { + index := element.entity.IndexOf(child) + if index < 0 { return } + if index == element.selected { + element.selected = -1 + element.entity.SelectChild(index, false) + } + element.entity.Disown(index) + delete(element.scratch, child) } - element.entity.Disown(index) - delete(element.scratch, child) element.updateMinimumSize() element.entity.Invalidate() element.entity.InvalidateLayout() diff --git a/elements/notdone/directory.go b/elements/notdone/directory.go deleted file mode 100644 index df352cf..0000000 --- a/elements/notdone/directory.go +++ /dev/null @@ -1,378 +0,0 @@ -package fileElements - -import "io/fs" -import "image" -import "path/filepath" -import "git.tebibyte.media/sashakoshka/tomo" -import "git.tebibyte.media/sashakoshka/tomo/input" -import "git.tebibyte.media/sashakoshka/tomo/artist" -import "git.tebibyte.media/sashakoshka/tomo/canvas" -import "git.tebibyte.media/sashakoshka/tomo/textdraw" -import "git.tebibyte.media/sashakoshka/tomo/elements/core" -import "git.tebibyte.media/sashakoshka/tomo/default/theme" -import "git.tebibyte.media/sashakoshka/tomo/default/config" - -type fileLayoutEntry struct { - *File - fs.DirEntry - Bounds image.Rectangle - Drawer textdraw.Drawer - TextPoint image.Point -} - -type historyEntry struct { - location string - filesystem ReadDirStatFS -} - -// Directory displays a list of files within a particular directory and -// file system. -type Directory struct { - *core.Core - *core.Propagator - core core.CoreControl - - children []fileLayoutEntry - scroll image.Point - contentBounds image.Rectangle - - config config.Wrapped - theme theme.Wrapped - - onScrollBoundsChange func () - - history []historyEntry - historyIndex int - onChoose func (file string) -} - -// NewDirectory creates a new directory view. If within is nil, it will use -// the OS file system. -func NewDirectory ( - location string, - within ReadDirStatFS, -) ( - element *Directory, - err error, -) { - element = &Directory { } - element.theme.Case = tomo.C("files", "directory") - element.Core, element.core = core.NewCore(element, element.redoAll) - element.Propagator = core.NewPropagator(element, element.core) - err = element.SetLocation(location, within) - return -} - -// Location returns the directory's location and filesystem. -func (element *Directory) Location () (string, ReadDirStatFS) { - if len(element.history) < 1 { return "", nil } - current := element.history[element.historyIndex] - return current.location, current.filesystem -} - -// SetLocation sets the directory's location and filesystem. If within is nil, -// it will use the OS file system. -func (element *Directory) SetLocation ( - location string, - within ReadDirStatFS, -) error { - if within == nil { - within = defaultFS { } - } - element.scroll = image.Point { } - - if element.history != nil { - element.historyIndex ++ - } - element.history = append ( - element.history[:element.historyIndex], - historyEntry { location, within }) - return element.Update() -} - -// Backward goes back a directory in history -func (element *Directory) Backward () (bool, error) { - if element.historyIndex > 1 { - element.historyIndex -- - return true, element.Update() - } else { - return false, nil - } -} - -// Forward goes forward a directory in history -func (element *Directory) Forward () (bool, error) { - if element.historyIndex < len(element.history) - 1 { - element.historyIndex ++ - return true, element.Update() - } else { - return false, nil - } -} - -// Update refreshes the directory's contents. -func (element *Directory) Update () error { - location, filesystem := element.Location() - entries, err := filesystem.ReadDir(location) - - // disown all entries - for _, file := range element.children { - file.DrawTo(nil, image.Rectangle { }, nil) - file.SetParent(nil) - - if file.Focused() { - file.HandleUnfocus() - } - } - - element.children = make([]fileLayoutEntry, len(entries)) - for index, entry := range entries { - filePath := filepath.Join(location, entry.Name()) - file, _ := NewFile(filePath, filesystem) - file.SetParent(element) - file.OnChoose (func () { - if element.onChoose != nil { - element.onChoose(filePath) - } - }) - element.children[index].File = file - element.children[index].DirEntry = entry - element.children[index].Drawer.SetFace (element.theme.FontFace( - tomo.FontStyleRegular, - tomo.FontSizeNormal)) - element.children[index].Drawer.SetText([]rune(entry.Name())) - element.children[index].Drawer.SetAlign(textdraw.AlignCenter) - } - - if element.core.HasImage() { - element.redoAll() - element.core.DamageAll() - } - return err -} - -// OnChoose sets a function to be called when the user double-clicks a file or -// sub-directory within the directory view. -func (element *Directory) OnChoose (callback func (file string)) { - element.onChoose = callback -} - -// CountChildren returns the amount of children contained within this element. -func (element *Directory) CountChildren () (count int) { - return len(element.children) -} - -// Child returns the child at the specified index. If the index is out of -// bounds, this method will return nil. -func (element *Directory) Child (index int) (child tomo.Element) { - if index < 0 || index > len(element.children) { return } - return element.children[index].File -} - -func (element *Directory) HandleMouseDown (x, y int, button input.Button) { - if button == input.ButtonLeft { - var file *File - for _, entry := range element.children { - if image.Pt(x, y).In(entry.Bounds) { - file = entry.File - } - } - if file != nil { - file.SetSelected(!file.Selected()) - } - } - element.Propagator.HandleMouseDown(x, y, button) -} - -func (element *Directory) redoAll () { - if !element.core.HasImage() { return } - - // do a layout - element.doLayout() - - maxScrollHeight := element.maxScrollHeight() - if element.scroll.Y > maxScrollHeight { - element.scroll.Y = maxScrollHeight - element.doLayout() - } - - // draw a background - rocks := make([]image.Rectangle, len(element.children)) - for index, entry := range element.children { - rocks[index] = entry.Bounds - } - pattern := element.theme.Pattern ( - tomo.PatternPinboard, - tomo.State { }) - artist.DrawShatter(element.core, pattern, element.Bounds(), rocks...) - - element.partition() - if parent, ok := element.core.Parent().(tomo.ScrollableParent); ok { - parent.NotifyScrollBoundsChange(element) - } - if element.onScrollBoundsChange != nil { - element.onScrollBoundsChange() - } - - // draw labels - foreground := element.theme.Color(tomo.ColorForeground, tomo.State { }) - for _, entry := range element.children { - entry.Drawer.Draw(element.core, foreground, entry.TextPoint) - } -} - -func (element *Directory) partition () { - for _, entry := range element.children { - entry.DrawTo(nil, entry.Bounds, nil) - } - - // cut our canvas up and give peices to child elements - for _, entry := range element.children { - if entry.Bounds.Overlaps(element.Bounds()) { - entry.DrawTo ( - canvas.Cut(element.core, entry.Bounds), - entry.Bounds, func (region image.Rectangle) { - element.core.DamageRegion(region) - }) - } - } -} - -func (element *Directory) Window () tomo.Window { - return element.core.Window() -} - -// NotifyMinimumSizeChange notifies the container that the minimum size of a -// child element has changed. -func (element *Directory) NotifyMinimumSizeChange (child tomo.Element) { - element.redoAll() - element.core.DamageAll() -} - -// SetTheme sets the element's theme. -func (element *Directory) SetTheme (new tomo.Theme) { - if new == element.theme.Theme { return } - element.theme.Theme = new - element.Propagator.SetTheme(new) - element.redoAll() -} - -// SetConfig sets the element's configuration. -func (element *Directory) SetConfig (new tomo.Config) { - if new == element.config.Config { return } - element.Propagator.SetConfig(new) - element.redoAll() -} -// ScrollContentBounds returns the full content size of the element. -func (element *Directory) ScrollContentBounds () image.Rectangle { - return element.contentBounds -} - -// ScrollViewportBounds returns the size and position of the element's -// viewport relative to ScrollBounds. -func (element *Directory) ScrollViewportBounds () image.Rectangle { - padding := element.theme.Padding(tomo.PatternPinboard) - bounds := padding.Apply(element.Bounds()) - bounds = bounds.Sub(bounds.Min).Add(element.scroll) - return bounds -} - -// ScrollTo scrolls the viewport to the specified point relative to -// ScrollBounds. -func (element *Directory) ScrollTo (position image.Point) { - if position.Y < 0 { - position.Y = 0 - } - maxScrollHeight := element.maxScrollHeight() - if position.Y > maxScrollHeight { - position.Y = maxScrollHeight - } - element.scroll = position - if element.core.HasImage() { - element.redoAll() - element.core.DamageAll() - } -} - -// OnScrollBoundsChange sets a function to be called when the element's viewport -// bounds, content bounds, or scroll axes change. -func (element *Directory) OnScrollBoundsChange (callback func ()) { - element.onScrollBoundsChange = callback -} - -// ScrollAxes returns the supported axes for scrolling. -func (element *Directory) ScrollAxes () (horizontal, vertical bool) { - return false, true -} - -func (element *Directory) maxScrollHeight () (height int) { - padding := element.theme.Padding(tomo.PatternSunken) - viewportHeight := element.Bounds().Dy() - padding.Vertical() - height = element.contentBounds.Dy() - viewportHeight - if height < 0 { height = 0 } - return -} - -func (element *Directory) doLayout () { - margin := element.theme.Margin(tomo.PatternPinboard) - padding := element.theme.Padding(tomo.PatternPinboard) - bounds := padding.Apply(element.Bounds()) - element.contentBounds = image.Rectangle { } - - beginningOfRow := true - dot := bounds.Min.Sub(element.scroll) - rowHeight := 0 - for index, entry := range element.children { - width, height := entry.MinimumSize() - - if dot.X + width > bounds.Max.X { - dot.X = bounds.Min.Sub(element.scroll).X - dot.Y += rowHeight - if index > 1 { - dot.Y += margin.Y - } - beginningOfRow = true - } - - if beginningOfRow { - beginningOfRow = false - } else { - dot.X += margin.X - } - - entry.Drawer.SetMaxWidth(width) - bounds := image.Rect(dot.X, dot.Y, dot.X + width, dot.Y + height) - entry.Bounds = bounds - - drawerHeight := entry.Drawer.ReccomendedHeightFor(width) - entry.TextPoint = - image.Pt(bounds.Min.X, bounds.Max.Y + margin.Y). - Sub(entry.Drawer.LayoutBounds().Min) - bounds.Max.Y += margin.Y + drawerHeight - height += margin.Y + drawerHeight - if rowHeight < height { - rowHeight = height - } - - element.contentBounds = element.contentBounds.Union(bounds) - element.children[index] = entry - dot.X += width - } - - element.contentBounds = - element.contentBounds.Sub(element.contentBounds.Min) -} - -func (element *Directory) updateMinimumSize () { - padding := element.theme.Padding(tomo.PatternPinboard) - minimumWidth := 0 - for _, entry := range element.children { - width, _ := entry.MinimumSize() - if width > minimumWidth { - minimumWidth = width - } - } - element.core.SetMinimumSize ( - minimumWidth + padding.Horizontal(), - padding.Vertical()) -} diff --git a/examples/fileBrowser/main.go b/examples/fileBrowser/main.go index 156aab1..842df94 100644 --- a/examples/fileBrowser/main.go +++ b/examples/fileBrowser/main.go @@ -3,10 +3,7 @@ package main import "os" import "path/filepath" import "git.tebibyte.media/sashakoshka/tomo" -import "git.tebibyte.media/sashakoshka/tomo/layouts" import "git.tebibyte.media/sashakoshka/tomo/elements" -import "git.tebibyte.media/sashakoshka/tomo/elements/file" -import "git.tebibyte.media/sashakoshka/tomo/elements/containers" import _ "git.tebibyte.media/sashakoshka/tomo/backends/all" func main () { @@ -16,11 +13,11 @@ func main () { func run () { window, _ := tomo.NewWindow(tomo.Bounds(0, 0, 384, 384)) window.SetTitle("File browser") - container := containers.NewContainer(layouts.Vertical { true, true }) + container := elements.NewVBox(elements.SpaceBoth) window.Adopt(container) homeDir, _ := os.UserHomeDir() - controlBar := containers.NewContainer(layouts.Horizontal { }) + controlBar := elements.NewHBox(elements.SpaceNone) backButton := elements.NewButton("Back") backButton.SetIcon(tomo.IconBackward) backButton.ShowText(false) @@ -35,12 +32,11 @@ func run () { upwardButton.ShowText(false) locationInput := elements.NewTextBox("Location", "") - statusBar := containers.NewContainer(layouts.Horizontal { true, false }) - directory, _ := fileElements.NewFile(homeDir, nil) - baseName := elements.NewLabel(filepath.Base(homeDir), false) + statusBar := elements.NewHBox(elements.SpaceMargin) + directory, _ := elements.NewFile(homeDir, nil) + baseName := elements.NewLabel(filepath.Base(homeDir)) - scrollContainer := containers.NewScrollContainer(false, true) - directoryView, _ := fileElements.NewDirectory(homeDir, nil) + directoryView, _ := elements.NewDirectory(homeDir, nil) updateStatus := func () { filePath, _ := directoryView.Location() directory.SetLocation(filePath, nil) @@ -72,19 +68,15 @@ func run () { filePath, _ := directoryView.Location() choose(filepath.Dir(filePath)) }) + + controlBar.Adopt(backButton, forwardButton, refreshButton, upwardButton) + controlBar.AdoptExpand(locationInput) + statusBar.Adopt(directory, baseName) - controlBar.Adopt(backButton, false) - controlBar.Adopt(forwardButton, false) - controlBar.Adopt(refreshButton, false) - controlBar.Adopt(upwardButton, false) - controlBar.Adopt(locationInput, true) - scrollContainer.Adopt(directoryView) - statusBar.Adopt(directory, false) - statusBar.Adopt(baseName, false) - - container.Adopt(controlBar, false) - container.Adopt(scrollContainer, true) - container.Adopt(statusBar, false) + container.Adopt(controlBar) + container.AdoptExpand ( + elements.NewScroll(elements.ScrollVertical, directoryView)) + container.Adopt(statusBar) window.OnClose(tomo.Stop) window.Show() diff --git a/theme.go b/theme.go index 73af8b8..15a651c 100644 --- a/theme.go +++ b/theme.go @@ -197,8 +197,8 @@ const ( IconBackward IconForward - IconRefresh IconUpward + IconRefresh IconYes IconNo