この章では、ToDoアプリを作ります。この章には、多くのことを詰め込みました。
ターミナルもしくはコマンドプロンプトを起動して、次のようにしてアプリケーション作成の下準備をします。 ディレクトリはどこでも構いませんが、Documents などに go か fyne などのディレクトリを作り、さらに次のようにディレクトリを作るのが良いでしょう。
mkdir ToDo
cd ToDo
go mod init example.com/ToDo
// 通常は、example.com の部分を、あなたが持っているURLにします。
// デフォルトでは、スラッシュ(/)の次の名前がアプリの名前になります。
次のファイルを記述します。main 関数を記述しているファイルは、 main.go とするのが一般的ですが、好きなファイル名にしても構いません。
package main
import (
"bufio"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
"os"
)
var data = []string{}
// Windows と macOS の場合は、次のようなパスを設定します。
const path string = "/Users/ユーザー名/Documentsなどのディレクトリ名/todo.txt"
/* Linux の場合は、次のようなパスを設定します。
const path string = "/home/ユーザー名/Documentsなどのディレクトリ名/todo.txt"
*/
func read(filename string) error {
file, _ := os.Open(filename)
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
data = append(data, line)
}
return nil
}
func save(filename string) error {
file, _ := os.Create(path)
defer file.Close()
for _, line := range data {
file.WriteString(line + "\n")
}
return nil
}
func main() {
read(path)
row := -1
app := app.New()
app.Settings().SetTheme(&myTheme{})
win := app.NewWindow("ToDo")
list := widget.NewList(
func() int {
return len(data)
},
func() fyne.CanvasObject {
return widget.NewLabel("template")
},
func(i widget.ListItemID, o fyne.CanvasObject) {
o.(*widget.Label).SetText(data[i])
},
)
list.OnSelected = func(id int) { row = id }
add := widget.NewButtonWithIcon("", theme.ContentAddIcon(),
func() {
entry := widget.NewEntry()
dialog.ShowForm("ToDoを追加", "追加", "キャンセル",
[]*widget.FormItem{
widget.NewFormItem("入力...", entry)},
func(b bool) {
if b && entry.Text != "" {
data = append([]string{entry.Text}, data...)
list.Refresh()
save(path)
}
}, win)
})
edit := widget.NewButtonWithIcon("", theme.DocumentCreateIcon(),
func() {
if row != -1 {
entry := widget.NewEntry()
entry.SetText(data[row])
dialog.ShowForm("ToDoを編集", "編集", "キャンセル",
[]*widget.FormItem{
widget.NewFormItem("入力...", entry)},
func(b bool) {
if b && row != -1 {
data[row] = entry.Text
list.Refresh()
save(path)
}
}, win)
}
})
remove := widget.NewButtonWithIcon("", theme.ContentRemoveIcon(),
func() {
if row != -1 {
dialog.ShowConfirm("ToDoの削除",
"本当に削除してもよろしいですか?",
func(b bool) {
if b {
data = append(data[:row], data[row+1:]...)
list.Unselect(row)
list.Refresh()
save(path)
}
}, win)
}
})
up := widget.NewButtonWithIcon("", theme.MoveUpIcon(),
func() {
if row != 0 && row != -1 {
temp := data[row]
data = append(data[:row], data[row+1:]...)
data = append(data[:row-1],
append([]string{temp}, data[row-1:]...)...)
row -= 1
list.Select(row)
list.Refresh()
save(path)
}
})
down := widget.NewButtonWithIcon("", theme.MoveDownIcon(),
func() {
if row != len(data)-1 && row != -1 {
temp := data[row]
data = append(data[:row], data[row+1:]...)
data = append(data[:row+1],
append([]string{temp}, data[row+1:]...)...)
row += 1
list.Select(row)
list.Refresh()
save(path)
}
})
buttons := container.New(layout.NewVBoxLayout(),
add, edit, up, down, layout.NewSpacer(), remove)
content := container.New(layout.NewBorderLayout(nil, nil, nil, buttons),
buttons, list)
win.SetContent(content)
win.Resize(fyne.NewSize(400, 200))
win.ShowAndRun()
}
fyne bundle Osaka.ttf > bundle.go
// 上記のようにコマンドするとフォントをリソースに変換した bundle.go が出来上がります。
// Osaka.ttf のところは、フォントフォルダからコピーしてきた任意の日本語フォントを指定します。
// フォントファイルをコピーせずに、フォントファイルまでのフルパスを指定することもできます。
// この bundle.go は、大変大きなファイルになりますので、ほとんどのテキストエディタでは開けません。
// 私の場合は、ターミナル版 Emacs とデスクトップ版 CotEditor では、フリーズしてしまいました。
// デスクトップ版のEmacsでは開くことができました。
// フォントのサイズがそのままアプリケーションのサイズになることはありませんが、大きなサイズのフォントほど大きなアプリケーションのサイズになります。
// サイズを気にしないのであれば、Mac の場合 Arial Unicode.ttf などが探しやすくて良いでしょう。
var resourceOsakaTtf = &fyne.StaticResource{
StaticName: "Osaka.ttf",
StaticContent: []byte(
"\x00\x01\x00\x00\x00\x19\x01\x00\x00\x04\x01\x80OS/2V\x82p}\x00\x00\x02\xf0\x00"・・・
// bundle.go を大きなサイズのファイルを開くことができるテキストエディタで開くと上記のように書かれています。末尾はフォントを定義した数値が続きます。
// ここで必要となるのは var の次に書かれている resourceOsakaTtf というリソース名です。
// このリソース名は読み込んだフォントによって変わります。
// 15行目の return に先程のフォントリソース名を指定します。
package main
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/theme"
"image/color"
)
type myTheme struct{}
var _ fyne.Theme = (*myTheme)(nil)
// return bundled font resource
func (*myTheme) Font(s fyne.TextStyle) fyne.Resource {
return resourceOsakaTtf
}
func (*myTheme) Color(n fyne.ThemeColorName, v fyne.ThemeVariant) color.Color {
return theme.DefaultTheme().Color(n, v)
}
func (*myTheme) Icon(n fyne.ThemeIconName) fyne.Resource {
return theme.DefaultTheme().Icon(n)
}
func (*myTheme) Size(n fyne.ThemeSizeName) float32 {
return theme.DefaultTheme().Size(n)
}
コードの記述が完了しましたら、次のようにして実行します。
go run todo.go bundle.go theme.go
// 次のようにパッケージをインストールしてくださいという表示が出ます。
todo.go:4:8: no required module provides package fyne.io/fyne/v2; to add it:
go get fyne.io/fyne/v2
todo.go:5:8: no required module provides package fyne.io/fyne/v2/app; to add it:
go get fyne.io/fyne/v2/app
todo.go:6:8: no required module provides package fyne.io/fyne/v2/container; to add it:
go get fyne.io/fyne/v2/container
todo.go:7:8: no required module provides package fyne.io/fyne/v2/dialog; to add it:
go get fyne.io/fyne/v2/dialog
todo.go:8:8: no required module provides package fyne.io/fyne/v2/layout; to add it:
go get fyne.io/fyne/v2/layout
goto.go:9:8: no required module provides package fyne.io/fyne/v2/theme; to add it:
go get fyne.io/fyne/v2/theme
todo.go:10:8: no required module provides package fyne.io/fyne/v2/widget; to add it:
go get fyne.io/fyne/v2/widget
// 指示のとおりインストールします。
go get fyne.io/fyne/v2
go get fyne.io/fyne/v2/app
go get fyne.io/fyne/v2/container
go get fyne.io/fyne/v2/dialog
go get fyne.io/fyne/v2/layout
go get fyne.io/fyne/v2/theme
go get fyne.io/fyne/v2/widget
// もう一度実行してみます。
go run todo.go bundle.go theme.go
Fyne のリストでは、選択されている項目は、色が反転するのではなく、 項目の左隅に青い縦線が表示されるだけです、注意してくだい。
デフォルトでは、Windowsの場合は白いウィンドウ、macOSとLinuxの場合は、 黒いウィンドウになります。
いきなり、ToDo というチュートリアルでやるべき最後のサンプルのようなことから初めてしまいました。 ここから続けるとすれば、テキストエディタか画像ビューワになると思いますが、その前に Fyne のインストールと簡単なサンプルを前に戻って説明しようと思います。