Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(tui): Implement Event-Editor via generic editor #54

Merged
merged 13 commits into from
Jul 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
196 changes: 89 additions & 107 deletions internal/control/cli/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,14 +105,20 @@ func NewController(
"<right>": "move-cursor-rune-right",
"0": "move-cursor-to-beginning",
"$": "move-cursor-to-end",
"w": "move-cursor-to-next-word-beginning",
"b": "move-cursor-to-prev-word-beginning",
"e": "move-cursor-to-next-word-end",
"<ESC>": "quit",
"D": "delete-to-end",
"d$": "delete-to-end",
"d0": "backspace-to-beginning",
"C": "delete-to-end-and-insert",
"c$": "delete-to-end-and-insert",
"c0": "backspace-to-beginning-and-insert",
"x": "delete-rune",
"s": "delete-rune-and-insert",
"i": "swap-mode-insert",
"a": "swap-mode-insert-append",
},
Insert: map[input.Keyspec]input.Actionspec{
"<left>": "move-cursor-rune-left",
Expand Down Expand Up @@ -207,14 +213,7 @@ func NewController(
screenWidth, screenHeight := screenSize()
return 0, 0, screenWidth, screenHeight
}
centeredFloat := func(floatWidth, floatHeight int) func() (x, y, w, h int) {
return func() (x, y, w, h int) {
screenWidth, screenHeight := screenSize()
return (screenWidth / 2) - (floatWidth / 2), (screenHeight / 2) - (floatHeight / 2), floatWidth, floatHeight
}
}
helpDimensions := screenDimensions
editorDimensions := centeredFloat(editorWidth, editorHeight)
tasksDimensions := func() (x, y, w, h int) {
screenWidth, screenHeight := screenSize()
return screenWidth - rightFlexWidth, 0, tasksWidth, screenHeight - statusHeight
Expand All @@ -227,6 +226,12 @@ func NewController(
screenWidth, screenHeight := screenSize()
return 0, screenHeight - statusHeight, screenWidth, statusHeight
}
editorDimensions := func() (x, y, w, h int) {
screenWidth, screenHeight := screenSize()
taskEditorBoxWidth := int(math.Min(float64(editorWidth), float64(screenWidth)))
taskEditorBoxHeight := int(math.Min(float64(editorHeight), float64(screenHeight)))
return (screenWidth / 2) - (taskEditorBoxWidth / 2), (screenHeight / 2) - (taskEditorBoxHeight / 2), taskEditorBoxWidth, taskEditorBoxHeight
}
dayViewMainPaneDimensions := screenDimensions
dayViewScrollablePaneDimensions := func() (x, y, w, h int) {
parentX, parentY, parentW, parentH := dayViewMainPaneDimensions()
Expand Down Expand Up @@ -460,7 +465,7 @@ func NewController(
log.Warn().Msg("apparently, task editor was still active when a new one was activated, unexpected / error")
}
var err error
taskEditor, err := editors.ConstructEditor("root", task, nil, func() (bool, bool) { return true, true })
taskEditor, err := editors.ConstructEditor("root", task, nil, nil)
if err != nil {
log.Error().Err(err).Interface("task", task).Msg("was not able to construct editor for task")
return
Expand All @@ -472,24 +477,21 @@ func NewController(
controller.data.TaskEditor = nil
return
}
taskEditorPane, err := controller.data.TaskEditor.GetPane(
ui.NewConstrainedRenderer(renderer, func() (x, y, w, h int) {
screenWidth, screenHeight := screenSize()
taskEditorBoxWidth := int(math.Min(80, float64(screenWidth)))
taskEditorBoxHeight := int(math.Min(20, float64(screenHeight)))
return (screenWidth / 2) - (taskEditorBoxWidth / 2), (screenHeight / 2) - (taskEditorBoxHeight / 2), taskEditorBoxWidth, taskEditorBoxHeight
}),

taskEditorRenderer := ui.NewConstrainedRenderer(renderer, editorDimensions)

taskEditorPane, err := panes.NewCompositeEditorPane(
taskEditorRenderer,
cursorWrangler,
func() bool { return true },
inputConfig,
stylesheet,
cursorWrangler,
controller.data.TaskEditor,
)
if err != nil {
log.Error().Err(err).Msgf("could not construct task editor pane")
controller.data.TaskEditor = nil
return
log.Fatal().Err(err).Msg("could not construct task editor pane (this is likely a serious programming error / omission)")
}
log.Info().Str("info", taskEditorPane.(*panes.CompositeEditorPane).GetDebugInfo()).Msg("here is the debug info for the task editor pane")

controller.rootPane.PushSubpane(taskEditorPane)
taskEditorDone := make(chan struct{})
controller.data.TaskEditor.AddQuitCallback(func() {
Expand Down Expand Up @@ -716,7 +718,6 @@ func NewController(
}
}
var startMovePushing func()
var pushEditorAsRootSubpane func()
// TODO: directly?
eventsPaneDayInputExtension := map[input.Keyspec]action.Action{
"j": action.NewSimple(func() string { return "switch to next event" }, func() {
Expand All @@ -741,10 +742,50 @@ func NewController(
}),
"<cr>": action.NewSimple(func() string { return "open the event editor" }, func() {
event := controller.data.GetCurrentDay().Current
if event != nil {
controller.data.EventEditor.Activate(event)
if event == nil {
log.Warn().Msgf("ignoring event editing request since no current event selected")
return
}
pushEditorAsRootSubpane()

if controller.data.EventEditor != nil {
log.Warn().Msgf("was about to construct new event editor but still have old one")
return
}
newEventEditor, err := editors.ConstructEditor("event", event, nil, nil)
if err != nil {
log.Warn().Err(err).Msgf("unable to construct event editor")
return
}
var ok bool
controller.data.EventEditor, ok = newEventEditor.(*editors.Composite)
if !ok {
log.Error().Msgf("something went _really_ wrong and the editor constructed for the event is _not_ a composite editor but a %T", newEventEditor)
controller.data.EventEditor = nil
return
}

eventEditorRenderer := ui.NewConstrainedRenderer(renderer, editorDimensions)
eventEditorPane, err := panes.NewCompositeEditorPane(
eventEditorRenderer,
cursorWrangler,
func() bool { return true },
inputConfig,
stylesheet,
controller.data.EventEditor,
)
if err != nil {
log.Fatal().Err(err).Msg("could not construct event editor pane (this is likely a serious programming error / omission)")
}

controller.rootPane.PushSubpane(eventEditorPane)
eventEditorDone := make(chan struct{})
controller.data.EventEditor.AddQuitCallback(func() {
close(eventEditorDone) // TODO: this can CERTAINLY happen twice; prevent
})
go func() {
<-eventEditorDone
controller.controllerEvents <- ControllerEventEventEditorExit
}()
}),
"o": action.NewSimple(func() string { return "add event after selected" }, func() {
current := controller.data.GetCurrentDay().Current
Expand Down Expand Up @@ -1302,61 +1343,6 @@ func NewController(
stderrLogger.Fatal().Err(err).Msg("failed to construct input tree for summary pane")
}

var editorStartInsertMode func()
var editorLeaveInsertMode func()
editorInsertMode, err := processors.NewTextInputProcessor( // TODO rename
map[input.Keyspec]action.Action{
"<ESC>": action.NewSimple(func() string { return "exit insert mode" }, func() { editorLeaveInsertMode() }),
"<c-a>": action.NewSimple(func() string { return "move cursor to beginning" }, controller.data.EventEditor.MoveCursorToBeginning),
"<del>": action.NewSimple(func() string { return "delete character" }, controller.data.EventEditor.DeleteRune),
"<c-d>": action.NewSimple(func() string { return "delete character" }, controller.data.EventEditor.DeleteRune),
"<c-bs>": action.NewSimple(func() string { return "backspace" }, controller.data.EventEditor.BackspaceRune),
"<bs>": action.NewSimple(func() string { return "backspace" }, controller.data.EventEditor.BackspaceRune),
"<c-e>": action.NewSimple(func() string { return "move cursor to end" }, controller.data.EventEditor.MoveCursorToEnd),
"<c-u>": action.NewSimple(func() string { return "backspace to beginning" }, controller.data.EventEditor.BackspaceToBeginning),
"<left>": action.NewSimple(func() string { return "move cursor left" }, controller.data.EventEditor.MoveCursorLeft),
"<right>": action.NewSimple(func() string { return "move cursor right" }, controller.data.EventEditor.MoveCursorRight),
},
controller.data.EventEditor.AddRune,
)
if err != nil {
log.Fatal().Err(err).Msgf("could not construct editor insert mode processor")
}
editorNormalModeTree, err := input.ConstructInputTree(
map[input.Keyspec]action.Action{
"<esc>": action.NewSimple(func() string { return "abord edit, discard changes" }, controller.abortEdit),
"<cr>": action.NewSimple(func() string { return "finish edit, commit changes" }, controller.endEdit),
"i": action.NewSimple(func() string { return "enter insert mode" }, func() { editorStartInsertMode() }),
"a": action.NewSimple(func() string { return "enter insert mode (after character)" }, func() {
controller.data.EventEditor.MoveCursorRightA()
editorStartInsertMode()
}),
"A": action.NewSimple(func() string { return "enter insert mode (at end)" }, func() {
controller.data.EventEditor.MoveCursorPastEnd()
editorStartInsertMode()
}),
"0": action.NewSimple(func() string { return "move cursor to beginning" }, controller.data.EventEditor.MoveCursorToBeginning),
"$": action.NewSimple(func() string { return "move cursor to end" }, controller.data.EventEditor.MoveCursorToEnd),
"h": action.NewSimple(func() string { return "move cursor left" }, controller.data.EventEditor.MoveCursorLeft),
"l": action.NewSimple(func() string { return "move cursor right" }, controller.data.EventEditor.MoveCursorRight),
"w": action.NewSimple(func() string { return "move cursor to next word beginning" }, controller.data.EventEditor.MoveCursorNextWordBeginning),
"b": action.NewSimple(func() string { return "move cursor to previous word beginning" }, controller.data.EventEditor.MoveCursorPrevWordBeginning),
"e": action.NewSimple(func() string { return "move cursor to next word end" }, controller.data.EventEditor.MoveCursorNextWordEnd),
"x": action.NewSimple(func() string { return "delete character" }, controller.data.EventEditor.DeleteRune),
"C": action.NewSimple(func() string { return "delete to end" }, func() {
controller.data.EventEditor.DeleteToEnd()
editorStartInsertMode()
}),
"dd": action.NewSimple(func() string { return "clear text content" }, func() { controller.data.EventEditor.Clear() }),
"cc": action.NewSimple(func() string { return "clear text content, enter insert" }, func() {
controller.data.EventEditor.Clear()
editorStartInsertMode()
}),
},
)
if err != nil {
stderrLogger.Fatal().Err(err).Msg("failed to construct input tree for editor pane's normal mode")
}
helpPaneInputTree, err := input.ConstructInputTree(
map[input.Keyspec]action.Action{
"?": action.NewSimple(func() string { return "close help" }, func() {
Expand All @@ -1374,26 +1360,6 @@ func NewController(
func() bool { return controller.data.ShowHelp },
processors.NewModalInputProcessor(helpPaneInputTree),
)
editorPane := panes.NewEventEditorPane(
ui.NewConstrainedRenderer(renderer, editorDimensions),
cursorWrangler,
editorDimensions,
stylesheet,
func() bool { return controller.data.EventEditor.Active },
func() string { return controller.data.EventEditor.TmpEventInfo.Name },
controller.data.EventEditor.GetMode,
func() int { return controller.data.EventEditor.CursorPos },
processors.NewModalInputProcessor(editorNormalModeTree),
)
pushEditorAsRootSubpane = func() { controller.rootPane.PushSubpane(editorPane) }
editorStartInsertMode = func() {
editorPane.ApplyModalOverlay(editorInsertMode)
controller.data.EventEditor.SetMode(input.TextEditModeInsert)
}
editorLeaveInsertMode = func() {
editorPane.PopModalOverlay()
controller.data.EventEditor.SetMode(input.TextEditModeNormal)
}

rootPane := panes.NewRootPane(
renderer,
Expand Down Expand Up @@ -1489,7 +1455,6 @@ func NewController(
helpPane.Content = rootPane.GetHelp()
}

controller.data.EventEditor.SetMode(input.TextEditModeNormal)
controller.data.EventEditMode = edit.EventEditModeNormal

coordinatesProvided := (envData.Latitude != "" && envData.Longitude != "")
Expand Down Expand Up @@ -1589,20 +1554,23 @@ func (t *Controller) ScrollBottom() {
func (t *Controller) abortEdit() {
t.data.MouseEditState = edit.MouseEditStateNone
t.data.MouseEditedEvent = nil
t.data.EventEditor.Active = false
if t.data.EventEditor != nil {
t.data.EventEditor.Quit()
t.data.EventEditor = nil
}
t.rootPane.PopSubpane()
}

func (t *Controller) endEdit() {
t.data.MouseEditState = edit.MouseEditStateNone
t.data.MouseEditedEvent = nil
if t.data.EventEditor.Active {
t.data.EventEditor.Active = false
tmp := t.data.EventEditor.TmpEventInfo
t.data.EventEditor.Original.Name = tmp.Name
if t.data.EventEditor != nil {
t.data.EventEditor.Write()
t.data.EventEditor.Quit()
t.data.EventEditor = nil
}
t.data.GetCurrentDay().UpdateEventOrder()
t.rootPane.PopSubpane()
t.rootPane.PopSubpane() // TODO: this will need to be re-done conceptually
}

func (t *Controller) startMouseMove(eventsInfo *ui.EventsPanePositionInfo) {
Expand Down Expand Up @@ -1788,7 +1756,7 @@ func (t *Controller) handleMouseNoneEditEvent(e *tcell.EventMouse) {
case ui.EventBoxInterior:
t.startMouseMove(eventsInfo)
case ui.EventBoxTopEdge:
t.data.EventEditor.Activate(eventsInfo.Event)
log.Info().Msgf("would construct editor here, once the programmer has figured out how to do so correctly")
}

case tcell.WheelUp:
Expand Down Expand Up @@ -1876,6 +1844,7 @@ const (
ControllerEventExit ControllerEvent = iota
ControllerEventRender
ControllerEventTaskEditorExit
ControllerEventEventEditorExit
)

// Empties all render events from the channel.
Expand Down Expand Up @@ -1941,8 +1910,21 @@ func (t *Controller) Run() {
go func() { t.controllerEvents <- ControllerEventRender }()
}

case ControllerEventEventEditorExit:
if t.data.EventEditor == nil {
log.Warn().Msgf("got event editor exit event, but no event editor active; likely logic error")
} else {
t.data.EventEditor = nil
t.rootPane.PopSubpane()
log.Debug().Msgf("removed (presumed) event-editor subpane from root")
go func() { t.controllerEvents <- ControllerEventRender }()
}

case ControllerEventExit:
return

default:
log.Error().Interface("event", controllerEvent).Msgf("unhandled controller event")
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion internal/control/data.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ type ControlData struct {
CurrentDate model.Date
Weather weather.Handler

EventEditor editors.EventEditor
EventEditor *editors.Composite
TaskEditor *editors.Composite

ShowLog bool
Expand Down
Loading
Loading