gui-gio:温度转换器的demo2

使用go-gio库示例温度转换器的详解

温度转换器

包含的控件和功能
1、编辑器组件的显示和获取文本、设置文本和提示信息
2、边框的显示和设置
3、按钮事件

package main

import (
	"image/color"
	"log"
	"os"
	"strconv"

	"gioui.org/app"             // app contains Window handling.
	"gioui.org/font/gofont"     // gofont is used for loading the default font.
	"gioui.org/io/key"          // key is used for keyboard events.
	"gioui.org/io/system"       // system is used for system events (e.g. closing the window).
	"gioui.org/layout"          // layout is used for layouting widgets.
	"gioui.org/op"              // op is used for recording different operations.
	"gioui.org/unit"            // unit is used to define pixel-independent sizes
	"gioui.org/widget"          // widget contains state handling for widgets.
	"gioui.org/widget/material" // material contains material design widgets.
)

func main() {
	ui := NewUI()

	go func() {
		w := app.NewWindow(
			app.Title("Temperature Converter"),
			app.Size(unit.Dp(400), unit.Dp(60)),
		)
		if err := ui.Run(w); err != nil {
			log.Println(err)
			os.Exit(1)
		}
		os.Exit(0)
	}()

	app.Main()
}

var defaultMargin = unit.Dp(10)

// UI 界面类
type UI struct {
	// 主题
	Theme *material.Theme

	// 转换器类
	Converter Converter
}

// NewUI 实例化界面
func NewUI() *UI {
	ui := &UI{}
	ui.Theme = material.NewTheme(gofont.Collection())

	ui.Converter.Init()
	return ui
}

// Run 运行逻辑
func (ui *UI) Run(w *app.Window) error {
	var ops op.Ops

	for e := range w.Events() {
		switch e := e.(type) {
		case system.FrameEvent:
			gtx := layout.NewContext(&ops, e)

			ui.Layout(gtx)

			e.Frame(gtx.Ops)

		case key.Event:
			switch e.Name {
			case key.NameEscape:
				return nil
			}
		case system.DestroyEvent:
			return e.Err
		}
	}

	return nil
}

// Layout 显示界面
func (ui *UI) Layout(gtx layout.Context) layout.Dimensions {
	// 在窗口周边填充
	inset := layout.UniformInset(defaultMargin)
	return inset.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
		return ui.Converter.Layout(ui.Theme, gtx)
	})
}

// Converter 记录各自的状态
// 包含两个编辑器
type Converter struct {
	clicked    widget.Clickable
	Celsius    Field // 摄氏度组件
	Fahrenheit Field // 华氏度组件

}

// Init 设置编辑器的显示、强制文本显示在一行上、单行显示不换行
func (conv *Converter) Init() {
	conv.Celsius.SingleLine = true
	conv.Fahrenheit.SingleLine = true
}

// Layout lays out the editors.
func (conv *Converter) Layout(th *material.Theme, gtx layout.Context) layout.Dimensions {
	// 创建一个空的组件来占位
	spacer := layout.Rigid(layout.Spacer{Width: defaultMargin}.Layout)

	// 检查摄氏度值是否更改
	if conv.Celsius.Changed() {
		newValue, err := strconv.Atoi(conv.Celsius.Text())
		// update whether the editor is displaying a valid value
		conv.Celsius.Invalid = err != nil
		if !conv.Celsius.Invalid {
			// 设置华氏度数值合法
			conv.Fahrenheit.Invalid = false
			// 设置华氏度编辑器显示的值
			conv.Fahrenheit.SetText(strconv.Itoa(newValue*9/5 + 32))
		}
	}

	// 检查华氏度数值是否变化
	if conv.Fahrenheit.Changed() {
		newValue, err := strconv.Atoi(conv.Fahrenheit.Text())
		conv.Fahrenheit.Invalid = err != nil
		if !conv.Fahrenheit.Invalid {
			// 更改摄氏度状态和显示值
			conv.Celsius.Invalid = false
			conv.Celsius.SetText(strconv.Itoa((newValue - 32) * 5 / 9))
		}
	}
	// 按钮点击、清空输入框内容
	for range conv.clicked.Clicks() {
		conv.Celsius.SetText("")
		conv.Fahrenheit.SetText("")
	}
	return layout.Flex{}.Layout(gtx,
		layout.Flexed(1, func(gtx layout.Context) layout.Dimensions {
			return conv.Celsius.Layout(th, gtx)
		}),
		spacer,
		// 刚体结构、不会变形
		layout.Rigid(material.Body1(th, "Celsius").Layout),
		spacer,
		layout.Rigid(material.Body1(th, "=").Layout),
		spacer,
		// 可以变形
		layout.Flexed(1, func(gtx layout.Context) layout.Dimensions {
			return conv.Fahrenheit.Layout(th, gtx)
		}),
		spacer,
		layout.Rigid(material.Body1(th, "Fahrenheit").Layout),
		spacer,
		layout.Rigid(material.Button(th, &conv.clicked, "clear").Layout),
	)
}

// Field 编辑器插件类
type Field struct {
	widget.Editor      //编辑器组件
	Invalid       bool // 数值是否非法

	old string // 旧值
}

// Changed 检查当前值是否更改了
func (ed *Field) Changed() bool {
	// 获取当前值
	newText := ed.Editor.Text()
	// 检查是否和旧值相等
	changed := newText != ed.old
	// 记录新值
	ed.old = newText
	return changed
}

// SetText 设置编辑器显示的内容
func (ed *Field) SetText(s string) {
	ed.old = s           // 将值记录为旧值
	ed.Editor.SetText(s) // 将值设置到编辑器内
}

// Layout 编辑器显示逻辑
func (ed *Field) Layout(th *material.Theme, gtx layout.Context) layout.Dimensions {
	// 根据编辑器的状态确定边框的颜色
	borderWidth := float32(0.5)        // 边框宽度
	borderColor := color.NRGBA{A: 107} // 边框颜色
	switch {
	case ed.Editor.Focused(): // 当编辑器被选中
		borderColor = th.Palette.ContrastBg
		borderWidth = 2 // 加大边框值
	case ed.Invalid:
		borderColor = color.NRGBA{R: 200, A: 0xFF}
	}

	// 画一个带边框的编辑器
	return widget.Border{
		Color:        borderColor,          // 颜色
		CornerRadius: unit.Dp(4),           // 边角角度值
		Width:        unit.Dp(borderWidth), // 宽度
	}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
		return layout.UniformInset(unit.Dp(4)).Layout(gtx,
			// 编辑器、主题-编辑器组件、提示信息
			material.Editor(th, &ed.Editor, "1").Layout)
	})
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值