Go言語のGUIライブラリfyneでハマったことメモ

# 概要
Go言語のGUIライブラリである[fyne](https://github.com/fyne-io/fyne)を使っていて、いくらか戸惑う場面が多かったので、対処法をここにメモしておきます。

# ハマったこととその対処法
## ボタンの色の変更
fyneのライブラリには、ウィジェットとして[button](https://developer.fyne.io/widget/button)が用意されています。
この`button`ウィジェットは、ライブラリのソースコードを見ればわかる通り、見た目に関する情報はあまり持っていないようです。

https://github.com/fyne-io/fyne/blob/master/widget/button.go

```go:button.go
type Button struct {
	DisableableWidget
	Text string
	Icon fyne.Resource
	// Specify how prominent the button should be, High will highlight the button and Low will remove some decoration.
	//
	// Since: 1.4
	Importance    ButtonImportance
	Alignment     ButtonAlign
	IconPlacement ButtonIconPlacement

	OnTapped func() `json:"-"`

	hovered, focused bool
	tapAnim          *fyne.Animation
}
```
ボタン上に表示するテキストの情報やボタン自体をどのようなアラインメントで配置するかなどの情報はありますが、色や形などの情報は持っていません。
そこで、色を付けたい場合は、ボタンに色の付いた別のシートをかぶせて着色します。以下がサンプルです。

```go
// ボタンの作成
sampleButton := widget.NewButton("", nil)
// 色の付いたシートの作成
coloredSheet := canvas.NewRectangle(color.White)
// 上の二つを重ね合わせて表示
sampleContainer := container.New(layout.NewMaxLayout(), centerButton, centerButtonSheet, centerImg)
```
`MaxLayout`というレイアウトが、複数の画面部品を重ね合わせて表示することを利用して、ボタンに色を付けています。

## 画像を崩さずに表示
上記の方法で別の画面部品に画像を重ねて表示しようとすると、画像の縦横比がずれることがあります。
ライブラリのソースコード内の、画像を表す構造体を見てみると、この問題を解決する方法がありました。

https://github.com/fyne-io/fyne/blob/master/canvas/image.go

```go:image.go
type Image struct {
	baseObject

	// one of the following sources will provide our image data
	File     string        // Load the image from a file
	Resource fyne.Resource // Load the image from an in-memory resource
	Image    image.Image   // Specify a loaded image to use in this canvas object

	Translucency float64    // Set a translucency value > 0.0 to fade the image
	FillMode     ImageFill  // Specify how the image should expand to fill or fit the available space
	ScaleMode    ImageScale // Specify the type of scaling interpolation applied to the image

}

```

`FillMode`というフィールドの設定値で、縦横比の保持の設定ができます。
このフィールドには、具体的には以下の値が設定できます。

https://github.com/fyne-io/fyne/blob/master/canvas/image.go


```go:image.go
const (
	// ImageFillStretch will scale the image to match the Size() values.
	// This is the default and does not maintain aspect ratio.
	ImageFillStretch ImageFill = iota
	// ImageFillContain makes the image fit within the object Size(),
	// centrally and maintaining aspect ratio.
	// There may be transparent sections top and bottom or left and right.
	ImageFillContain //(Fit)
	// ImageFillOriginal ensures that the container grows to the pixel dimensions
	// required to fit the original image. The aspect of the image will be maintained so,
	// as with ImageFillContain there may be transparent areas around the image.
	// Note that the minSize may be smaller than the image dimensions if scale > 1.
	ImageFillOriginal
)
```

`ImageFillContain`を指定することで、画像を別の部品と重ね合わせて表示しても、縦横比が保持されます。
以下がサンプルです。

```go:sample.go
// 画像の読み込み
sampleImg := canvas.NewImageFromFile("sample.png")
// FillModeの設定
sampleImg.FillMode = canvas.ImageFillContain
// FillModeを設定しても、最低の大きさを指定しないと表示が崩れることも
sampleImg.SetMinSize(fyne.Size{Width: 30, Height: 30})
```

ちなみに、`Image`はfyneの画面部品で唯一(多分)最低の大きさを設定することができます(上のコードの`sampleImg.SetMinSize()`の部分)。
他のウィジェットやコンテナは、内容によって最低の大きさが動的に変わってしまい、レイアウトの設定が難しいです。

## ウィンドウを広げたときにウィジェットも引き延ばされるのを阻止
fyneで作る画面は、HTMLでフロントエンドを作成するときと違い、画面の各要素の大きさを固定で設定することはできません。
そのため、基本的にはユーザーがGUIのウィンドウを広げると、その中に配置されている要素もあわせて引き延ばされてしまいます。

これを阻止するための方法が、以下です。

```go:sample.go
// この2つのボタンを表示したい
sampleButtonOne :=  widget.NewButton("", nil)
sampleButtonTwo :=  widget.NewButton("", nil)

//このように表示すると、ウィンドウを広げたときに2つのボタンも引き延ばされてしまいます
buttonsContainer := container.New(layout.NewHBoxLayout(), sampleButtonOne, sampleButtonTwo) 

// Spacerで囲うと、ウィンドウが広げられてもボタンの大きさは一定です
buttonsContainer := container.New(layout.NewHBoxLayout(), layout.NewSpacer(), sampleButtonOne, sampleButtonTwo, layout.NewSpacer())
```
`Spacer`という、通常`BoxLayout`で用いる要素で大きさを変えたくない部品を囲うことで、それらの大きさは最低値で一定となります。