Half-done implementation of file elements

This commit is contained in:
Sasha Koshka 2023-04-18 18:37:50 -04:00
parent 7cdc5868e5
commit ac58a43220
7 changed files with 237 additions and 490 deletions

114
elements/directory.go Normal file
View File

@ -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
}

View File

@ -1,4 +1,4 @@
package fileElements package elements
import "time" import "time"
import "io/fs" import "io/fs"
@ -6,27 +6,29 @@ import "image"
import "git.tebibyte.media/sashakoshka/tomo" import "git.tebibyte.media/sashakoshka/tomo"
import "git.tebibyte.media/sashakoshka/tomo/input" import "git.tebibyte.media/sashakoshka/tomo/input"
import "git.tebibyte.media/sashakoshka/tomo/artist" 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/theme"
import "git.tebibyte.media/sashakoshka/tomo/default/config" 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 displays an interactive visual representation of a file within any
// file system. // file system.
type File struct { type File struct {
*core.Core entity fileEntity
*core.FocusableCore
core core.CoreControl
focusableControl core.FocusableCoreControl
config config.Wrapped config config.Wrapped
theme theme.Wrapped theme theme.Wrapped
lastClick time.Time lastClick time.Time
pressed bool pressed bool
enabled bool
iconID tomo.Icon iconID tomo.Icon
filesystem fs.StatFS filesystem fs.StatFS
location string location string
selected bool
onChoose func () onChoose func ()
} }
@ -40,15 +42,44 @@ func NewFile (
element *File, element *File,
err error, err error,
) { ) {
element = &File { } element = &File { enabled: true }
element.theme.Case = tomo.C("files", "file") element.theme.Case = tomo.C("files", "file")
element.Core, element.core = core.NewCore(element, element.drawAll) element.entity = tomo.NewEntity(element).(fileEntity)
element.FocusableCore,
element.focusableControl = core.NewFocusableCore(element.core, element.drawAndPush)
err = element.SetLocation(location, within) err = element.SetLocation(location, within)
return 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. // Location returns the file's location and filesystem.
func (element *File) Location () (string, fs.StatFS) { func (element *File) Location () (string, fs.StatFS) {
return element.location, element.filesystem return element.location, element.filesystem
@ -82,55 +113,66 @@ func (element *File) Update () error {
} }
element.updateMinimumSize() element.updateMinimumSize()
element.drawAndPush() element.entity.Invalidate()
return err 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) { func (element *File) HandleKeyDown (key input.Key, modifiers input.Modifiers) {
if !element.Enabled() { return } if !element.Enabled() { return }
if key == input.KeyEnter { if key == input.KeyEnter {
element.pressed = true element.pressed = true
element.drawAndPush() element.entity.Invalidate()
} }
} }
func (element *File) HandleKeyUp(key input.Key, modifiers input.Modifiers) { func (element *File) HandleKeyUp(key input.Key, modifiers input.Modifiers) {
if key == input.KeyEnter && element.pressed { if key == input.KeyEnter && element.pressed {
element.pressed = false element.pressed = false
element.drawAndPush()
if !element.Enabled() { return } if !element.Enabled() { return }
element.entity.Invalidate()
if element.onChoose != nil { if element.onChoose != nil {
element.onChoose() element.onChoose()
} }
} }
} }
func (element *File) HandleFocusChange () {
element.entity.Invalidate()
}
func (element *File) OnChoose (callback func ()) { func (element *File) OnChoose (callback func ()) {
element.onChoose = callback 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) { func (element *File) HandleMouseDown (x, y int, button input.Button) {
if !element.Enabled() { return } if !element.Enabled() { return }
if !element.Focused() { element.Focus() } if !element.entity.Focused() { element.Focus() }
if button != input.ButtonLeft { return } if button != input.ButtonLeft { return }
element.pressed = true element.pressed = true
element.drawAndPush() element.entity.Invalidate()
} }
func (element *File) HandleMouseUp (x, y int, button input.Button) { func (element *File) HandleMouseUp (x, y int, button input.Button) {
if button != input.ButtonLeft { return } if button != input.ButtonLeft { return }
element.pressed = false 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 time.Since(element.lastClick) < element.config.DoubleClickDelay() {
if element.Enabled() && within && element.onChoose != nil { if element.Enabled() && within && element.onChoose != nil {
element.onChoose() element.onChoose()
@ -138,29 +180,29 @@ func (element *File) HandleMouseUp (x, y int, button input.Button) {
} else { } else {
element.lastClick = time.Now() element.lastClick = time.Now()
} }
element.drawAndPush() element.entity.Invalidate()
} }
// SetTheme sets the element's theme. // SetTheme sets the element's theme.
func (element *File) SetTheme (new tomo.Theme) { func (element *File) SetTheme (theme tomo.Theme) {
if new == element.theme.Theme { return } if theme == element.theme.Theme { return }
element.theme.Theme = new element.theme.Theme = theme
element.drawAndPush() element.entity.Invalidate()
} }
// SetConfig sets the element's configuration. // SetConfig sets the element's configuration.
func (element *File) SetConfig (new tomo.Config) { func (element *File) SetConfig (config tomo.Config) {
if new == element.config.Config { return } if config == element.config.Config { return }
element.config.Config = new element.config.Config = config
element.drawAndPush() element.entity.Invalidate()
} }
func (element *File) state () tomo.State { func (element *File) state () tomo.State {
return tomo.State { return tomo.State {
Disabled: !element.Enabled(), Disabled: !element.Enabled(),
Focused: element.Focused(), Focused: element.entity.Focused(),
Pressed: element.pressed, Pressed: element.pressed,
On: element.selected, On: element.entity.Selected(),
} }
} }
@ -172,44 +214,11 @@ func (element *File) updateMinimumSize () {
padding := element.theme.Padding(tomo.PatternButton) padding := element.theme.Padding(tomo.PatternButton)
icon := element.icon() icon := element.icon()
if icon == nil { if icon == nil {
element.core.SetMinimumSize ( element.entity.SetMinimumSize (
padding.Horizontal(), padding.Horizontal(),
padding.Vertical()) padding.Vertical())
} else { } else {
bounds := padding.Inverse().Apply(icon.Bounds()) bounds := padding.Inverse().Apply(icon.Bounds())
element.core.SetMinimumSize(bounds.Dx(), bounds.Dy()) element.entity.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))
} }
} }

View File

@ -1,4 +1,4 @@
package fileElements package elements
import "os" import "os"
import "io/fs" import "io/fs"

View File

@ -7,6 +7,15 @@ import "git.tebibyte.media/sashakoshka/tomo/canvas"
import "git.tebibyte.media/sashakoshka/tomo/artist" import "git.tebibyte.media/sashakoshka/tomo/artist"
import "git.tebibyte.media/sashakoshka/tomo/default/theme" 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 { type listEntity interface {
tomo.ContainerEntity tomo.ContainerEntity
tomo.ScrollableEntity tomo.ScrollableEntity
@ -36,10 +45,7 @@ func NewList (columns int, children ...tomo.Element) (element *List) {
element.columnSizes = make([]int, columns) element.columnSizes = make([]int, columns)
element.theme.Case = tomo.C("tomo", "list") element.theme.Case = tomo.C("tomo", "list")
element.entity = tomo.NewEntity(element).(listEntity) element.entity = tomo.NewEntity(element).(listEntity)
element.Adopt(children...)
for _, child := range children {
element.Adopt(child)
}
return return
} }
@ -117,23 +123,27 @@ func (element *List) Layout () {
} }
} }
func (element *List) Adopt (child tomo.Element) { func (element *List) Adopt (children ...tomo.Element) {
element.entity.Adopt(child) for _, child := range children {
element.scratch[child] = scratchEntry { } element.entity.Adopt(child)
element.scratch[child] = scratchEntry { }
}
element.updateMinimumSize() element.updateMinimumSize()
element.entity.Invalidate() element.entity.Invalidate()
element.entity.InvalidateLayout() element.entity.InvalidateLayout()
} }
func (element *List) Disown (child tomo.Element) { func (element *List) Disown (children ...tomo.Element) {
index := element.entity.IndexOf(child) for _, child := range children {
if index < 0 { return } index := element.entity.IndexOf(child)
if index == element.selected { if index < 0 { return }
element.selected = -1 if index == element.selected {
element.entity.SelectChild(index, false) 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.updateMinimumSize()
element.entity.Invalidate() element.entity.Invalidate()
element.entity.InvalidateLayout() element.entity.InvalidateLayout()

View File

@ -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())
}

View File

@ -3,10 +3,7 @@ package main
import "os" import "os"
import "path/filepath" import "path/filepath"
import "git.tebibyte.media/sashakoshka/tomo" 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"
import "git.tebibyte.media/sashakoshka/tomo/elements/file"
import "git.tebibyte.media/sashakoshka/tomo/elements/containers"
import _ "git.tebibyte.media/sashakoshka/tomo/backends/all" import _ "git.tebibyte.media/sashakoshka/tomo/backends/all"
func main () { func main () {
@ -16,11 +13,11 @@ func main () {
func run () { func run () {
window, _ := tomo.NewWindow(tomo.Bounds(0, 0, 384, 384)) window, _ := tomo.NewWindow(tomo.Bounds(0, 0, 384, 384))
window.SetTitle("File browser") window.SetTitle("File browser")
container := containers.NewContainer(layouts.Vertical { true, true }) container := elements.NewVBox(elements.SpaceBoth)
window.Adopt(container) window.Adopt(container)
homeDir, _ := os.UserHomeDir() homeDir, _ := os.UserHomeDir()
controlBar := containers.NewContainer(layouts.Horizontal { }) controlBar := elements.NewHBox(elements.SpaceNone)
backButton := elements.NewButton("Back") backButton := elements.NewButton("Back")
backButton.SetIcon(tomo.IconBackward) backButton.SetIcon(tomo.IconBackward)
backButton.ShowText(false) backButton.ShowText(false)
@ -35,12 +32,11 @@ func run () {
upwardButton.ShowText(false) upwardButton.ShowText(false)
locationInput := elements.NewTextBox("Location", "") locationInput := elements.NewTextBox("Location", "")
statusBar := containers.NewContainer(layouts.Horizontal { true, false }) statusBar := elements.NewHBox(elements.SpaceMargin)
directory, _ := fileElements.NewFile(homeDir, nil) directory, _ := elements.NewFile(homeDir, nil)
baseName := elements.NewLabel(filepath.Base(homeDir), false) baseName := elements.NewLabel(filepath.Base(homeDir))
scrollContainer := containers.NewScrollContainer(false, true) directoryView, _ := elements.NewDirectory(homeDir, nil)
directoryView, _ := fileElements.NewDirectory(homeDir, nil)
updateStatus := func () { updateStatus := func () {
filePath, _ := directoryView.Location() filePath, _ := directoryView.Location()
directory.SetLocation(filePath, nil) directory.SetLocation(filePath, nil)
@ -72,19 +68,15 @@ func run () {
filePath, _ := directoryView.Location() filePath, _ := directoryView.Location()
choose(filepath.Dir(filePath)) choose(filepath.Dir(filePath))
}) })
controlBar.Adopt(backButton, forwardButton, refreshButton, upwardButton)
controlBar.AdoptExpand(locationInput)
statusBar.Adopt(directory, baseName)
controlBar.Adopt(backButton, false) container.Adopt(controlBar)
controlBar.Adopt(forwardButton, false) container.AdoptExpand (
controlBar.Adopt(refreshButton, false) elements.NewScroll(elements.ScrollVertical, directoryView))
controlBar.Adopt(upwardButton, false) container.Adopt(statusBar)
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)
window.OnClose(tomo.Stop) window.OnClose(tomo.Stop)
window.Show() window.Show()

View File

@ -197,8 +197,8 @@ const (
IconBackward IconBackward
IconForward IconForward
IconRefresh
IconUpward IconUpward
IconRefresh
IconYes IconYes
IconNo IconNo