Repository navigation
Routeboy: map exploring and GPX management app #6575
Replies: 3 comments 5 replies
|
Very cool. but for multi-touch support you should not have needed any extra API or GLFW patching - it works in the Fyne mobile layer. Building "fyne package -tags mobile" should have enabled a lot of that for you. That would also resolve the drag-scroll and "fyne doesn't know it's on a phone". Without it you've built a desktop app and loaded it onto a mobile device. Did you consider the map from fyne-x rather than the agent building a new widget? |
Awesome, I'll give that a try. Multi-touch was working on Android without the patch, but maybe I missed it and it also works on Wayland/Phosh on Linux mobile? I did find a few issues linked above documenting the issue, perhaps that derailed the whole thing. I'll report back!
Yeah I did. Last time I checked it was using raster tiles, which are blurry and/or hard to scale in HiDPI devices, and performance of the Widget wasn't there for the mid-range Linux Fairphone 6. The vibecoded Map widget uses hardware accelerated rendering to render tiles from vector maps, which the Adreno GPU on the Firephone seems to be happy with. |
@andydotxyz this is the code I used to test multi touch on the Fairphone, run with: I couldn't find a way for it to register multiple fingers, touch up, etc. What am I missing? package main
import (
"fmt"
"image/color"
"log"
"maps"
"slices"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/driver/desktop"
"fyne.io/fyne/v2/driver/mobile"
"fyne.io/fyne/v2/widget"
)
func main() {
a := app.New()
w := a.NewWindow("Fingers")
status := widget.NewLabel("")
status.Wrapping = fyne.TextWrapWord
p := newPad(status)
w.SetContent(container.NewBorder(status, nil, nil, nil, p))
w.Resize(fyne.NewSize(400, 700))
a.Lifecycle().SetOnStarted(func() {
fyne.Do(func() {
p.update()
log.Print(p.status.Text)
})
})
w.ShowAndRun()
}
// pointer is the ID shown for the desktop driver's single pointer.
const pointer = -1
var palette = []color.NRGBA{
{R: 0xe0, G: 0x1b, B: 0x24, A: 0xff}, {R: 0x33, G: 0xd1, B: 0x7a, A: 0xff}, {R: 0x35, G: 0x84, B: 0xe4, A: 0xff},
{R: 0xf6, G: 0xd3, B: 0x2d, A: 0xff}, {R: 0x91, G: 0x41, B: 0xac, A: 0xff}, {R: 0xff, G: 0x78, B: 0x00, A: 0xff},
}
type pad struct {
widget.BaseWidget
status *widget.Label
layer *fyne.Container
fingers map[int]fyne.Position
peak int
events int
pressed bool
}
func newPad(status *widget.Label) *pad {
p := &pad{status: status, layer: container.NewWithoutLayout(), fingers: map[int]fyne.Position{}}
p.ExtendBaseWidget(p)
return p
}
func (p *pad) CreateRenderer() fyne.WidgetRenderer {
bg := canvas.NewRectangle(color.NRGBA{R: 0x30, G: 0x30, B: 0x30, A: 0xff})
return widget.NewSimpleRenderer(container.NewStack(bg, p.layer))
}
// The mobile driver reports every finger here, each with its ID.
func (p *pad) TouchDown(e *mobile.TouchEvent) { p.record("touch down", e.ID, e.Position) }
func (p *pad) TouchMoved(e *mobile.TouchEvent) { p.record("touch move", e.ID, e.Position) }
func (p *pad) TouchUp(e *mobile.TouchEvent) { p.lift("touch up", e.ID) }
func (p *pad) TouchCancel(e *mobile.TouchEvent) { p.lift("touch cancel", e.ID) }
// The desktop driver knows one pointer. The mobile driver drags with finger 0
// as well, which is already counted above.
func (p *pad) MouseDown(e *desktop.MouseEvent) {
if !fyne.CurrentDevice().IsMobile() {
p.pressed = true
p.record("mouse down", pointer, e.Position)
}
}
func (p *pad) MouseUp(*desktop.MouseEvent) {
if p.pressed {
p.pressed = false
p.lift("mouse up", pointer)
}
}
func (p *pad) Dragged(e *fyne.DragEvent) {
if p.pressed {
p.record("mouse drag", pointer, e.Position)
}
}
func (p *pad) DragEnd() {
// After a drag the mobile driver sends DragEnd for finger 0 and no TouchUp.
if fyne.CurrentDevice().IsMobile() {
p.lift("drag end", 0)
}
}
func (p *pad) record(kind string, id int, pos fyne.Position) {
if _, down := p.fingers[id]; !down {
log.Printf("%s id=%d at %.0f,%.0f", kind, id, pos.X, pos.Y)
}
p.fingers[id] = pos
p.peak = max(p.peak, len(p.fingers))
p.events++
p.update()
}
func (p *pad) lift(kind string, id int) {
if _, down := p.fingers[id]; !down {
return
}
log.Printf("%s id=%d", kind, id)
delete(p.fingers, id)
p.events++
p.update()
}
func (p *pad) update() {
ids := slices.Sorted(maps.Keys(p.fingers))
scale := float32(0)
if c := fyne.CurrentApp().Driver().CanvasForObject(p); c != nil {
scale = c.Scale()
}
p.status.SetText(fmt.Sprintf("driver %T · IsMobile %v · scale %.2f\nfingers down %d %v · most at once %d · events %d",
fyne.CurrentApp().Driver(), fyne.CurrentDevice().IsMobile(), scale, len(ids), ids, p.peak, p.events))
p.layer.Objects = p.layer.Objects[:0]
for _, id := range ids {
dot := canvas.NewCircle(palette[((id%len(palette))+len(palette))%len(palette)])
dot.Resize(fyne.NewSize(60, 60))
dot.Move(p.fingers[id].SubtractXY(30, 30))
p.layer.Add(dot)
}
p.layer.Refresh()
} |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
I recently got a Fairphone 6 in my hands, installed an experimental Nura (formerly postmarketOS) image on it, and wanted to get a taste of what developing Fyne apps for it would look like. Where the limits are, with Fyne targeting Linux mobile.
I settled on vibecoding a Fyne app (Astra/Fable driven mainly) that:
After quite a few days iterating (definitely not a one-shot vibecoded app), and mixing in other libraries I had around, I'm pleasantly surprised with the results: Fyne ended up being incredibly malleable and easy to extend. Fyne itself is stock 2.8.1. Only the GLFW Go bindings required patching, for touch and on-screen keyboard support on Linux mobile.
Results
Routeboy on a GNOME desktop:
routeboy-desktop.mp4
Routeboy on the Fairphone 6:
routeboy-phone.mp4
Routeboy on the Android tablet:
routeboy-android-tablet.mp4
The source code for the app: Routeboy. The Adwaita-like layer it's built on: Fade.
What the agents added to the stack:
hostnamectlreports itNura/PostmarketOS specific fixes/workarounds
References
The phone and its OS:
Fyne and GLFW:
fyne.DoWayland and Linux:
My stuff:
All reactions