apply worl loading ui
This commit is contained in:
@@ -14,10 +14,6 @@ func NewClient() *client {
|
|||||||
c := &client{}
|
c := &client{}
|
||||||
c.app = app.New()
|
c.app = app.New()
|
||||||
c.window = c.app.NewWindow("Galaxy+")
|
c.window = c.app.NewWindow("Galaxy+")
|
||||||
|
|
||||||
// https://github.com/fyne-io/fyne/issues/418 - interactive raster
|
|
||||||
// https://github.com/fyne-io/fyne/issues/224 - resize
|
|
||||||
|
|
||||||
editor := NewEditor()
|
editor := NewEditor()
|
||||||
editor.BuildUI(c.window)
|
editor.BuildUI(c.window)
|
||||||
|
|
||||||
|
|||||||
+70
-32
@@ -10,7 +10,8 @@ import (
|
|||||||
"fyne.io/fyne/v2"
|
"fyne.io/fyne/v2"
|
||||||
"fyne.io/fyne/v2/canvas"
|
"fyne.io/fyne/v2/canvas"
|
||||||
"fyne.io/fyne/v2/container"
|
"fyne.io/fyne/v2/container"
|
||||||
"fyne.io/fyne/v2/layout"
|
"fyne.io/fyne/v2/theme"
|
||||||
|
"fyne.io/fyne/v2/widget"
|
||||||
"github.com/iliadenisov/galaxy/client/world"
|
"github.com/iliadenisov/galaxy/client/world"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -24,8 +25,7 @@ type editor struct {
|
|||||||
raster *canvas.Raster
|
raster *canvas.Raster
|
||||||
canvasScale float32
|
canvasScale float32
|
||||||
|
|
||||||
canvas *interactiveRaster
|
win fyne.Window
|
||||||
win fyne.Window
|
|
||||||
|
|
||||||
// Coalescer for latest-wins refresh scheduling.
|
// Coalescer for latest-wins refresh scheduling.
|
||||||
co *RasterCoalescer[world.RenderParams]
|
co *RasterCoalescer[world.RenderParams]
|
||||||
@@ -58,10 +58,6 @@ func (e *editor) ForceFullRedraw() {
|
|||||||
e.world.ForceFullRedrawNext()
|
e.world.ForceFullRedrawNext()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *editor) buildUI() fyne.CanvasObject {
|
|
||||||
return e.canvas
|
|
||||||
}
|
|
||||||
|
|
||||||
// здесь определяю, изменились ли границы raster, если да - обновляю размеры viewport, margin и корректирую zoom
|
// здесь определяю, изменились ли границы raster, если да - обновляю размеры viewport, margin и корректирую zoom
|
||||||
func (e *editor) updateSizes() {
|
func (e *editor) updateSizes() {
|
||||||
canvas := fyne.CurrentApp().Driver().CanvasForObject(e.raster)
|
canvas := fyne.CurrentApp().Driver().CanvasForObject(e.raster)
|
||||||
@@ -75,16 +71,21 @@ func (e *editor) updateSizes() {
|
|||||||
width := int(size.Width * e.canvasScale)
|
width := int(size.Width * e.canvasScale)
|
||||||
height := int(size.Height * e.canvasScale)
|
height := int(size.Height * e.canvasScale)
|
||||||
|
|
||||||
if width > 0 && height > 0 && (width != e.wp.ViewportWidthPx || height != e.wp.ViewportHeightPx) {
|
if width <= 0 || height <= 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if width != e.wp.ViewportWidthPx || height != e.wp.ViewportHeightPx {
|
||||||
e.wp.ViewportWidthPx = width
|
e.wp.ViewportWidthPx = width
|
||||||
e.wp.ViewportHeightPx = height
|
e.wp.ViewportHeightPx = height
|
||||||
e.wp.MarginXPx = e.wp.ViewportWidthPx / 4
|
e.wp.MarginXPx = e.wp.ViewportWidthPx / 4
|
||||||
e.wp.MarginYPx = e.wp.ViewportHeightPx / 4
|
e.wp.MarginYPx = e.wp.ViewportHeightPx / 4
|
||||||
|
defer func() { e.co.Request(*e.wp) }()
|
||||||
|
}
|
||||||
|
if e.world != nil {
|
||||||
e.wp.CameraZoom = e.world.CorrectCameraZoom(e.wp.CameraZoom, e.wp.ViewportWidthPx, e.wp.ViewportHeightPx)
|
e.wp.CameraZoom = e.world.CorrectCameraZoom(e.wp.CameraZoom, e.wp.ViewportWidthPx, e.wp.ViewportHeightPx)
|
||||||
e.world.IndexOnViewportChange(e.wp.ViewportWidthPx, e.wp.ViewportHeightPx, e.wp.CameraZoom)
|
e.world.IndexOnViewportChange(e.wp.ViewportWidthPx, e.wp.ViewportHeightPx, e.wp.CameraZoom)
|
||||||
e.world.ClampRenderParamsNoWrap(e.wp)
|
e.world.ClampRenderParamsNoWrap(e.wp)
|
||||||
e.co.Request(*e.wp)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,6 +176,9 @@ func (e *editor) onDradEnd() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (e *editor) onTapped(ev *fyne.PointEvent) {
|
func (e *editor) onTapped(ev *fyne.PointEvent) {
|
||||||
|
if e.world == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
hits, err := e.world.HitTest(e.hits, e.wp, int(ev.Position.X*e.canvasScale), int(ev.Position.Y*e.canvasScale))
|
hits, err := e.world.HitTest(e.hits, e.wp, int(ev.Position.X*e.canvasScale), int(ev.Position.Y*e.canvasScale))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@@ -193,34 +197,68 @@ func (e *editor) onTapped(ev *fyne.PointEvent) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *editor) InitImage() {
|
|
||||||
s := fyne.NewSize(292, 292)
|
|
||||||
e.canvas.SetMinSize(s)
|
|
||||||
e.updateSizes()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *editor) onMapLayout(s fyne.Size) {
|
func (e *editor) onMapLayout(s fyne.Size) {
|
||||||
e.updateSizes()
|
e.updateSizes()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *editor) BuildUI(w fyne.Window) {
|
func (e *editor) BuildUI(w fyne.Window) {
|
||||||
e.win = w
|
e.win = w
|
||||||
content := container.New(layout.NewStackLayout(), e.buildUI())
|
|
||||||
|
mapCanvas := newInteractiveRaster(e, e.raster, e.onMapLayout, e.onScrolled, e.onDragged, e.onDradEnd, e.onTapped)
|
||||||
|
mapCanvas.SetMinSize(fyne.NewSize(292, 292))
|
||||||
|
|
||||||
|
toolbar := widget.NewToolbar(
|
||||||
|
widget.NewToolbarAction(
|
||||||
|
theme.FolderIcon(),
|
||||||
|
func() { e.loadWorld(mockWorld()) }),
|
||||||
|
widget.NewToolbarSeparator(),
|
||||||
|
widget.NewToolbarAction(
|
||||||
|
theme.NavigateBackIcon(),
|
||||||
|
func() {}),
|
||||||
|
widget.NewToolbarAction(
|
||||||
|
theme.NavigateNextIcon(),
|
||||||
|
func() {}),
|
||||||
|
)
|
||||||
|
|
||||||
|
tabs := container.NewAppTabs(
|
||||||
|
container.NewTabItemWithIcon(
|
||||||
|
"Map",
|
||||||
|
theme.GridIcon(),
|
||||||
|
mapCanvas),
|
||||||
|
container.NewTabItemWithIcon(
|
||||||
|
"Calculator",
|
||||||
|
theme.ComputerIcon(),
|
||||||
|
container.NewStack(widget.NewButton("Calc", func() {})),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
content := container.NewBorder(
|
||||||
|
toolbar, // top
|
||||||
|
nil, // bottom
|
||||||
|
nil, // left
|
||||||
|
nil, // right
|
||||||
|
tabs, // center
|
||||||
|
)
|
||||||
|
|
||||||
w.CenterOnScreen()
|
w.CenterOnScreen()
|
||||||
w.SetContent(content)
|
w.SetContent(content)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *editor) loadWorld(w *world.World) {
|
||||||
|
e.world = w
|
||||||
|
// TODO: store camera position in user settings
|
||||||
|
e.wp.CameraXWorldFp = w.W / 2
|
||||||
|
e.wp.CameraYWorldFp = w.H / 2
|
||||||
|
e.updateSizes()
|
||||||
|
e.RequestRefresh()
|
||||||
|
}
|
||||||
|
|
||||||
func NewEditor() *editor {
|
func NewEditor() *editor {
|
||||||
w := world.NewWorld(300, 300)
|
|
||||||
testWorldInit(w)
|
|
||||||
e := &editor{
|
e := &editor{
|
||||||
world: w,
|
world: nil,
|
||||||
wp: &world.RenderParams{
|
wp: &world.RenderParams{
|
||||||
CameraZoom: 1.0,
|
CameraZoom: 1.0,
|
||||||
CameraXWorldFp: w.W / 2,
|
Options: &world.RenderOptions{DisableWrapScroll: false},
|
||||||
CameraYWorldFp: w.H / 2,
|
|
||||||
// Viewport sizes and margins will be filled from draw(w,h).
|
|
||||||
Options: &world.RenderOptions{DisableWrapScroll: false},
|
|
||||||
},
|
},
|
||||||
canvasScale: 1.0,
|
canvasScale: 1.0,
|
||||||
hits: make([]world.Hit, 5),
|
hits: make([]world.Hit, 5),
|
||||||
@@ -234,7 +272,6 @@ func NewEditor() *editor {
|
|||||||
return e.draw(wPx, hPx)
|
return e.draw(wPx, hPx)
|
||||||
})
|
})
|
||||||
|
|
||||||
e.canvas = newInteractiveRaster(e, e.raster, e.onMapLayout, e.onScrolled, e.onDragged, e.onDradEnd, e.onTapped)
|
|
||||||
e.pan = NewPanController(e)
|
e.pan = NewPanController(e)
|
||||||
|
|
||||||
// Wire coalescer: it schedules raster.Refresh() on UI thread and renders once per draw call.
|
// Wire coalescer: it schedules raster.Refresh() on UI thread and renders once per draw call.
|
||||||
@@ -251,11 +288,16 @@ func NewEditor() *editor {
|
|||||||
// Kick initial draw.
|
// Kick initial draw.
|
||||||
e.RequestRefresh()
|
e.RequestRefresh()
|
||||||
|
|
||||||
e.InitImage()
|
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
func testWorldInit(w *world.World) {
|
func mockWorld() *world.World {
|
||||||
|
w := world.NewWorld(300, 300)
|
||||||
|
mockWorldInit(w)
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
|
func mockWorldInit(w *world.World) {
|
||||||
lineStyle := w.AddStyleLine(world.StyleOverride{
|
lineStyle := w.AddStyleLine(world.StyleOverride{
|
||||||
StrokeColor: color.RGBA{R: 0, G: 255, B: 0, A: 255},
|
StrokeColor: color.RGBA{R: 0, G: 255, B: 0, A: 255},
|
||||||
StrokeWidthPx: new(3.0),
|
StrokeWidthPx: new(3.0),
|
||||||
@@ -283,10 +325,6 @@ func testWorldInit(w *world.World) {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// if _, err := w.AddLine(100, 20, 200, 30); err != nil {
|
|
||||||
// panic(err)
|
|
||||||
// }
|
|
||||||
|
|
||||||
if _, err := w.AddLine(100, 20, 200, 30, world.LineWithStyleID(lineStyle), world.LineWithPriority(500)); err != nil {
|
if _, err := w.AddLine(100, 20, 200, 30, world.LineWithStyleID(lineStyle), world.LineWithPriority(500)); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-15
@@ -4,8 +4,6 @@ import (
|
|||||||
"image"
|
"image"
|
||||||
|
|
||||||
"fyne.io/fyne/v2"
|
"fyne.io/fyne/v2"
|
||||||
// adjust import path to your world package
|
|
||||||
// "your/module/world"
|
|
||||||
"github.com/fogleman/gg"
|
"github.com/fogleman/gg"
|
||||||
"github.com/iliadenisov/galaxy/client/world"
|
"github.com/iliadenisov/galaxy/client/world"
|
||||||
)
|
)
|
||||||
@@ -30,6 +28,10 @@ This adapter enforces:
|
|||||||
- IndexOnViewportChange is called when viewport sizes changed (you can also include zoom if desired)
|
- IndexOnViewportChange is called when viewport sizes changed (you can also include zoom if desired)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
var (
|
||||||
|
blankImage = image.NewRGBA(image.Rect(0, 0, 0, 0))
|
||||||
|
)
|
||||||
|
|
||||||
// FyneExecutor posts functions onto the Fyne UI thread.
|
// FyneExecutor posts functions onto the Fyne UI thread.
|
||||||
type FyneExecutor struct{}
|
type FyneExecutor struct{}
|
||||||
|
|
||||||
@@ -76,25 +78,15 @@ func (e *editor) RequestRefresh() {
|
|||||||
// draw is the raster callback. It must be cheap and must not block on multiple re-renders.
|
// draw is the raster callback. It must be cheap and must not block on multiple re-renders.
|
||||||
// It delegates coalescing + rendering decision to RasterCoalescer.
|
// It delegates coalescing + rendering decision to RasterCoalescer.
|
||||||
func (e *editor) draw(wPx, hPx int) image.Image {
|
func (e *editor) draw(wPx, hPx int) image.Image {
|
||||||
// Snapshot latest params and render once.
|
|
||||||
// e.mu.RLock()
|
|
||||||
// p := e.wp
|
|
||||||
// e.mu.RUnlock()
|
|
||||||
|
|
||||||
// Request() already scheduled refreshes; Draw() actually renders for this callback.
|
|
||||||
// We bypass co.Draw(w,h) because we need to pass our snapshot to coalescer in a controlled way.
|
|
||||||
// The simplest pattern: keep coalescer as the sole driver: call co.Draw(w,h) here.
|
|
||||||
// But then coalescer uses its internal latest. So make sure we always call co.Request on updates.
|
|
||||||
//
|
|
||||||
// In normal operation you can just: return e.co.Draw(wPx,hPx)
|
|
||||||
// and never use p above. We'll do that to keep a single source of truth.
|
|
||||||
// _ = p
|
|
||||||
return e.co.Draw(wPx, hPx)
|
return e.co.Draw(wPx, hPx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// renderRasterImage renders the expanded canvas into the GGDrawer backing image,
|
// renderRasterImage renders the expanded canvas into the GGDrawer backing image,
|
||||||
// then copies only the viewport ROI into a reusable viewport buffer and returns it.
|
// then copies only the viewport ROI into a reusable viewport buffer and returns it.
|
||||||
func (e *editor) renderRasterImage(viewportW, viewportH int, p world.RenderParams) image.Image {
|
func (e *editor) renderRasterImage(viewportW, viewportH int, p world.RenderParams) image.Image {
|
||||||
|
if e.world == nil {
|
||||||
|
return blankImage
|
||||||
|
}
|
||||||
// 1) Viewport sizes come from raster draw callback.
|
// 1) Viewport sizes come from raster draw callback.
|
||||||
p.ViewportWidthPx = viewportW
|
p.ViewportWidthPx = viewportW
|
||||||
p.ViewportHeightPx = viewportH
|
p.ViewportHeightPx = viewportH
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
package client
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"fyne.io/fyne/v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
func PrintSize(c fyne.Canvas) {
|
|
||||||
if c == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fmt.Println(c.Size())
|
|
||||||
}
|
|
||||||
@@ -20,7 +20,6 @@ func (r *rasterWidgetRender) Layout(size fyne.Size) {
|
|||||||
if r.onLayout != nil {
|
if r.onLayout != nil {
|
||||||
r.onLayout(size)
|
r.onLayout(size)
|
||||||
}
|
}
|
||||||
// fmt.Println("widget layout:", size.Width, size.Height, "raster:", r.canvas.raster.Size())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *rasterWidgetRender) MinSize() fyne.Size {
|
func (r *rasterWidgetRender) MinSize() fyne.Size {
|
||||||
|
|||||||
Reference in New Issue
Block a user