diff --git a/cmd/gim/main.go b/cmd/gim/main.go index 521a923..276f763 100644 --- a/cmd/gim/main.go +++ b/cmd/gim/main.go @@ -23,11 +23,13 @@ func main() { prog = program.NewProgramBuilder(). EmptyProgram(). WithOpt(tea.WithAltScreen()). + WithOpt(tea.WithMouseCellMotion()). Build() } else { prog = program.NewProgramBuilder(). FileProgram(args[0]). WithOpt(tea.WithAltScreen()). + WithOpt(tea.WithMouseCellMotion()). Build() } diff --git a/internal/editor/update.go b/internal/editor/update.go index 08d98ac..d747120 100644 --- a/internal/editor/update.go +++ b/internal/editor/update.go @@ -1,83 +1,95 @@ package editor import ( - "git.gophernest.net/azpect/TextEditor/internal/core" - tea "github.com/charmbracelet/bubbletea" + "git.gophernest.net/azpect/TextEditor/internal/core" + "git.gophernest.net/azpect/TextEditor/internal/motion" + tea "github.com/charmbracelet/bubbletea" ) // Model.Update: Handles BubbleTea messages including window resizes and key // presses. Routes input to the handler and adjusts scroll after updates. func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { - var cmd tea.Cmd + var cmd tea.Cmd - switch msg := msg.(type) { + switch msg := msg.(type) { - case tea.WindowSizeMsg: - m.termHeight = msg.Height - m.termWidth = msg.Width + case tea.WindowSizeMsg: + m.termHeight = msg.Height + m.termWidth = msg.Width - // TODO: Implement a layout method that handles this - // - // func (m *Model) layoutWindows() { - // if len(m.windows) == 0 { - // return - // } - // - // if len(m.windows) == 1 { - // // Single window - full screen - // m.windows[0].Width = m.termWidth - // m.windows[0].Height = m.termHeight - // return - // } - // - // // Multiple windows - distribute space - // // This is where you'd implement split layout logic - // // For example, horizontal split: - // halfHeight := m.termHeight / 2 - // for i, win := range m.windows { - // win.Width = m.termWidth - // if i < len(m.windows)-1 { - // win.Height = halfHeight - // } else { - // // Last window gets remainder - // win.Height = m.termHeight - (halfHeight * (len(m.windows) - 1)) - // } - // } - // } - for i := range m.windows { - m.windows[i].Height = msg.Height - m.windows[i].Width = msg.Width - } + // TODO: Implement a layout method that handles this + // + // func (m *Model) layoutWindows() { + // if len(m.windows) == 0 { + // return + // } + // + // if len(m.windows) == 1 { + // // Single window - full screen + // m.windows[0].Width = m.termWidth + // m.windows[0].Height = m.termHeight + // return + // } + // + // // Multiple windows - distribute space + // // This is where you'd implement split layout logic + // // For example, horizontal split: + // halfHeight := m.termHeight / 2 + // for i, win := range m.windows { + // win.Width = m.termWidth + // if i < len(m.windows)-1 { + // win.Height = halfHeight + // } else { + // // Last window gets remainder + // win.Height = m.termHeight - (halfHeight * (len(m.windows) - 1)) + // } + // } + // } + for i := range m.windows { + m.windows[i].Height = msg.Height + m.windows[i].Width = msg.Width + } - case tea.KeyMsg: - // TODO: This needs to be removed, but for now its required for the tests. - // Ctrl+C always quits regardless of mode - if msg.Type == tea.KeyCtrlC { - return m, tea.Quit - } + // TODO: This is not great, totally temporary. But I don't like vim's handling, so this is up to me + case tea.MouseMsg: + switch msg.Button { + case tea.MouseButtonWheelUp: + scrollAction := motion.ScrollUpPage{Divisor: 4} // Quarter page + cmd = scrollAction.Execute(m) + case tea.MouseButtonWheelDown: + scrollAction := motion.ScrollDownPage{Divisor: 4} // Quarter page + cmd = scrollAction.Execute(m) + } - // TODO: This is not great - // TODO: Any vim action should exit also - // Simple override for command output mode for now - if m.Mode() == core.CommandOutputMode { - // TODO: Implement g/G/d/u - switch msg.String() { - case "enter": - m.SetMode(core.NormalMode) - m.SetCommandOutput(&core.CommandOutput{}) - case "j": - m.CommandOutput().ScrollDown(m.termHeight) - case "k": - m.CommandOutput().ScrollUp() - } - } else { - cmd = m.input.Handle(m, msg.String()) - } - } + case tea.KeyMsg: + // TODO: This needs to be removed, but for now its required for the tests. + // Ctrl+C always quits regardless of mode + if msg.Type == tea.KeyCtrlC { + return m, tea.Quit + } - // Keep cursor in view after any update - win := m.ActiveWindow() - win.AdjustScroll() + // TODO: This is not great + // TODO: Any vim action should exit also + // Simple override for command output mode for now + if m.Mode() == core.CommandOutputMode { + // TODO: Implement g/G/d/u + switch msg.String() { + case "enter": + m.SetMode(core.NormalMode) + m.SetCommandOutput(&core.CommandOutput{}) + case "j": + m.CommandOutput().ScrollDown(m.termHeight) + case "k": + m.CommandOutput().ScrollUp() + } + } else { + cmd = m.input.Handle(m, msg.String()) + } + } - return m, cmd + // Keep cursor in view after any update + win := m.ActiveWindow() + win.AdjustScroll() + + return m, cmd } diff --git a/internal/editor/view.go b/internal/editor/view.go index 4e3ee44..7f5a701 100644 --- a/internal/editor/view.go +++ b/internal/editor/view.go @@ -2,6 +2,7 @@ package editor import ( "fmt" + "strconv" "strings" "git.gophernest.net/azpect/TextEditor/internal/core" @@ -22,6 +23,10 @@ func (m Model) View() string { styles := m.Styles() options := win.Options + // Adjust gutter to fit line len + maxLineLen := len(strconv.Itoa(win.Buffer.LineCount())) + options.GutterSize = max(options.GutterSize, maxLineLen+2) + // Draw window view := viewWindow(win, styles, options, m.Mode())