Skip to content

見た目とウィンドウ

すべての要素が取るキーワード、配色、そしてウィンドウ自身が持ってくるもの。

すべての要素が取るキーワード

次の 15 の性質は、どの要素でも同じ意味を持ち、どれもメソッドとして呼べます。

メソッド 働き
Width(v), Height(v) float64 固定の大きさ。0 でもそのまま書かれる
MinWidth(v), MaxWidth(v) float64 大きさの下限と上限
Disabled(v) bool 操作を受け付けず、その見た目で描かれる
Theme(v) string 下にある要素が色の名前を引く配色。"dark""light"
Animate(ms), Easing(v) float64, string 変化にかける時間と、その曲がり方("out""inOut"
Enter(v), Exit(v) bool 現れるとき、消えるときの動き
ColSpan(v), RowSpan(v) int グリッドの列や行をいくつ分またぐか
Role(v), A11yLabel(v) string 読み上げに伝える内容
Tooltip(v) string ポインタを合わせたときに出る文字

同じ名前をもともと自分の意味で持っている要素では、要素側の意味がそのまま通ります。 ButtonWidth はボタン自身の幅で、ボタンはその幅で描かれます。 ColumnWidth は、中身を囲む箱の幅です。 ラベルがそのまま読み上げの名前になる要素(CheckboxSwitchProgress)は、A11yLabel を断ります。 与えるべき二つ目の名前がないからです。

テーマとアニメーション

色は 16 進の値か、エンジンが持つ色の名前("accent""panel""text""textDim")です。 名前で書いた色は、上にあるいちばん近い Theme の配色で決まります。 だからメソッド一つで、パネル全体の色が切り替わります。

package main

import (
    "fmt"

    . "github.com/i2y/yokan/gomamochi"
)

func key(b *ButtonEl) *ButtonEl { return b.Background("#313244").HoverBackground("#45475a") }

type Styled struct {
    mode string
    n    int
}

func (s *Styled) flip() {
    if s.mode == "dark" {
        s.mode = "light"
    } else {
        s.mode = "dark"
    }
}

func (s *Styled) View() Element {
    return Column(
        Text(fmt.Sprintf("n=%d", s.n)).Size(18).Color("accent"),
        Row(
            key(Button("+1")).OnClick(func() { s.n += 1 }),
            key(Button("flip")).Background("#fab387").OnClick(func() { s.flip() }),
        ).Spacing(6),
    ).Spacing(8).Padding(12).Background("panel").Theme(s.mode)
}

func main() {
    Run(&Styled{mode: "dark"}, Title("styled"))
}

見た目を一か所にまとめるには、渡された要素すべてに同じ性質を付ける関数を書きます。 上の key がそれです。 返るのは元の要素の型なので、そのあともメソッドをつなげられます。 要素に Animate(120).Easing("out") と書くと、その要素は変化のたびに 120 ミリ秒かけてなめらかに動きます。 現れるときと消えるときにも、EnterExit で同じ動きがつきます。

ウィンドウまわり

Run はアプリとそのオプションを取ります。 オプションは TitleSize(width, height)、それにウィンドウの縁とアプリのツリーのあいだの余白である Padding です。 Quit() は、エンジンの次のフレームでウィンドウを閉じます。 ウィンドウなしの実行ではこれが効かないので、スクリプトは最後まで走ります。

並べたり覆ったりする要素は、次のとおりです。 Grid は子要素を Columns で決めた数の列に並べ、GridCell(child).ColSpan(2) はその列をまたぎます。 Stack は子要素を重ねて置きます。 ScrollViewHScrollView はスクロールする領域です。 Modal は、Open のあいだ、ウィンドウのほかの部分に重なって出るパネルです。 Spacer は親要素に余っている場所を取り、Divider は罫線を引き、Link はページを開き、SpinnerProgress は処理中であることを示し、ImageSvg はファイルを表示します。

package main

import (
    "fmt"

    . "github.com/i2y/yokan/gomamochi"
)

type Panels struct {
    open bool
}

func (p *Panels) View() Element {
    var lines []Element
    for n := 1; n <= 12; n++ {
        lines = append(lines, Text(fmt.Sprintf("line %d", n)))
    }
    return Stack(
        Column(
            Row(Text("Panels").Size(18), Spacer(), Spinner().Size(14)).Spacing(8),
            Grid(Text("one"), Text("two"), GridCell(Text("across both").Align("center")).ColSpan(2)).Columns(2).Spacing(6),
            ScrollView(Column(lines...).Spacing(2)).Height(90),
            Button("about").OnClick(func() { p.open = true }),
        ).Spacing(10).Padding(14),
        Modal(
            Column(
                Text("A panel over the rest of it.").Size(14),
                Button("close").OnClick(func() { p.open = false }),
            ).Spacing(8).Padding(12).Background("panel"),
        ).Open(p.open),
    )
}

func main() {
    Run(&Panels{}, Title("panels"), Size(420, 360))
}