Move logic into separate internal/editor package
- Create internal/editor/state.go with State struct and EditorLayout - Create internal/editor/logic.go with Logic struct and Run method - Logic owns all state, provides channels for config, frames, input, results - Main goroutine uses editor.NewLogic() and logic.Run() - Proper separation for testing later Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
parent
556f72643f
commit
013416ee08
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"gioui.org/text"
|
"gioui.org/text"
|
||||||
"gioui.org/unit"
|
"gioui.org/unit"
|
||||||
|
|
||||||
|
"pad/internal/editor"
|
||||||
"pad/internal/ui"
|
"pad/internal/ui"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -32,27 +33,22 @@ func run(w *app.Window) error {
|
||||||
shaper := text.NewShaper()
|
shaper := text.NewShaper()
|
||||||
renderer := ui.New(ui.Theme{FontSize: 14}, shaper)
|
renderer := ui.New(ui.Theme{FontSize: 14}, shaper)
|
||||||
|
|
||||||
// Channels for communication between goroutines
|
// Initial screen size
|
||||||
configChan := make(chan ui.ConfigEvent)
|
screenWidth := unit.Dp(390)
|
||||||
frameChan := make(chan []ui.Element)
|
screenHeight := unit.Dp(844)
|
||||||
inputChan := make(chan []ui.InputEvent)
|
|
||||||
|
// Create logic instance
|
||||||
|
logic := editor.NewLogic(screenWidth, screenHeight)
|
||||||
|
|
||||||
// Shared state protected by mutex
|
// Shared state protected by mutex
|
||||||
var mu sync.Mutex
|
var mu sync.Mutex
|
||||||
var elems []ui.Element
|
var elems []ui.Element
|
||||||
|
|
||||||
// Initial screen size
|
|
||||||
screenWidth := unit.Dp(390)
|
|
||||||
screenHeight := unit.Dp(844)
|
|
||||||
|
|
||||||
// Compute initial elements
|
|
||||||
elems = ui.EditorLayout(screenWidth, screenHeight)
|
|
||||||
|
|
||||||
// Start logic goroutine
|
// Start logic goroutine
|
||||||
go logic(w, shaper, screenWidth, screenHeight, configChan, frameChan, inputChan)
|
go logic.Run()
|
||||||
|
|
||||||
// Start frame receiver goroutine
|
// Start frame receiver goroutine
|
||||||
go frameReceiver(w, &mu, &elems, frameChan)
|
go frameReceiver(w, &mu, &elems, logic.FrameChan())
|
||||||
|
|
||||||
// Main event loop
|
// Main event loop
|
||||||
for {
|
for {
|
||||||
|
|
@ -61,7 +57,7 @@ func run(w *app.Window) error {
|
||||||
return e.Err
|
return e.Err
|
||||||
case app.ConfigEvent:
|
case app.ConfigEvent:
|
||||||
// Send config event to logic goroutine
|
// Send config event to logic goroutine
|
||||||
configChan <- ui.ConfigEvent{
|
logic.ConfigChan() <- editor.ConfigEvent{
|
||||||
Width: unit.Dp(e.Config.Size.X),
|
Width: unit.Dp(e.Config.Size.X),
|
||||||
Height: unit.Dp(e.Config.Size.Y),
|
Height: unit.Dp(e.Config.Size.Y),
|
||||||
}
|
}
|
||||||
|
|
@ -78,28 +74,6 @@ func run(w *app.Window) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// logic runs in a separate goroutine and owns all application state.
|
|
||||||
func logic(w *app.Window, shaper *text.Shaper, screenWidth, screenHeight unit.Dp,
|
|
||||||
configChan <-chan ui.ConfigEvent, frameChan chan<- []ui.Element, inputChan <-chan []ui.InputEvent) {
|
|
||||||
|
|
||||||
// Initial elements
|
|
||||||
elems := ui.EditorLayout(screenWidth, screenHeight)
|
|
||||||
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case cfg := <-configChan:
|
|
||||||
// Update screen size and recompute layout
|
|
||||||
screenWidth = cfg.Width
|
|
||||||
screenHeight = cfg.Height
|
|
||||||
elems = ui.EditorLayout(screenWidth, screenHeight)
|
|
||||||
frameChan <- elems
|
|
||||||
case <-inputChan:
|
|
||||||
// Process input (not implemented in mockup)
|
|
||||||
frameChan <- elems
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// frameReceiver runs in a separate goroutine and bridges frameChan to the main loop.
|
// frameReceiver runs in a separate goroutine and bridges frameChan to the main loop.
|
||||||
func frameReceiver(w *app.Window, mu *sync.Mutex, elems *[]ui.Element, frameChan <-chan []ui.Element) {
|
func frameReceiver(w *app.Window, mu *sync.Mutex, elems *[]ui.Element, frameChan <-chan []ui.Element) {
|
||||||
for {
|
for {
|
||||||
|
|
|
||||||
90
internal/editor/logic.go
Normal file
90
internal/editor/logic.go
Normal file
|
|
@ -0,0 +1,90 @@
|
||||||
|
package editor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gioui.org/unit"
|
||||||
|
|
||||||
|
"pad/internal/ui"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ConfigEvent represents a window configuration change (resize, orientation).
|
||||||
|
type ConfigEvent struct {
|
||||||
|
Width unit.Dp
|
||||||
|
Height unit.Dp
|
||||||
|
}
|
||||||
|
|
||||||
|
// InputEvent represents a user input event routed to an element.
|
||||||
|
type InputEvent struct {
|
||||||
|
ElementID string
|
||||||
|
Type ui.InputType
|
||||||
|
Data any
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResultEvent represents a completed async task result.
|
||||||
|
type ResultEvent struct {
|
||||||
|
// Future: add result fields here
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logic runs the logic goroutine and provides channels for communication.
|
||||||
|
type Logic struct {
|
||||||
|
state *State
|
||||||
|
configChan chan ConfigEvent
|
||||||
|
frameChan chan []ui.Element
|
||||||
|
inputChan chan []InputEvent
|
||||||
|
resultChan chan ResultEvent
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLogic creates a new Logic instance.
|
||||||
|
func NewLogic(screenWidth, screenHeight unit.Dp) *Logic {
|
||||||
|
return &Logic{
|
||||||
|
state: NewState(screenWidth, screenHeight),
|
||||||
|
configChan: make(chan ConfigEvent),
|
||||||
|
frameChan: make(chan []ui.Element),
|
||||||
|
inputChan: make(chan []InputEvent),
|
||||||
|
resultChan: make(chan ResultEvent),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConfigChan returns the config channel for the logic goroutine.
|
||||||
|
func (l *Logic) ConfigChan() chan<- ConfigEvent {
|
||||||
|
return l.configChan
|
||||||
|
}
|
||||||
|
|
||||||
|
// FrameChan returns the frame channel for the logic goroutine.
|
||||||
|
func (l *Logic) FrameChan() <-chan []ui.Element {
|
||||||
|
return l.frameChan
|
||||||
|
}
|
||||||
|
|
||||||
|
// InputChan returns the input channel for the logic goroutine.
|
||||||
|
func (l *Logic) InputChan() chan<- []InputEvent {
|
||||||
|
return l.inputChan
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResultChan returns the result channel for the logic goroutine.
|
||||||
|
func (l *Logic) ResultChan() <-chan ResultEvent {
|
||||||
|
return l.resultChan
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run runs the logic goroutine loop.
|
||||||
|
func (l *Logic) Run() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case cfg := <-l.configChan:
|
||||||
|
// Update screen size and recompute layout
|
||||||
|
l.state.ScreenWidth = cfg.Width
|
||||||
|
l.state.ScreenHeight = cfg.Height
|
||||||
|
l.state.Elems = EditorLayout(cfg.Width, cfg.Height)
|
||||||
|
l.frameChan <- l.state.Elems
|
||||||
|
case <-l.inputChan:
|
||||||
|
// Process input (not implemented in mockup)
|
||||||
|
l.frameChan <- l.state.Elems
|
||||||
|
case <-l.resultChan:
|
||||||
|
// Handle result (not implemented in mockup)
|
||||||
|
l.frameChan <- l.state.Elems
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// State returns the current state.
|
||||||
|
func (l *Logic) State() *State {
|
||||||
|
return l.state
|
||||||
|
}
|
||||||
70
internal/editor/state.go
Normal file
70
internal/editor/state.go
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
package editor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gioui.org/unit"
|
||||||
|
|
||||||
|
"pad/internal/ui"
|
||||||
|
)
|
||||||
|
|
||||||
|
// State holds all application state owned by the logic goroutine.
|
||||||
|
type State struct {
|
||||||
|
ScreenWidth unit.Dp
|
||||||
|
ScreenHeight unit.Dp
|
||||||
|
Elems []ui.Element
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewState creates a new State with initial editor layout.
|
||||||
|
func NewState(screenWidth, screenHeight unit.Dp) *State {
|
||||||
|
return &State{
|
||||||
|
ScreenWidth: screenWidth,
|
||||||
|
ScreenHeight: screenHeight,
|
||||||
|
Elems: EditorLayout(screenWidth, screenHeight),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditorLayout computes regions for the editor page.
|
||||||
|
// It takes screen dimensions and returns []Element.
|
||||||
|
func EditorLayout(screenWidth, screenHeight unit.Dp) []ui.Element {
|
||||||
|
// Compute StatusBar region
|
||||||
|
// Line 1: filename (24 DP)
|
||||||
|
// Line 2: icons (24 DP)
|
||||||
|
statusBarHeight := unit.Dp(48)
|
||||||
|
statusBarRegion := ui.Region{
|
||||||
|
X: 0,
|
||||||
|
Y: 0,
|
||||||
|
W: screenWidth,
|
||||||
|
H: statusBarHeight,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute BottomBar region (fixed height, at bottom of screen)
|
||||||
|
bottomBarHeight := unit.Dp(24)
|
||||||
|
bottomBarY := screenHeight - bottomBarHeight
|
||||||
|
bottomBarRegion := ui.Region{
|
||||||
|
X: 0,
|
||||||
|
Y: bottomBarY,
|
||||||
|
W: screenWidth,
|
||||||
|
H: bottomBarHeight,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create StatusBar with mocked filename
|
||||||
|
statusBar := ui.NewStatusBar(
|
||||||
|
statusBarRegion,
|
||||||
|
"very_long_filename_that_does_not_fit_on_a_single_line.txt",
|
||||||
|
false, // FilenameExp
|
||||||
|
true, // CutCopy
|
||||||
|
false, // Copy
|
||||||
|
true, // Paste
|
||||||
|
false, // ConflictIcon
|
||||||
|
true, // Search
|
||||||
|
)
|
||||||
|
|
||||||
|
// Create BottomBar with mocked data
|
||||||
|
bottomBar := ui.NewBottomBar(
|
||||||
|
bottomBarRegion,
|
||||||
|
"Ln 47, Col 12",
|
||||||
|
"1024 / 50000",
|
||||||
|
true, // WordWrap
|
||||||
|
)
|
||||||
|
|
||||||
|
return []ui.Element{statusBar, bottomBar}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user