From 85dc29bf8ad0aaed9e3f2b0889e73f226fdf25e9 Mon Sep 17 00:00:00 2001 From: stk Date: Sat, 11 Mar 2023 18:05:12 +0100 Subject: [PATCH] Implement focus events --- _demos/mouse.go | 12 +++++++++++- focus.go | 28 ++++++++++++++++++++++++++++ tscreen.go | 45 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 focus.go diff --git a/_demos/mouse.go b/_demos/mouse.go index 394808f..7b41238 100644 --- a/_demos/mouse.go +++ b/_demos/mouse.go @@ -135,6 +135,7 @@ func main() { btnfmt := "Buttons: %s" keyfmt := "Keys: %s" pastefmt := "Paste: [%d] %s" + focusfmt := "Focus: %s" white := tcell.StyleDefault. Foreground(tcell.ColorMidnightBlue).Background(tcell.ColorLightCoral) @@ -148,9 +149,10 @@ func main() { pstr := "" ecnt := 0 pasting := false + focus := true // assume we are focused when we start for { - drawBox(s, 1, 1, 42, 7, white, ' ') + drawBox(s, 1, 1, 42, 8, white, ' ') emitStr(s, 2, 2, white, "Press ESC twice to exit, C to clear.") emitStr(s, 2, 3, white, fmt.Sprintf(posfmt, mx, my)) emitStr(s, 2, 4, white, fmt.Sprintf(btnfmt, bstr)) @@ -162,6 +164,12 @@ func main() { } emitStr(s, 2, 6, white, fmt.Sprintf(pastefmt, len(pstr), ps)) + fstr := "false" + if focus { + fstr = "true" + } + emitStr(s, 2, 7, white, fmt.Sprintf(focusfmt, fstr)) + s.Show() bstr = "" ev := s.PollEvent() @@ -315,6 +323,8 @@ func main() { lchar = ch s.SetContent(w-1, h-1, 'M', nil, st) mx, my = x, y + case *tcell.EventFocus: + focus = ev.Focused default: s.SetContent(w-1, h-1, 'X', nil, st) } diff --git a/focus.go b/focus.go new file mode 100644 index 0000000..e9b93ef --- /dev/null +++ b/focus.go @@ -0,0 +1,28 @@ +// Copyright 2023 The TCell Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use file except in compliance with the License. +// You may obtain a copy of the license at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tcell + +// EventFocus is a focus event. It is sent when the terminal window (or tab) +// gets or loses focus. +type EventFocus struct { + *EventTime + + // True if the window received focus, false if it lost focus + Focused bool +} + +func NewEventFocus(focused bool) *EventFocus { + return &EventFocus{Focused: focused} +} diff --git a/tscreen.go b/tscreen.go index 8fc8866..78f3975 100644 --- a/tscreen.go +++ b/tscreen.go @@ -1043,6 +1043,14 @@ func (t *tScreen) enablePasting(on bool) { } } +func (t *tScreen) enableFocusReporting() { + t.TPuts("\x1b[?1004h") +} + +func (t *tScreen) disableFocusReporting() { + t.TPuts("\x1b[?1004l") +} + func (t *tScreen) Size() (int, int) { t.Lock() w, h := t.w, t.h @@ -1382,6 +1390,35 @@ func (t *tScreen) parseSgrMouse(buf *bytes.Buffer, evs *[]Event) (bool, bool) { return true, false } +func (t *tScreen) parseFocus(buf *bytes.Buffer, evs *[]Event) (bool, bool) { + state := 0 + b := buf.Bytes() + for i := range b { + switch state { + case 0: + if b[i] != '\x1b' { + return false, false + } + state = 1 + case 1: + if b[i] != '[' { + return false, false + } + state = 2 + case 2: + if b[i] != 'I' && b[i] != 'O' { + return false, false + } + *evs = append(*evs, NewEventFocus(b[i] == 'I')) + _, _ = buf.ReadByte() + _, _ = buf.ReadByte() + _, _ = buf.ReadByte() + return true, true + } + } + return true, false +} + // parseXtermMouse is like parseSgrMouse, but it parses a legacy // X11 mouse record. func (t *tScreen) parseXtermMouse(buf *bytes.Buffer, evs *[]Event) (bool, bool) { @@ -1558,6 +1595,12 @@ func (t *tScreen) collectEventsFromInput(buf *bytes.Buffer, expire bool) []Event partials++ } + if part, comp := t.parseFocus(buf, &res); comp { + continue + } else if part { + partials++ + } + // Only parse mouse records if this term claims to have // mouse support @@ -1806,6 +1849,7 @@ func (t *tScreen) engage() error { t.stopQ = stopQ t.enableMouse(t.mouseFlags) t.enablePasting(t.pasteEnabled) + t.enableFocusReporting() ti := t.ti t.TPuts(ti.EnterCA) @@ -1855,6 +1899,7 @@ func (t *tScreen) disengage() { t.TPuts(ti.ExitKeypad) t.enableMouse(0) t.enablePasting(false) + t.disableFocusReporting() _ = t.tty.Stop() }