DirectoryView uses File to display files
This commit is contained in:
		
							parent
							
								
									dcc672e2bc
								
							
						
					
					
						commit
						68341517f7
					
				@ -219,7 +219,6 @@ func (element *DocumentContainer) NotifyFlexibleHeightChange (child elements.Fle
 | 
			
		||||
	element.core.DamageAll()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// SetTheme sets the element's theme.
 | 
			
		||||
func (element *DocumentContainer) SetTheme (new theme.Theme) {
 | 
			
		||||
	if new == element.theme.Theme { return }
 | 
			
		||||
 | 
			
		||||
@ -1,22 +1,44 @@
 | 
			
		||||
package fileElements
 | 
			
		||||
 | 
			
		||||
import "io/fs"
 | 
			
		||||
import "image"
 | 
			
		||||
import "path/filepath"
 | 
			
		||||
import "git.tebibyte.media/sashakoshka/tomo/elements/basic"
 | 
			
		||||
import "git.tebibyte.media/sashakoshka/tomo/theme"
 | 
			
		||||
import "git.tebibyte.media/sashakoshka/tomo/artist"
 | 
			
		||||
import "git.tebibyte.media/sashakoshka/tomo/canvas"
 | 
			
		||||
import "git.tebibyte.media/sashakoshka/tomo/config"
 | 
			
		||||
import "git.tebibyte.media/sashakoshka/tomo/elements"
 | 
			
		||||
import "git.tebibyte.media/sashakoshka/tomo/elements/core"
 | 
			
		||||
 | 
			
		||||
type ReadDirStatFS interface {
 | 
			
		||||
	fs.ReadDirFS
 | 
			
		||||
	fs.StatFS
 | 
			
		||||
type fileLayoutEntry struct {
 | 
			
		||||
	*File
 | 
			
		||||
	fs.DirEntry
 | 
			
		||||
	Bounds image.Rectangle
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DirectoryView displays a list of files within a particular directory and
 | 
			
		||||
// file system.
 | 
			
		||||
type DirectoryView struct {
 | 
			
		||||
	*basicElements.List
 | 
			
		||||
	*core.Core
 | 
			
		||||
	*core.Propagator
 | 
			
		||||
	core core.CoreControl
 | 
			
		||||
 | 
			
		||||
	children []fileLayoutEntry
 | 
			
		||||
	scroll   image.Point
 | 
			
		||||
	contentBounds image.Rectangle
 | 
			
		||||
	
 | 
			
		||||
	config config.Wrapped
 | 
			
		||||
	theme  theme.Wrapped
 | 
			
		||||
 | 
			
		||||
	onScrollBoundsChange func ()
 | 
			
		||||
 | 
			
		||||
	filesystem ReadDirStatFS
 | 
			
		||||
	location string
 | 
			
		||||
	onChoose func (file string)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewDirectoryView creates a new directory view. If within is nil, it will use
 | 
			
		||||
// the OS file system.
 | 
			
		||||
func NewDirectoryView (
 | 
			
		||||
	location string,
 | 
			
		||||
	within ReadDirStatFS,
 | 
			
		||||
@ -24,17 +46,21 @@ func NewDirectoryView (
 | 
			
		||||
	element *DirectoryView,
 | 
			
		||||
	err error,
 | 
			
		||||
) {
 | 
			
		||||
	element = &DirectoryView {
 | 
			
		||||
		List: basicElements.NewList(),
 | 
			
		||||
	}
 | 
			
		||||
	element = &DirectoryView { }
 | 
			
		||||
	element.theme.Case = theme.C("files", "directoryView")
 | 
			
		||||
	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 *DirectoryView) Location () (string, fs.ReadDirFS) {
 | 
			
		||||
	return element.location, element.filesystem
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetLocation sets the directory's location and filesystem. If within is nil,
 | 
			
		||||
// it will use the OS file system.
 | 
			
		||||
func (element *DirectoryView) SetLocation (
 | 
			
		||||
	location string,
 | 
			
		||||
	within ReadDirStatFS,
 | 
			
		||||
@ -42,32 +68,231 @@ func (element *DirectoryView) SetLocation (
 | 
			
		||||
	if within == nil {
 | 
			
		||||
		within = defaultFS { }
 | 
			
		||||
	}
 | 
			
		||||
	element.scroll = image.Point { }
 | 
			
		||||
	element.location   = location
 | 
			
		||||
	element.filesystem = within
 | 
			
		||||
	return element.Update()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Update refreshes the directory's contents.
 | 
			
		||||
func (element *DirectoryView) Update () error {
 | 
			
		||||
	defer element.redoAll()
 | 
			
		||||
	entries, err := element.filesystem.ReadDir(element.location)
 | 
			
		||||
 | 
			
		||||
	listEntries := make([]basicElements.ListEntry, len(entries))
 | 
			
		||||
	// 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(element.location, entry.Name())
 | 
			
		||||
		listEntries[index] = basicElements.NewListEntry (
 | 
			
		||||
			entry.Name(),
 | 
			
		||||
			func () {
 | 
			
		||||
				filePath := filePath
 | 
			
		||||
		file, err := NewFile (
 | 
			
		||||
			filePath,
 | 
			
		||||
			element.filesystem)
 | 
			
		||||
		if err != nil { return err }
 | 
			
		||||
		file.SetParent(element)
 | 
			
		||||
		element.children[index].File = file
 | 
			
		||||
		element.children[index].DirEntry = entry
 | 
			
		||||
		element.OnChoose (func (filepath string) {
 | 
			
		||||
			if element.onChoose != nil {
 | 
			
		||||
				element.onChoose(filePath)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
	element.Clear()
 | 
			
		||||
	element.Append(listEntries...)
 | 
			
		||||
	
 | 
			
		||||
	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 *DirectoryView) OnChoose (callback func (file string)) {
 | 
			
		||||
	element.onChoose = callback
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CountChildren returns the amount of children contained within this element.
 | 
			
		||||
func (element *DirectoryView) 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 *DirectoryView) Child (index int) (child elements.Element) {
 | 
			
		||||
	if index < 0 || index > len(element.children) { return }
 | 
			
		||||
	return element.children[index].File
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (element *DirectoryView) 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 (
 | 
			
		||||
		theme.PatternPinboard,
 | 
			
		||||
		theme.State { })
 | 
			
		||||
	artist.DrawShatter(element.core, pattern, element.Bounds(), rocks...)
 | 
			
		||||
 | 
			
		||||
	element.partition()
 | 
			
		||||
	if parent, ok := element.core.Parent().(elements.ScrollableParent); ok {
 | 
			
		||||
		parent.NotifyScrollBoundsChange(element)
 | 
			
		||||
	}
 | 
			
		||||
	if element.onScrollBoundsChange != nil {
 | 
			
		||||
		element.onScrollBoundsChange()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (element *DirectoryView) 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)
 | 
			
		||||
				})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NotifyMinimumSizeChange notifies the container that the minimum size of a
 | 
			
		||||
// child element has changed.
 | 
			
		||||
func (element *DirectoryView) NotifyMinimumSizeChange (child elements.Element) {
 | 
			
		||||
	element.redoAll()
 | 
			
		||||
	element.core.DamageAll()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetTheme sets the element's theme.
 | 
			
		||||
func (element *DirectoryView) SetTheme (new theme.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 *DirectoryView) SetConfig (new config.Config) {
 | 
			
		||||
	if new == element.config.Config { return }
 | 
			
		||||
	element.Propagator.SetConfig(new)
 | 
			
		||||
	element.redoAll()
 | 
			
		||||
}
 | 
			
		||||
// ScrollContentBounds returns the full content size of the element.
 | 
			
		||||
func (element *DirectoryView) ScrollContentBounds () image.Rectangle {
 | 
			
		||||
	return element.contentBounds
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ScrollViewportBounds returns the size and position of the element's
 | 
			
		||||
// viewport relative to ScrollBounds.
 | 
			
		||||
func (element *DirectoryView) ScrollViewportBounds () image.Rectangle {
 | 
			
		||||
	padding := element.theme.Padding(theme.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 *DirectoryView) 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 *DirectoryView) OnScrollBoundsChange (callback func ()) {
 | 
			
		||||
	element.onScrollBoundsChange = callback
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ScrollAxes returns the supported axes for scrolling.
 | 
			
		||||
func (element *DirectoryView) ScrollAxes () (horizontal, vertical bool) {
 | 
			
		||||
	return false, true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (element *DirectoryView) maxScrollHeight () (height int) {
 | 
			
		||||
	padding := element.theme.Padding(theme.PatternSunken)
 | 
			
		||||
	viewportHeight := element.Bounds().Dy() - padding.Vertical()
 | 
			
		||||
	height = element.contentBounds.Dy() - viewportHeight
 | 
			
		||||
	if height < 0 { height = 0 }
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (element *DirectoryView) doLayout () {
 | 
			
		||||
	margin := element.theme.Margin(theme.PatternPinboard)
 | 
			
		||||
	padding := element.theme.Padding(theme.PatternPinboard)
 | 
			
		||||
	bounds := padding.Apply(element.Bounds())
 | 
			
		||||
	element.contentBounds = image.Rectangle { }
 | 
			
		||||
 | 
			
		||||
	beginningOfRow := true
 | 
			
		||||
	dot := bounds.Min.Sub(element.scroll)
 | 
			
		||||
	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 += height
 | 
			
		||||
			if index > 1 {
 | 
			
		||||
				dot.Y += margin.Y
 | 
			
		||||
			}
 | 
			
		||||
			beginningOfRow = true
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		if beginningOfRow {
 | 
			
		||||
			beginningOfRow = false
 | 
			
		||||
		} else {
 | 
			
		||||
			dot.X += margin.X
 | 
			
		||||
		}
 | 
			
		||||
	
 | 
			
		||||
		entry.Bounds.Min = dot
 | 
			
		||||
		entry.Bounds.Max = image.Pt(dot.X + width, dot.Y + height)
 | 
			
		||||
		element.children[index] = entry
 | 
			
		||||
		element.contentBounds = element.contentBounds.Union(entry.Bounds)
 | 
			
		||||
		dot.X += width
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	element.contentBounds =
 | 
			
		||||
		element.contentBounds.Sub(element.contentBounds.Min)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (element *DirectoryView) updateMinimumSize () {
 | 
			
		||||
	padding := element.theme.Padding(theme.PatternPinboard)
 | 
			
		||||
	minimumWidth := 0
 | 
			
		||||
	for _, entry := range element.children {
 | 
			
		||||
		width, _ := entry.MinimumSize()
 | 
			
		||||
		if width > minimumWidth {
 | 
			
		||||
			minimumWidth = width
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	element.core.SetMinimumSize (
 | 
			
		||||
		minimumWidth + padding.Horizontal(),
 | 
			
		||||
		padding.Vertical())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,24 +1,31 @@
 | 
			
		||||
package fileElements
 | 
			
		||||
 | 
			
		||||
import "io/fs"
 | 
			
		||||
import "image"
 | 
			
		||||
import "git.tebibyte.media/sashakoshka/tomo/theme"
 | 
			
		||||
import "git.tebibyte.media/sashakoshka/tomo/elements/basic"
 | 
			
		||||
import "git.tebibyte.media/sashakoshka/tomo/artist"
 | 
			
		||||
import "git.tebibyte.media/sashakoshka/tomo/config"
 | 
			
		||||
import "git.tebibyte.media/sashakoshka/tomo/elements/core"
 | 
			
		||||
 | 
			
		||||
// File is a 
 | 
			
		||||
// File displays an interactive visual representation of a file within any
 | 
			
		||||
// file system.
 | 
			
		||||
type File struct {
 | 
			
		||||
	*basicElements.Icon
 | 
			
		||||
	*core.Core
 | 
			
		||||
	*core.FocusableCore
 | 
			
		||||
	core core.CoreControl
 | 
			
		||||
	focusableControl core.FocusableCoreControl
 | 
			
		||||
	
 | 
			
		||||
	// we inherit from Icon directly because it is not our responsibility
 | 
			
		||||
	// to draw text. this will be the responsibility of the directory that
 | 
			
		||||
	// contains the file. we don't handle mouse events on the file label
 | 
			
		||||
	// text either because when the user clicks on that we want to rename
 | 
			
		||||
	// the file.
 | 
			
		||||
	config config.Wrapped
 | 
			
		||||
	theme  theme.Wrapped
 | 
			
		||||
	
 | 
			
		||||
	iconID     theme.Icon
 | 
			
		||||
	filesystem fs.StatFS
 | 
			
		||||
	location   string
 | 
			
		||||
	onChoose   func ()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewFile creates a new file element. If within is nil, it will use the OS file
 | 
			
		||||
// system
 | 
			
		||||
func NewFile (
 | 
			
		||||
	location string,
 | 
			
		||||
	within fs.StatFS,
 | 
			
		||||
@ -26,17 +33,22 @@ func NewFile (
 | 
			
		||||
	element *File,
 | 
			
		||||
	err error,
 | 
			
		||||
) {
 | 
			
		||||
	element = &File {
 | 
			
		||||
		Icon: basicElements.NewIcon(theme.IconFile, theme.IconSizeLarge),
 | 
			
		||||
	}
 | 
			
		||||
	element = &File { }
 | 
			
		||||
	element.theme.Case = theme.C("files", "file")
 | 
			
		||||
	element.Core, element.core = core.NewCore(element, element.drawAll)
 | 
			
		||||
	element.FocusableCore,
 | 
			
		||||
	element.focusableControl = core.NewFocusableCore(element.core, element.drawAndPush)
 | 
			
		||||
	err = element.SetLocation(location, within)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Location returns the file's location and filesystem.
 | 
			
		||||
func (element *File) Location () (string, fs.StatFS) {
 | 
			
		||||
	return element.location, element.filesystem
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetLocation sets the file's location and filesystem. If within is nil, it
 | 
			
		||||
// will use the OS file system.
 | 
			
		||||
func (element *File) SetLocation (
 | 
			
		||||
	location string,
 | 
			
		||||
	within fs.StatFS,
 | 
			
		||||
@ -49,19 +61,79 @@ func (element *File) SetLocation (
 | 
			
		||||
	return element.Update()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Update refreshes the element to match the file it represents.
 | 
			
		||||
func (element *File) Update () error {
 | 
			
		||||
	element.iconID = theme.IconError
 | 
			
		||||
	info, err := element.filesystem.Stat(element.location)
 | 
			
		||||
	if err != nil { return err }
 | 
			
		||||
 | 
			
		||||
	// TODO: choose icon based on file mime type
 | 
			
		||||
	if info.IsDir() {
 | 
			
		||||
		element.SetIcon(theme.IconDirectory, theme.IconSizeLarge)
 | 
			
		||||
		element.iconID = theme.IconDirectory
 | 
			
		||||
	} else {
 | 
			
		||||
		element.SetIcon(theme.IconFile, theme.IconSizeLarge)
 | 
			
		||||
		element.iconID = theme.IconFile
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	element.updateMinimumSize()
 | 
			
		||||
	element.drawAndPush()
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (element *File) OnChoose (callback func ()) {
 | 
			
		||||
	element.onChoose = callback
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (element *File) state () theme.State {
 | 
			
		||||
	return theme.State {
 | 
			
		||||
		Disabled: !element.Enabled(),
 | 
			
		||||
		Focused:  element.Focused(),
 | 
			
		||||
		// Pressed:  element.pressed,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (element *File) icon () artist.Icon {
 | 
			
		||||
	return element.theme.Icon(element.iconID, theme.IconSizeLarge)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (element *File) updateMinimumSize () {
 | 
			
		||||
	padding := element.theme.Padding(theme.PatternButton)
 | 
			
		||||
	icon := element.icon()
 | 
			
		||||
	if icon == nil {
 | 
			
		||||
		element.core.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()
 | 
			
		||||
	element.theme.
 | 
			
		||||
		Pattern(theme.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)
 | 
			
		||||
		icon.Draw (
 | 
			
		||||
			element.core,
 | 
			
		||||
			element.theme.Color (
 | 
			
		||||
				theme.ColorForeground, state),
 | 
			
		||||
			bounds.Min.Add(offset))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -3,16 +3,23 @@ package fileElements
 | 
			
		||||
import "os"
 | 
			
		||||
import "io/fs"
 | 
			
		||||
 | 
			
		||||
// ReadDirStatFS is a combination of fs.ReadDirFS and fs.StatFS. It is the
 | 
			
		||||
// minimum filesystem needed to satisfy a directory view.
 | 
			
		||||
type ReadDirStatFS interface {
 | 
			
		||||
	fs.ReadDirFS
 | 
			
		||||
	fs.StatFS
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type defaultFS struct { }
 | 
			
		||||
 | 
			
		||||
func (defaultFS) Open (name string) (fs.File, error) {
 | 
			
		||||
	return os.Open(name)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (defaultFS) ReadDir (name string) ([]DirEntry, error) {
 | 
			
		||||
func (defaultFS) ReadDir (name string) ([]fs.DirEntry, error) {
 | 
			
		||||
	return os.ReadDir(name)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (defaultFS) Stat (name string) (FileInfo, error) {
 | 
			
		||||
func (defaultFS) Stat (name string) (fs.FileInfo, error) {
 | 
			
		||||
	return os.Stat(name)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,7 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import "os"
 | 
			
		||||
import "path/filepath"
 | 
			
		||||
import "git.tebibyte.media/sashakoshka/tomo"
 | 
			
		||||
import "git.tebibyte.media/sashakoshka/tomo/theme"
 | 
			
		||||
import "git.tebibyte.media/sashakoshka/tomo/layouts/basic"
 | 
			
		||||
@ -18,6 +19,7 @@ func run () {
 | 
			
		||||
	window.SetTitle("File browser")
 | 
			
		||||
	container := containers.NewContainer(basicLayouts.Vertical { true, true })
 | 
			
		||||
	window.Adopt(container)
 | 
			
		||||
	homeDir, _ := os.UserHomeDir()
 | 
			
		||||
 | 
			
		||||
	controlBar := containers.NewContainer(basicLayouts.Horizontal { })
 | 
			
		||||
	backButton := basicElements.NewButton("Back")
 | 
			
		||||
@ -34,13 +36,17 @@ func run () {
 | 
			
		||||
	upwardButton.ShowText(false)
 | 
			
		||||
	locationInput := basicElements.NewTextBox("Location", "")
 | 
			
		||||
	
 | 
			
		||||
	statusBar := containers.NewContainer(basicLayouts.Horizontal { true, false })
 | 
			
		||||
	directory, _ := fileElements.NewFile(homeDir, nil)
 | 
			
		||||
	baseName := basicElements.NewLabel(filepath.Base(homeDir), false)
 | 
			
		||||
	
 | 
			
		||||
	scrollContainer  := containers.NewScrollContainer(false, true)
 | 
			
		||||
	homeDir,_ := os.UserHomeDir()
 | 
			
		||||
	directoryView, _ := fileElements.NewDirectoryView(homeDir)
 | 
			
		||||
	directoryView.Collapse(0, 8)
 | 
			
		||||
	directoryView, _ := fileElements.NewDirectoryView(homeDir, nil)
 | 
			
		||||
	choose := func (filePath string) {
 | 
			
		||||
		directoryView.SetLocation(filePath)
 | 
			
		||||
		locationInput.SetValue(directoryView.Location())
 | 
			
		||||
		directoryView.SetLocation(filePath, nil)
 | 
			
		||||
		directory.SetLocation(filePath, nil)
 | 
			
		||||
		locationInput.SetValue(filePath)
 | 
			
		||||
		baseName.SetText(filepath.Base(filePath))
 | 
			
		||||
	}
 | 
			
		||||
	directoryView.OnChoose(choose)
 | 
			
		||||
	locationInput.OnEnter (func () {
 | 
			
		||||
@ -48,14 +54,18 @@ func run () {
 | 
			
		||||
	})
 | 
			
		||||
	choose(homeDir)
 | 
			
		||||
	
 | 
			
		||||
	scrollContainer.Adopt(directoryView)
 | 
			
		||||
	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)
 | 
			
		||||
 | 
			
		||||
	window.OnClose(tomo.Stop)
 | 
			
		||||
	window.Show()
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user