File list

As Shiny does not define a list widget, we will construct one using another vertical flow container. Each item within this list will be a custom cell widget that displays an icon on the left with the filename text left-aligned in the remaining space. First, we will update our makeList() function to add some dummy data. Each item is a new cell, created using makeCell() (which is defined later). The items are laid out as a list using widget.NewFlow() on the vertical axis:

func makeList(dir string) node.Node {
parent := makeCell(dir, nil)
cell1 := makeCell("Filename 1", loadImage("shiny-hall.jpg"))
cell2 := makeCell("Filename 2", loadImage("shiny-hall.jpg"))
cell3 := makeCell("Filename 3", loadImage("shiny-hall.jpg"))
return widget.NewFlow(widget.AxisVertical, parent, cell1, cell2, cell3)
}

As you can see, the first item in the list is the name of our directory, which needs a different icon. We can load a standard icon from the Shiny icon collection using the iconvg package, specifically, iconvg.Rasterizer and iconvg.Decode(). Using the following helper function, we can load the icons.FileFolder icon into an image so it can be drawn using the same functions as images we load from the filesystem:

func loadDirIcon() image.Image {
var raster iconvg.Rasterizer
bounds := image.Rect(0, 0, iconSize, iconSize)
icon := image.NewRGBA(bounds)
raster.SetDstImage(icon, bounds, draw.Over)

iconvg.Decode(&raster, icons.FileFolder, nil)
return icon
}

The last part of our layout code is the makeCell() function. In this case, it's a simple wrapper around the creation of a cell widget. When this function is passed a nil icon, it will set up the directory icon using the helper above. When an icon is passed, then it creates an onClick function that will load the image in the main view:

func makeCell(name string, icon image.Image) node.Node {
var onClick func()
if icon == nil {
icon = loadDirIcon()
} else {
onClick = func() {chooseImage(icon)}
}

return newCell(icon, name, onClick)
}

The details of our cell widget are very similar to the button we created earlier and so most of the code is omitted. The next excerpt shows its PaintBase() function, which draws the icon and text to screen. It calculates the ratio of an image so that it can be correctly painted within the cell. The text is then drawn like the button code, but with a space between it and the image we painted.

To make this work, a simple scaleImage() function is also needed, which uses draw.ApproxBiLinear to resize the graphic to fit with reasonable performance:


func (c *cell) PaintBase(ctx *node.PaintBaseContext, origin image.Point) error {
c.Marks.UnmarkNeedsPaintBase()
face := ctx.Theme.AcquireFontFace(theme.FontFaceOptions{})
defer ctx.Theme.ReleaseFontFace(theme.FontFaceOptions{}, face)

ratio := float32(c.icon.Bounds().Max.Y)/float32(c.icon.Bounds().Max.X)
if c.icon.Bounds().Max.Y > c.icon.Bounds().Max.X {
ratio = float32(c.icon.Bounds().Max.X)/float32(c.icon.Bounds().Max.Y)
}
scaled := scaleImage(c.icon, iconSize, int(float32(iconSize)*ratio))

draw.Draw(ctx.Dst, c.Rect.Add(origin), scaled, image.Point{}, draw.Over)
d := font.Drawer{
Dst: ctx.Dst,
Src: theme.Foreground.Uniform(ctx.Theme),
Face: face,
Dot: fixed.Point26_6{X: fixed.I(c.Rect.Min.X + origin.X + iconSize + space),
Y: fixed.I(c.Rect.Min.Y + origin.Y + face.Metrics().Ascent.Ceil())},
}
d.DrawString(c.label)

return nil
}

func scaleImage(src image.Image, width, height int) image.Image {
ret := image.NewRGBA(image.Rect(0, 0, width, height))

draw.ApproxBiLinear.Scale(ret, ret.Bounds(), src, src.Bounds(), draw.Src, nil)

return ret
}

All this code comes together to create a file listing with an image preview, as shown in the following screenshot:

The completed file list on the left with placeholder content
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.139.80.209