diff --git a/customLayout.go b/customLayout.go deleted file mode 100644 index c6339b5..0000000 --- a/customLayout.go +++ /dev/null @@ -1,58 +0,0 @@ -package main - -import ( - "fyne.io/fyne/v2" -) - -type aspectRatioLayout struct { - aspectRatio float32 - rows int - cols int -} - -func newAspectRatioLayout(aspectRatio float32, rows, cols int) fyne.Layout { - return &aspectRatioLayout{aspectRatio: aspectRatio, rows: rows, cols: cols} -} - -func (a *aspectRatioLayout) Layout(objects []fyne.CanvasObject, size fyne.Size) { - // Calculate cell size based on width - cellWidthBasedOnWidth := size.Width / float32(a.cols) - cellHeightFromWidth := cellWidthBasedOnWidth / a.aspectRatio - - // Calculate cell size based on height - cellHeightBasedOnHeight := size.Height / float32(a.rows) - cellWidthFromHeight := cellHeightBasedOnHeight * a.aspectRatio - - var cellWidth, cellHeight float32 - - // Choose the smaller of the two to fit in the container - if cellWidthBasedOnWidth*float32(a.cols) <= size.Width && cellHeightFromWidth*float32(a.rows) <= size.Height { - cellWidth = cellWidthBasedOnWidth - cellHeight = cellHeightFromWidth - } else { - cellWidth = cellWidthFromHeight - cellHeight = cellHeightBasedOnHeight - } - - if cellHeight*float32(a.rows) > size.Height { - cellHeight = size.Height / float32(a.rows) - cellWidth = cellHeight * a.aspectRatio - } - - x, y := float32(0), float32(0) - for i, o := range objects { - o.Resize(fyne.NewSize(cellWidth, cellHeight)) - o.Move(fyne.NewPos(x, y)) - - if (i+1)%a.cols == 0 { - x = 0 - y += cellHeight - } else { - x += cellWidth - } - } -} - -func (a *aspectRatioLayout) MinSize(objects []fyne.CanvasObject) fyne.Size { - return fyne.NewSize(10, 10) -} diff --git a/history.go b/history.go index 324cab3..620a5cc 100644 --- a/history.go +++ b/history.go @@ -3,6 +3,7 @@ package main import ( "fyne.io/fyne/v2" "fyne.io/fyne/v2/container" + "fyne.io/fyne/v2/theme" "fyne.io/fyne/v2/widget" ) @@ -15,9 +16,9 @@ type calculation struct { type historyItemRenderer struct { item *historyItem - equationLabel *widget.Label - diceRollsLabel *widget.Label - resultLabel *widget.Label + equationLabel *customLabel + diceRollsLabel *customLabel + resultLabel *customLabel objects []fyne.CanvasObject } @@ -33,9 +34,9 @@ func (r *historyItemRenderer) ApplyTheme() { } func (r *historyItemRenderer) Refresh() { - r.equationLabel.SetText(r.item.calc.equation) - r.diceRollsLabel.SetText(r.item.calc.diceRolls) - r.resultLabel.SetText(r.item.calc.result) + r.equationLabel.Text = r.item.calc.equation + r.diceRollsLabel.Text = r.item.calc.diceRolls + r.resultLabel.Text = r.item.calc.result r.equationLabel.Refresh() r.diceRollsLabel.Refresh() r.resultLabel.Refresh() @@ -55,14 +56,9 @@ type historyItem struct { func (h *historyItem) CreateRenderer() fyne.WidgetRenderer { h.ExtendBaseWidget(h) - equationLabel := widget.NewLabel(h.calc.equation) - equationLabel.TextStyle.Bold = true - - diceRollsLabel := widget.NewLabel(h.calc.diceRolls) - diceRollsLabel.TextStyle.Italic = true - - resultLabel := widget.NewLabel(h.calc.result) - resultLabel.Alignment = fyne.TextAlignTrailing + equationLabel := newCustomLabel(h.calc.equation, fyne.CurrentApp().Settings().Theme().Size(theme.SizeNameText)*2, fyne.TextStyle{Bold: true}, fyne.TextAlignLeading, theme.ForegroundColor()) + diceRollsLabel := newCustomLabel(h.calc.diceRolls, fyne.CurrentApp().Settings().Theme().Size(theme.SizeNameText)*1.5, fyne.TextStyle{Italic: true}, fyne.TextAlignLeading, theme.ForegroundColor()) + resultLabel := newCustomLabel(h.calc.result, fyne.CurrentApp().Settings().Theme().Size(theme.SizeNameText)*2, fyne.TextStyle{}, fyne.TextAlignTrailing, theme.ForegroundColor()) layout := container.NewBorder( nil, diff --git a/main.go b/main.go index af47269..3096578 100644 --- a/main.go +++ b/main.go @@ -8,6 +8,7 @@ import ( "fyne.io/fyne/v2" "fyne.io/fyne/v2/app" "fyne.io/fyne/v2/container" + "fyne.io/fyne/v2/theme" "fyne.io/fyne/v2/widget" ) @@ -19,7 +20,7 @@ func main() { var historyList *widget.List // Dice input bar - diceInputEntry := widget.NewEntry() + diceInputEntry := newCustomEntry(fyne.CurrentApp().Settings().Theme().Size(theme.SizeNameText) * 2) diceInputEntry.SetPlaceHolder("e.g., 2d20H, 3d6+5") historyList = widget.NewList( @@ -34,8 +35,7 @@ func main() { }, ) - // Roll button - rollButton := widget.NewButton("ROLL", func() { + roll := func() { diceInput := strings.TrimSpace(diceInputEntry.Text) if diceInput == "" { return @@ -59,47 +59,53 @@ func main() { historyList.Refresh() diceInputEntry.SetText("") } - }) - rollButton.Importance = widget.HighImportance + } + + // Roll button + rollButton := newCustomButton2WithImportance("ROLL", widget.HighImportance, roll) + + diceInputEntry.OnSubmitted = func(s string) { + roll() + } buttons := []fyne.CanvasObject{ - widget.NewButton("H", func() { + newCustomButton2("H", func() { diceInputEntry.SetText(diceInputEntry.Text + "H") }), - widget.NewButton("dX", func() { + newCustomButton2("dX", func() { diceInputEntry.SetText(diceInputEntry.Text + "dX") }), - widget.NewButton("d4", func() { + newCustomButton2("d4", func() { diceInputEntry.SetText(diceInputEntry.Text + "d4") }), - widget.NewButton("d6", func() { + newCustomButton2("d6", func() { diceInputEntry.SetText(diceInputEntry.Text + "d6") }), - widget.NewButton("d8", func() { + newCustomButton2("d8", func() { diceInputEntry.SetText(diceInputEntry.Text + "d8") }), - widget.NewButton("L", func() { + newCustomButton2("L", func() { diceInputEntry.SetText(diceInputEntry.Text + "L") }), - widget.NewButton("d10", func() { + newCustomButton2("d10", func() { diceInputEntry.SetText(diceInputEntry.Text + "d10") }), - widget.NewButton("d12", func() { + newCustomButton2("d12", func() { diceInputEntry.SetText(diceInputEntry.Text + "d12") }), - widget.NewButton("d20", func() { + newCustomButton2("d20", func() { diceInputEntry.SetText(diceInputEntry.Text + "d20") }), - widget.NewButton("d100", func() { + newCustomButton2("d100", func() { diceInputEntry.SetText(diceInputEntry.Text + "d100") }), createCalcButton("7", diceInputEntry), createCalcButton("8", diceInputEntry), createCalcButton("9", diceInputEntry), - widget.NewButton("*", func() { + newCustomButton2("*", func() { diceInputEntry.SetText(diceInputEntry.Text + "*") }), - widget.NewButton("⌫", func() { + newCustomButton2("⌫", func() { text := diceInputEntry.Text if len(text) > 0 { diceInputEntry.SetText(text[:len(text)-1]) @@ -108,29 +114,29 @@ func main() { createCalcButton("4", diceInputEntry), createCalcButton("5", diceInputEntry), createCalcButton("6", diceInputEntry), - widget.NewButton("/", func() { + newCustomButton2("/", func() { diceInputEntry.SetText(diceInputEntry.Text + "/") }), - widget.NewButton("C", func() { + newCustomButton2("C", func() { diceInputEntry.SetText("") }), createCalcButton("1", diceInputEntry), createCalcButton("2", diceInputEntry), createCalcButton("3", diceInputEntry), - widget.NewButton("+", func() { + newCustomButton2("+", func() { diceInputEntry.SetText(diceInputEntry.Text + "+") }), - widget.NewButton("📊", func() { + newCustomButton2("📊", func() { // TODO: Implement statistics logic }), - widget.NewButton(".", func() { + newCustomButton2(".", func() { diceInputEntry.SetText(diceInputEntry.Text + ".") }), createCalcButton("0", diceInputEntry), - widget.NewButton("^", func() { + newCustomButton2("^", func() { diceInputEntry.SetText(diceInputEntry.Text + "^") }), - widget.NewButton("-", func() { + newCustomButton2("-", func() { diceInputEntry.SetText(diceInputEntry.Text + "-") }), rollButton, @@ -157,8 +163,8 @@ func main() { myWindow.ShowAndRun() } -func createCalcButton(label string, entry *widget.Entry) *widget.Button { - return widget.NewButton(label, func() { +func createCalcButton(label string, entry *customEntry) *customButton2 { + return newCustomButton2(label, func() { entry.SetText(entry.Text + label) }) } diff --git a/ui.go b/ui.go new file mode 100644 index 0000000..610d604 --- /dev/null +++ b/ui.go @@ -0,0 +1,298 @@ +package main + +import ( + "image/color" + + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/canvas" + "fyne.io/fyne/v2/driver/desktop" + "fyne.io/fyne/v2/theme" + "fyne.io/fyne/v2/widget" +) + +// customLabel is a custom label widget with configurable text styling +type customLabel struct { + widget.BaseWidget + Text string + TextSize float32 + Alignment fyne.TextAlign + TextStyle fyne.TextStyle + Color color.Color +} + +func (l *customLabel) CreateRenderer() fyne.WidgetRenderer { + l.ExtendBaseWidget(l) + text := canvas.NewText(l.Text, l.Color) + text.TextSize = l.TextSize + text.Alignment = l.Alignment + text.TextStyle = l.TextStyle + return &customLabelRenderer{ + text: text, + label: l, + objects: []fyne.CanvasObject{text}, + } +} + +type customLabelRenderer struct { + text *canvas.Text + label *customLabel + objects []fyne.CanvasObject +} + +func (r *customLabelRenderer) Layout(size fyne.Size) { + r.text.Resize(size) +} + +func (r *customLabelRenderer) MinSize() fyne.Size { + return r.text.MinSize() +} + +func (r *customLabelRenderer) Refresh() { + r.text.Text = r.label.Text + r.text.TextSize = r.label.TextSize + r.text.Alignment = r.label.Alignment + r.text.TextStyle = r.label.TextStyle + r.text.Color = r.label.Color + r.text.Refresh() +} + +func (r *customLabelRenderer) Objects() []fyne.CanvasObject { + return r.objects +} + +func (r *customLabelRenderer) Destroy() { +} + +func newCustomLabel(text string, size float32, style fyne.TextStyle, align fyne.TextAlign, textColor color.Color) *customLabel { + return &customLabel{ + Text: text, + TextSize: size, + TextStyle: style, + Alignment: align, + Color: textColor, + } +} + +// customButton2 is a custom button widget with theme-aware styling +type customButton2 struct { + widget.BaseWidget + Text string + OnTapped func() + Importance widget.Importance +} + +func (b *customButton2) CreateRenderer() fyne.WidgetRenderer { + b.ExtendBaseWidget(b) + text := canvas.NewText(b.Text, color.White) + text.Alignment = fyne.TextAlignCenter + background := canvas.NewRectangle(color.Transparent) + return &customButton2Renderer{ + text: text, + background: background, + button: b, + objects: []fyne.CanvasObject{background, text}, + } +} + +func (b *customButton2) Tapped(*fyne.PointEvent) { + if b.OnTapped != nil { + b.OnTapped() + } +} + +func (b *customButton2) TappedSecondary(*fyne.PointEvent) { +} + +func (b *customButton2) MouseIn(*desktop.MouseEvent) { +} + +func (b *customButton2) MouseOut() { +} + +func (b *customButton2) MouseMoved(*desktop.MouseEvent) { +} + +type customButton2Renderer struct { + text *canvas.Text + background *canvas.Rectangle + button *customButton2 + objects []fyne.CanvasObject +} + +func (r *customButton2Renderer) Layout(size fyne.Size) { + r.background.Resize(size) + r.text.Resize(size) + newTextSize := size.Height * 0.4 + if size.Width < newTextSize { + newTextSize = size.Width * 0.4 + } + r.text.TextSize = newTextSize +} + +func (r *customButton2Renderer) MinSize() fyne.Size { + return r.text.MinSize() +} + +func (r *customButton2Renderer) Refresh() { + r.text.Text = r.button.Text + r.text.Refresh() + r.background.FillColor = r.button.backgroundColor() + r.background.Refresh() +} + +func (r *customButton2Renderer) Objects() []fyne.CanvasObject { + return r.objects +} + +func (r *customButton2Renderer) Destroy() { +} + +func (b *customButton2) backgroundColor() color.Color { + switch b.Importance { + case widget.HighImportance: + return fyne.CurrentApp().Settings().Theme().Color(theme.ColorNamePrimary, theme.VariantDark) + default: + return fyne.CurrentApp().Settings().Theme().Color(theme.ColorNameButton, theme.VariantDark) + } +} + +func newCustomButton2(label string, onTap func()) *customButton2 { + return &customButton2{ + Text: label, + OnTapped: onTap, + } +} + +func newCustomButton2WithImportance(label string, importance widget.Importance, onTap func()) *customButton2 { + return &customButton2{ + Text: label, + OnTapped: onTap, + Importance: importance, + } +} + +// customEntry is a custom entry widget with configurable text size +type customEntry struct { + widget.Entry + TextSize float32 +} + +func (e *customEntry) CreateRenderer() fyne.WidgetRenderer { + e.ExtendBaseWidget(e) + + text := canvas.NewText(e.Text, color.White) + text.TextSize = e.TextSize + + placeholder := canvas.NewText(e.PlaceHolder, theme.PlaceHolderColor()) + placeholder.TextSize = e.TextSize + placeholder.TextStyle = e.TextStyle + + objects := []fyne.CanvasObject{placeholder, text} + + return &customEntryRenderer{ + entry: e, + text: text, + placeholder: placeholder, + objects: objects, + } +} + +type customEntryRenderer struct { + entry *customEntry + text *canvas.Text + placeholder *canvas.Text + objects []fyne.CanvasObject +} + +func (r *customEntryRenderer) Layout(size fyne.Size) { + r.text.Resize(size) + r.placeholder.Resize(size) +} + +func (r *customEntryRenderer) MinSize() fyne.Size { + return r.text.MinSize() +} + +func (r *customEntryRenderer) Refresh() { + r.text.Text = r.entry.Text + r.text.TextSize = r.entry.TextSize + r.text.Refresh() + + r.placeholder.Text = r.entry.PlaceHolder + r.placeholder.TextSize = r.entry.TextSize + if r.entry.Text == "" { + r.placeholder.Show() + } else { + r.placeholder.Hide() + } + r.placeholder.Refresh() +} + +func (r *customEntryRenderer) Objects() []fyne.CanvasObject { + return r.objects +} + +func (r *customEntryRenderer) Destroy() { +} + +func newCustomEntry(size float32) *customEntry { + e := &customEntry{ + TextSize: size, + } + e.ExtendBaseWidget(e) + return e +} + +// aspectRatioLayout is a custom layout that maintains aspect ratio for grid items +type aspectRatioLayout struct { + aspectRatio float32 + rows int + cols int +} + +func newAspectRatioLayout(aspectRatio float32, rows, cols int) fyne.Layout { + return &aspectRatioLayout{aspectRatio: aspectRatio, rows: rows, cols: cols} +} + +func (a *aspectRatioLayout) Layout(objects []fyne.CanvasObject, size fyne.Size) { + // Calculate cell size based on width + cellWidthBasedOnWidth := size.Width / float32(a.cols) + cellHeightFromWidth := cellWidthBasedOnWidth / a.aspectRatio + + // Calculate cell size based on height + cellHeightBasedOnHeight := size.Height / float32(a.rows) + cellWidthFromHeight := cellHeightBasedOnHeight * a.aspectRatio + + var cellWidth, cellHeight float32 + + // Choose the smaller of the two to fit in the container + if cellWidthBasedOnWidth*float32(a.cols) <= size.Width && cellHeightFromWidth*float32(a.rows) <= size.Height { + cellWidth = cellWidthBasedOnWidth + cellHeight = cellHeightFromWidth + } else { + cellWidth = cellWidthFromHeight + cellHeight = cellHeightBasedOnHeight + } + + if cellHeight*float32(a.rows) > size.Height { + cellHeight = size.Height / float32(a.rows) + cellWidth = cellHeight * a.aspectRatio + } + + x, y := float32(0), float32(0) + for i, o := range objects { + o.Resize(fyne.NewSize(cellWidth, cellHeight)) + o.Move(fyne.NewPos(x, y)) + + if (i+1)%a.cols == 0 { + x = 0 + y += cellHeight + } else { + x += cellWidth + } + } +} + +func (a *aspectRatioLayout) MinSize(objects []fyne.CanvasObject) fyne.Size { + return fyne.NewSize(10, 10) +}