aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--go.mod3
-rw-r--r--go.sum18
-rw-r--r--ui/fuzzy-search-modal.go61
-rw-r--r--ui/message-view.go131
-rw-r--r--ui/messages/imagemessage.go2
-rw-r--r--ui/messages/parser/parser.go24
-rw-r--r--ui/messages/tstring/cell.go3
-rw-r--r--ui/messages/tstring/string.go3
-rw-r--r--ui/room-list.go75
-rw-r--r--ui/room-view.go177
-rw-r--r--ui/tag-room-list.go46
-rw-r--r--ui/ui.go47
-rw-r--r--ui/view-login.go89
-rw-r--r--ui/view-main.go239
-rw-r--r--ui/widget/advanced-inputfield.go562
-rw-r--r--ui/widget/border.go33
-rw-r--r--ui/widget/center.go70
-rw-r--r--ui/widget/form-text-view.go44
-rw-r--r--ui/widget/util.go29
19 files changed, 550 insertions, 1106 deletions
diff --git a/go.mod b/go.mod
index 1934c0d..4f6a8c7 100644
--- a/go.mod
+++ b/go.mod
@@ -11,9 +11,8 @@ require (
github.com/zyedidia/clipboard v0.0.0-20180718195219-bd31d747117d
golang.org/x/image v0.0.0-20181116024801-cd38e8056d9b
golang.org/x/net v0.0.0-20190110200230-915654e7eabc
- gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
gopkg.in/yaml.v2 v2.2.2
maunium.net/go/mautrix v0.1.0-alpha.3
+ maunium.net/go/mauview v0.0.0-20190325223341-4c387be4b686
maunium.net/go/tcell v0.0.0-20190111223412-5e74142cb009
- maunium.net/go/tview v0.0.0-20190111223510-de38190b095b
)
diff --git a/go.sum b/go.sum
index 98f259c..f1bc936 100644
--- a/go.sum
+++ b/go.sum
@@ -10,6 +10,8 @@ github.com/lucasb-eyer/go-colorful v0.0.0-20181028223441-12d3b2882a08 h1:5MnxBC1
github.com/lucasb-eyer/go-colorful v0.0.0-20181028223441-12d3b2882a08/go.mod h1:NXg0ArsFk0Y01623LgUqoqcouGDB+PwCCQlrwrG6xJ4=
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
+github.com/rivo/uniseg v0.0.0-20190313204849-f699dde9c340 h1:nOZbL5f2xmBAHWYrrHbHV1xatzZirN++oOQ3g83Ypgs=
+github.com/rivo/uniseg v0.0.0-20190313204849-f699dde9c340/go.mod h1:SOLvOL4ybwgLJ6TYoX/rtaJ8EGOulH4XU7E9/TLrTCE=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
@@ -18,29 +20,17 @@ github.com/zyedidia/clipboard v0.0.0-20180718195219-bd31d747117d h1:Lhqt2eo+rgM8
github.com/zyedidia/clipboard v0.0.0-20180718195219-bd31d747117d/go.mod h1:WDk3p8GiZV9+xFWlSo8qreeoLhW6Ik692rqXk+cNeRY=
golang.org/x/image v0.0.0-20181116024801-cd38e8056d9b h1:VHyIDlv3XkfCa5/a81uzaoDkHH4rr81Z62g+xlnO8uM=
golang.org/x/image v0.0.0-20181116024801-cd38e8056d9b/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
-golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190110200230-915654e7eabc h1:Yx9JGxI1SBhVLFjpAkWMaO1TF+xyqtHLjZpvQboJGiM=
golang.org/x/net v0.0.0-20190110200230-915654e7eabc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/DATA-DOG/go-sqlmock.v1 v1.3.0/go.mod h1:OdE7CF6DbADk7lN8LIKRzRJTTZXIjtWgA5THM5lhBAw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-maunium.net/go/mautrix v0.1.0-alpha.2 h1:NsLc5tyrp5tyrKTvFSmqcLi+FISQ+FsuWC/ycL08PzI=
-maunium.net/go/mautrix v0.1.0-alpha.2/go.mod h1:C8akEpHpmmO8gQhLvmInr3HujhUXyKvCoCAzFsxHjGE=
maunium.net/go/mautrix v0.1.0-alpha.3 h1:kBz7M63hRetQnAnYK+gVmuSxsmZesX6xERphVgEn324=
maunium.net/go/mautrix v0.1.0-alpha.3/go.mod h1:GTVu6WDHR+98DKOrYetWsXorvUeKQV3jsSWO6ScbuFI=
-maunium.net/go/tcell v0.0.0-20190111210611-542340841245 h1:JIekRWZ4na6cZJa5VMwLFpuiGzmeX+5Xx+WRVOHQHuk=
-maunium.net/go/tcell v0.0.0-20190111210611-542340841245/go.mod h1:U+akxk8CP6vAWV74r2NOqEMMHw6kPGWTyvjzCtemxtM=
-maunium.net/go/tcell v0.0.0-20190111212645-703b3f6ecec9 h1:aSZPhcBmGu8MIddPxWNksfjbXHmlRL0yUddwB9CP4s0=
-maunium.net/go/tcell v0.0.0-20190111212645-703b3f6ecec9/go.mod h1:U+akxk8CP6vAWV74r2NOqEMMHw6kPGWTyvjzCtemxtM=
+maunium.net/go/mauview v0.0.0-20190325223341-4c387be4b686 h1:kFgijToFPbMQGIMElizZGPQsffu+ZqO0olORXnfj1g4=
+maunium.net/go/mauview v0.0.0-20190325223341-4c387be4b686/go.mod h1:Uw1CaNoCs9id/rKBF3Eg9KhhFVg+3akJTebZomFKW+4=
maunium.net/go/tcell v0.0.0-20190111223412-5e74142cb009 h1:4lojuJmNSun1nUB67m3DGg+RkYg1MUO6aUxgKQU5iZk=
maunium.net/go/tcell v0.0.0-20190111223412-5e74142cb009/go.mod h1:U+akxk8CP6vAWV74r2NOqEMMHw6kPGWTyvjzCtemxtM=
-maunium.net/go/tview v0.0.0-20190111211351-2f23a5129af0 h1:xHG0S9ExKp+6dkhasnK/fgO9mLHSSSqVoAymjyUtDdI=
-maunium.net/go/tview v0.0.0-20190111211351-2f23a5129af0/go.mod h1:ypYT6Dn71E7sVv6NxCjNo2cBJWJa257VSHCGOssGbV0=
-maunium.net/go/tview v0.0.0-20190111212720-d6aa1eac1b9a h1:f4JVX4GHJH/wMcL9VACMVXT0eaWhxx9a7OT/NLcxsw8=
-maunium.net/go/tview v0.0.0-20190111212720-d6aa1eac1b9a/go.mod h1:CTOF8OnDeK31Wl25GXdbYzTDvvZoiazmKNZdwkVUmzE=
-maunium.net/go/tview v0.0.0-20190111223510-de38190b095b h1:misvyPolT0TVGAtjc9Lr+oEYqGsV4YDByJVqZHxuu70=
-maunium.net/go/tview v0.0.0-20190111223510-de38190b095b/go.mod h1:Oi2eW32B8/cE7ZYXL6jyHMrXJL8ARDOQk/3aDvLEyVs=
diff --git a/ui/fuzzy-search-modal.go b/ui/fuzzy-search-modal.go
index 995b4ba..774a804 100644
--- a/ui/fuzzy-search-modal.go
+++ b/ui/fuzzy-search-modal.go
@@ -23,19 +23,18 @@ import (
"github.com/lithammer/fuzzysearch/fuzzy"
+ "maunium.net/go/mauview"
"maunium.net/go/tcell"
- "maunium.net/go/tview"
"maunium.net/go/gomuks/debug"
"maunium.net/go/gomuks/matrix/rooms"
- "maunium.net/go/gomuks/ui/widget"
)
type FuzzySearchModal struct {
- tview.Primitive
+ mauview.Component
- search *tview.InputField
- results *tview.TextView
+ search *mauview.InputField
+ results *mauview.TextView
matches fuzzy.Ranks
selected int
@@ -43,39 +42,39 @@ type FuzzySearchModal struct {
roomList []*rooms.Room
roomTitles []string
- parent *GomuksUI
- mainView *MainView
+ parent *MainView
}
func NewFuzzySearchModal(mainView *MainView, width int, height int) *FuzzySearchModal {
fs := &FuzzySearchModal{
- parent: mainView.parent,
- mainView: mainView,
+ parent: mainView,
}
fs.InitList(mainView.rooms)
- fs.search = tview.NewInputField().
- SetLabel("Room: ")
- fs.search.
- SetChangedFunc(fs.changeHandler).
- SetInputCapture(fs.keyHandler)
+ fs.search = mauview.NewInputField().SetChangedFunc(fs.changeHandler)
+ wrappedSearch := mauview.NewBox(fs.search).SetKeyCaptureFunc(fs.keyHandler)
+ searchLabel := mauview.NewTextField().SetText("Room")
+ combinedSearch := mauview.NewFlex().
+ SetDirection(mauview.FlexColumn).
+ AddFixedComponent(searchLabel, 5).
+ AddProportionalComponent(wrappedSearch, 1)
- fs.results = tview.NewTextView().
- SetRegions(true)
- fs.results.SetBorderPadding(1, 0, 0, 0)
+ fs.results = mauview.NewTextView().SetRegions(true)
// Flex widget containing input box and results
- container := tview.NewFlex().
- SetDirection(tview.FlexRow).
- AddItem(fs.search, 1, 0, true).
- AddItem(fs.results, 0, 1, false)
- container.
+ container := mauview.NewBox(mauview.NewFlex().
+ SetDirection(mauview.FlexRow).
+ AddFixedComponent(combinedSearch, 1).
+ AddProportionalComponent(fs.results, 1)).
SetBorder(true).
- SetBorderPadding(1, 1, 1, 1).
- SetTitle("Quick Room Switcher")
+ SetTitle("Quick Room Switcher").
+ SetBlurCaptureFunc(func() bool {
+ fs.parent.HideModal()
+ return true
+ })
- fs.Primitive = widget.TransparentCenter(width, height, container)
+ fs.Component = mauview.Center(container, width, height)
return fs
}
@@ -96,7 +95,7 @@ func (fs *FuzzySearchModal) changeHandler(str string) {
for _, match := range fs.matches {
fmt.Fprintf(fs.results, `["%d"]%s[""]%s`, match.OriginalIndex, match.Target, "\n")
}
- fs.parent.Render()
+ fs.parent.parent.Render()
fs.results.Highlight(strconv.Itoa(fs.matches[0].OriginalIndex))
fs.results.ScrollToBeginning()
} else {
@@ -105,13 +104,12 @@ func (fs *FuzzySearchModal) changeHandler(str string) {
}
}
-func (fs *FuzzySearchModal) keyHandler(event *tcell.EventKey) *tcell.EventKey {
+func (fs *FuzzySearchModal) keyHandler(event mauview.KeyEvent) mauview.KeyEvent {
highlights := fs.results.GetHighlights()
switch event.Key() {
case tcell.KeyEsc:
// Close room finder
- fs.parent.views.RemovePage("fuzzy-search-modal")
- fs.parent.app.SetFocus(fs.parent.views)
+ fs.parent.HideModal()
return nil
case tcell.KeyTab:
// Cycle highlighted area to next match
@@ -125,10 +123,9 @@ func (fs *FuzzySearchModal) keyHandler(event *tcell.EventKey) *tcell.EventKey {
// Switch room to currently selected room
if len(highlights) > 0 {
debug.Print("Fuzzy Selected Room:", fs.roomList[fs.matches[fs.selected].OriginalIndex].GetTitle())
- fs.mainView.SwitchRoom(fs.roomList[fs.matches[fs.selected].OriginalIndex].Tags()[0].Tag, fs.roomList[fs.matches[fs.selected].OriginalIndex])
+ fs.parent.SwitchRoom(fs.roomList[fs.matches[fs.selected].OriginalIndex].Tags()[0].Tag, fs.roomList[fs.matches[fs.selected].OriginalIndex])
}
- fs.parent.views.RemovePage("fuzzy-search-modal")
- fs.parent.app.SetFocus(fs.parent.views)
+ fs.parent.HideModal()
fs.results.Clear()
fs.search.SetText("")
return nil
diff --git a/ui/message-view.go b/ui/message-view.go
index 4af0ba9..c8a7bfd 100644
--- a/ui/message-view.go
+++ b/ui/message-view.go
@@ -25,8 +25,8 @@ import (
"github.com/mattn/go-runewidth"
+ "maunium.net/go/mauview"
"maunium.net/go/tcell"
- "maunium.net/go/tview"
"maunium.net/go/gomuks/config"
"maunium.net/go/gomuks/debug"
@@ -38,8 +38,6 @@ import (
)
type MessageView struct {
- *tview.Box
-
parent *RoomView
config *config.Config
@@ -51,6 +49,8 @@ type MessageView struct {
LoadingMessages bool
widestSender int
+ width int
+ height int
prevWidth int
prevHeight int
prevMsgCount int
@@ -65,7 +65,6 @@ type MessageView struct {
func NewMessageView(parent *RoomView) *MessageView {
return &MessageView{
- Box: tview.NewBox(),
parent: parent,
config: parent.config,
@@ -174,7 +173,7 @@ func (view *MessageView) AddMessage(ifcMessage ifc.Message, direction ifc.Messag
view.updateWidestSender(message.Sender())
- _, _, width, _ := view.GetRect()
+ width := view.width
bare := view.config.Preferences.BareMessageView
if !bare {
width -= view.TimestampWidth + TimestampSenderGap + view.widestSender + SenderMessageGap
@@ -267,15 +266,15 @@ func (view *MessageView) replaceBuffer(original messages.UIMessage, new messages
}
func (view *MessageView) recalculateBuffers() {
- _, _, width, height := view.GetRect()
prefs := view.config.Preferences
- if !prefs.BareMessageView {
- width -= view.TimestampWidth + TimestampSenderGap + view.widestSender + SenderMessageGap
- }
- recalculateMessageBuffers := width != view.prevWidth ||
+ recalculateMessageBuffers := view.width != view.prevWidth ||
view.prevPrefs.BareMessageView != prefs.BareMessageView ||
view.prevPrefs.DisableImages != prefs.DisableImages
if recalculateMessageBuffers || len(view.messages) != view.prevMsgCount {
+ width := view.width
+ if !prefs.BareMessageView {
+ width -= view.TimestampWidth + TimestampSenderGap + view.widestSender + SenderMessageGap
+ }
view.textBuffer = []tstring.TString{}
view.metaBuffer = []ifc.MessageMeta{}
view.prevMsgCount = 0
@@ -290,8 +289,8 @@ func (view *MessageView) recalculateBuffers() {
view.appendBuffer(message)
}
}
- view.prevHeight = height
- view.prevWidth = width
+ view.prevHeight = view.height
+ view.prevWidth = view.width
view.prevPrefs = prefs
}
@@ -343,50 +342,56 @@ func (view *MessageView) handleUsernameClick(message ifc.MessageMeta, prevMessag
return true
}
-func (view *MessageView) HandleClick(x, y int, button tcell.ButtonMask) bool {
- if button != tcell.Button1 {
- return false
- }
-
- _, _, _, height := view.GetRect()
- line := view.TotalHeight() - view.ScrollOffset - height + y
- if line < 0 || line >= view.TotalHeight() {
- return false
- }
+func (view *MessageView) OnMouseEvent(event mauview.MouseEvent) bool {
+ switch event.Buttons() {
+ case tcell.WheelUp:
+ if view.IsAtTop() {
+ go view.parent.parent.LoadHistory(view.parent.Room.ID)
+ } else {
+ view.AddScrollOffset(WheelScrollOffsetDiff)
+ return true
+ }
+ case tcell.WheelDown:
+ view.AddScrollOffset(-WheelScrollOffsetDiff)
+ view.parent.parent.MarkRead(view.parent)
+ return true
+ case tcell.Button1:
+ x, y := event.Position()
+ line := view.TotalHeight() - view.ScrollOffset - view.height + y
+ if line < 0 || line >= view.TotalHeight() {
+ return false
+ }
- message := view.metaBuffer[line]
- var prevMessage ifc.MessageMeta
- if y != 0 && line > 0 {
- prevMessage = view.metaBuffer[line-1]
- }
+ message := view.metaBuffer[line]
+ var prevMessage ifc.MessageMeta
+ if y != 0 && line > 0 {
+ prevMessage = view.metaBuffer[line-1]
+ }
- usernameX := view.TimestampWidth + TimestampSenderGap
- messageX := usernameX + view.widestSender + SenderMessageGap
+ usernameX := view.TimestampWidth + TimestampSenderGap
+ messageX := usernameX + view.widestSender + SenderMessageGap
- shouldRerender := false
- if x >= messageX {
- shouldRerender = view.handleMessageClick(message)
- } else if x >= usernameX {
- shouldRerender = view.handleUsernameClick(message, prevMessage)
+ if x >= messageX {
+ return view.handleMessageClick(message)
+ } else if x >= usernameX {
+ return view.handleUsernameClick(message, prevMessage)
+ }
}
-
- return shouldRerender
+ return false
}
const PaddingAtTop = 5
func (view *MessageView) AddScrollOffset(diff int) {
- _, _, _, height := view.GetRect()
-
totalHeight := view.TotalHeight()
- if diff >= 0 && view.ScrollOffset+diff >= totalHeight-height+PaddingAtTop {
- view.ScrollOffset = totalHeight - height + PaddingAtTop
+ if diff >= 0 && view.ScrollOffset+diff >= totalHeight-view.height+PaddingAtTop {
+ view.ScrollOffset = totalHeight - view.height + PaddingAtTop
} else {
view.ScrollOffset += diff
}
- if view.ScrollOffset > totalHeight-height+PaddingAtTop {
- view.ScrollOffset = totalHeight - height + PaddingAtTop
+ if view.ScrollOffset > totalHeight-view.height+PaddingAtTop {
+ view.ScrollOffset = totalHeight - view.height + PaddingAtTop
}
if view.ScrollOffset < 0 {
view.ScrollOffset = 0
@@ -394,8 +399,7 @@ func (view *MessageView) AddScrollOffset(diff int) {
}
func (view *MessageView) Height() int {
- _, _, _, height := view.GetRect()
- return height
+ return view.height
}
func (view *MessageView) TotalHeight() int {
@@ -403,9 +407,8 @@ func (view *MessageView) TotalHeight() int {
}
func (view *MessageView) IsAtTop() bool {
- _, _, _, height := view.GetRect()
totalHeight := len(view.textBuffer)
- return view.ScrollOffset >= totalHeight-height+PaddingAtTop
+ return view.ScrollOffset >= totalHeight-view.height+PaddingAtTop
}
const (
@@ -449,15 +452,14 @@ func (view *MessageView) calculateScrollBar(height int) (scrollBarHeight, scroll
return
}
-func (view *MessageView) getIndexOffset(screen tcell.Screen, height, messageX int) (indexOffset int) {
+func (view *MessageView) getIndexOffset(screen mauview.Screen, height, messageX int) (indexOffset int) {
indexOffset = view.TotalHeight() - view.ScrollOffset - height
if indexOffset <= -PaddingAtTop {
message := "Scroll up to load more messages."
if view.LoadingMessages {
message = "Loading more messages..."
}
- _, y, _, _ := view.GetRect()
- widget.WriteLineSimpleColor(screen, message, messageX, y, tcell.ColorGreen)
+ widget.WriteLineSimpleColor(screen, message, messageX, 0, tcell.ColorGreen)
}
return
}
@@ -488,25 +490,25 @@ func (view *MessageView) CapturePlaintext(height int) string {
return buf.String()
}
-func (view *MessageView) Draw(screen tcell.Screen) {
- x, y, _, height := view.GetRect()
+func (view *MessageView) Draw(screen mauview.Screen) {
+ view.width, view.height = screen.Size()
view.recalculateBuffers()
if view.TotalHeight() == 0 {
- widget.WriteLineSimple(screen, "It's quite empty in here.", x, y+height)
+ widget.WriteLineSimple(screen, "It's quite empty in here.", 0, view.height)
return
}
- usernameX := x + view.TimestampWidth + TimestampSenderGap
+ usernameX := view.TimestampWidth + TimestampSenderGap
messageX := usernameX + view.widestSender + SenderMessageGap
separatorX := usernameX + view.widestSender + SenderSeparatorGap
bareMode := view.config.Preferences.BareMessageView
if bareMode {
- messageX = x
+ messageX = 0
}
- indexOffset := view.getIndexOffset(screen, height, messageX)
+ indexOffset := view.getIndexOffset(screen, view.height, messageX)
if len(view.textBuffer) != len(view.metaBuffer) {
debug.Printf("Unexpected text/meta buffer length mismatch: %d != %d.", len(view.textBuffer), len(view.metaBuffer))
@@ -514,13 +516,13 @@ func (view *MessageView) Draw(screen tcell.Screen) {
return
}
- scrollBarHeight, scrollBarPos := view.calculateScrollBar(height)
+ scrollBarHeight, scrollBarPos := view.calculateScrollBar(view.height)
var prevMeta ifc.MessageMeta
firstLine := true
skippedLines := 0
- for line := 0; line < height; line++ {
+ for line := 0; line < view.height; line++ {
index := indexOffset + line
if index < 0 {
skippedLines++
@@ -530,31 +532,32 @@ func (view *MessageView) Draw(screen tcell.Screen) {
}
showScrollbar := line-skippedLines >= scrollBarPos-scrollBarHeight && line-skippedLines < scrollBarPos
- isTop := firstLine && view.ScrollOffset+height >= view.TotalHeight()
- isBottom := line == height-1 && view.ScrollOffset == 0
+ isTop := firstLine && view.ScrollOffset+view.height >= view.TotalHeight()
+ isBottom := line == view.height-1 && view.ScrollOffset == 0
borderChar, borderStyle := getScrollbarStyle(showScrollbar, isTop, isBottom)
firstLine = false
if !bareMode {
- screen.SetContent(separatorX, y+line, borderChar, nil, borderStyle)
+ screen.SetContent(separatorX, line, borderChar, nil, borderStyle)
}
text, meta := view.textBuffer[index], view.metaBuffer[index]
if meta != prevMeta {
if len(meta.FormatTime()) > 0 {
- widget.WriteLineSimpleColor(screen, meta.FormatTime(), x, y+line, meta.TimestampColor())
+ widget.WriteLineSimpleColor(screen, meta.FormatTime(), 0, line, meta.TimestampColor())
}
if !bareMode && (prevMeta == nil || meta.Sender() != prevMeta.Sender()) {
widget.WriteLineColor(
- screen, tview.AlignRight, meta.Sender(),
- usernameX, y+line, view.widestSender,
+ screen, mauview.AlignRight, meta.Sender(),
+ usernameX, line, view.widestSender,
meta.SenderColor())
}
prevMeta = meta
}
- text.Draw(screen, messageX, y+line)
+ text.Draw(screen, messageX, line)
}
+ debug.Print(screen)
}
diff --git a/ui/messages/imagemessage.go b/ui/messages/imagemessage.go
index 0efe676..cad76a4 100644
--- a/ui/messages/imagemessage.go
+++ b/ui/messages/imagemessage.go
@@ -80,7 +80,7 @@ func (msg *ImageMessage) updateData() {
debug.Print("Loading image:", msg.Homeserver, msg.FileID)
data, _, _, err := msg.matrix.Download(fmt.Sprintf("mxc://%s/%s", msg.Homeserver, msg.FileID))
if err != nil {
- debug.Print("Failed to download image %s/%s: %v", msg.Homeserver, msg.FileID, err)
+ debug.Printf("Failed to download image %s/%s: %v", msg.Homeserver, msg.FileID, err)
return
}
debug.Print("Image", msg.Homeserver, msg.FileID, "loaded.")
diff --git a/ui/messages/parser/parser.go b/ui/messages/parser/parser.go
index 94ab5b6..b36fde0 100644
--- a/ui/messages/parser/parser.go
+++ b/ui/messages/parser/parser.go
@@ -102,14 +102,24 @@ func ParseMessage(matrix ifc.MatrixContainer, room *rooms.Room, evt *mautrix.Eve
}
if len(evt.Content.GetReplyTo()) > 0 {
evt.Content.RemoveReplyFallback()
- replyToEvt, _ := matrix.Client().GetEvent(room.ID, evt.Content.GetReplyTo())
- replyToEvt.Content.RemoveReplyFallback()
- if len(replyToEvt.Content.FormattedBody) == 0 {
- replyToEvt.Content.FormattedBody = html.EscapeString(replyToEvt.Content.Body)
+ roomID := evt.Content.RelatesTo.InReplyTo.RoomID
+ if len(roomID) == 0 {
+ roomID = room.ID
+ }
+ replyToEvt, _ := matrix.Client().GetEvent(roomID, evt.Content.GetReplyTo())
+ if replyToEvt != nil {
+ replyToEvt.Content.RemoveReplyFallback()
+ if len(replyToEvt.Content.FormattedBody) == 0 {
+ replyToEvt.Content.FormattedBody = html.EscapeString(replyToEvt.Content.Body)
+ }
+ evt.Content.FormattedBody = fmt.Sprintf(
+ "In reply to <a href='https://matrix.to/#/%[1]s'>%[1]s</a><blockquote>%[2]s</blockquote><br/>%[3]s",
+ replyToEvt.Sender, replyToEvt.Content.FormattedBody, evt.Content.FormattedBody)
+ } else {
+ evt.Content.FormattedBody = fmt.Sprintf(
+ "In reply to unknown event https://matrix.to/#/%[1]s/%[2]s<br/>%[3]s",
+ roomID, evt.Content.GetReplyTo(), evt.Content.FormattedBody)
}
- evt.Content.FormattedBody = fmt.Sprintf(
- "In reply to <a href='https://matrix.to/#/%[1]s'>%[1]s</a><blockquote>%[2]s</blockquote><br/>%[3]s",
- replyToEvt.Sender, replyToEvt.Content.FormattedBody, evt.Content.FormattedBody)
}
ts := unixToTime(evt.Timestamp)
switch evt.Content.MsgType {
diff --git a/ui/messages/tstring/cell.go b/ui/messages/tstring/cell.go
index aee1716..3ea7b5a 100644
--- a/ui/messages/tstring/cell.go
+++ b/ui/messages/tstring/cell.go
@@ -18,6 +18,7 @@ package tstring
import (
"github.com/mattn/go-runewidth"
+ "maunium.net/go/mauview"
"maunium.net/go/tcell"
)
@@ -43,7 +44,7 @@ func (cell Cell) RuneWidth() int {
return runewidth.RuneWidth(cell.Char)
}
-func (cell Cell) Draw(screen tcell.Screen, x, y int) (chWidth int) {
+func (cell Cell) Draw(screen mauview.Screen, x, y int) (chWidth int) {
chWidth = cell.RuneWidth()
for runeWidthOffset := 0; runeWidthOffset < chWidth; runeWidthOffset++ {
screen.SetContent(x+runeWidthOffset, y, cell.Char, nil, cell.Style)
diff --git a/ui/messages/tstring/string.go b/ui/messages/tstring/string.go
index 7feeda0..b14dc8e 100644
--- a/ui/messages/tstring/string.go
+++ b/ui/messages/tstring/string.go
@@ -21,6 +21,7 @@ import (
"unicode"
"github.com/mattn/go-runewidth"
+ "maunium.net/go/mauview"
"maunium.net/go/tcell"
)
@@ -181,7 +182,7 @@ func (str TString) AdjustStyleFull(fn func(tcell.Style) tcell.Style) {
str.AdjustStyle(0, len(str), fn)
}
-func (str TString) Draw(screen tcell.Screen, x, y int) {
+func (str TString) Draw(screen mauview.Screen, x, y int) {
offsetX := 0
for _, cell := range str {
offsetX += cell.Draw(screen, x+offsetX, y)
diff --git a/ui/room-list.go b/ui/room-list.go
index a3ae17e..259e315 100644
--- a/ui/room-list.go
+++ b/ui/room-list.go
@@ -21,15 +21,15 @@ import (
"regexp"
"strings"
+ "maunium.net/go/mauview"
"maunium.net/go/tcell"
- "maunium.net/go/tview"
"maunium.net/go/gomuks/debug"
"maunium.net/go/gomuks/matrix/rooms"
)
type RoomList struct {
- *tview.Box
+ parent *MainView
// The list of tags in display order.
tags []string
@@ -40,6 +40,8 @@ type RoomList struct {
selectedTag string
scrollOffset int
+ height int
+ width int
// The item main text color.
mainTextColor tcell.Color
@@ -49,9 +51,10 @@ type RoomList struct {
selectedBackgroundColor tcell.Color
}
-func NewRoomList() *RoomList {
+func NewRoomList(parent *MainView) *RoomList {
list := &RoomList{
- Box: tview.NewBox(),
+ parent: parent,
+
items: make(map[string]*TagRoomList),
tags: []string{"m.favourite", "net.maunium.gomuks.fake.direct", "", "m.lowpriority"},
@@ -182,11 +185,10 @@ func (list *RoomList) SetSelected(tag string, room *rooms.Room) {
list.selected = room
list.selectedTag = tag
pos := list.index(tag, room)
- _, _, _, height := list.GetRect()
if pos <= list.scrollOffset {
list.scrollOffset = pos - 1
- } else if pos >= list.scrollOffset+height {
- list.scrollOffset = pos - height + 1
+ } else if pos >= list.scrollOffset+list.height {
+ list.scrollOffset = pos - list.height + 1
}
if list.scrollOffset < 0 {
list.scrollOffset = 0
@@ -208,10 +210,9 @@ func (list *RoomList) SelectedRoom() *rooms.Room {
func (list *RoomList) AddScrollOffset(offset int) {
list.scrollOffset += offset
- _, _, _, viewHeight := list.GetRect()
contentHeight := list.ContentHeight()
- if list.scrollOffset > contentHeight-viewHeight {
- list.scrollOffset = contentHeight - viewHeight
+ if list.scrollOffset > contentHeight-list.height {
+ list.scrollOffset = contentHeight - list.height
}
if list.scrollOffset < 0 {
list.scrollOffset = 0
@@ -372,10 +373,39 @@ func (list *RoomList) ContentHeight() (height int) {
return
}
-func (list *RoomList) HandleClick(column, line int, mod bool) (string, *rooms.Room) {
+func (list *RoomList) OnKeyEvent(event mauview.KeyEvent) bool {
+ return false
+}
+
+func (list *RoomList) OnPasteEvent(event mauview.PasteEvent) bool {
+ return false
+}
+
+func (list *RoomList) OnMouseEvent(event mauview.MouseEvent) bool {
+ switch event.Buttons() {
+ case tcell.WheelUp:
+ list.AddScrollOffset(-WheelScrollOffsetDiff)
+ case tcell.WheelDown:
+ list.AddScrollOffset(WheelScrollOffsetDiff)
+ case tcell.Button1:
+ x, y := event.Position()
+ return list.clickRoom(y, x, event.Modifiers() == tcell.ModCtrl)
+ }
+ return false
+}
+
+func (list *RoomList) Focus() {
+
+}
+
+func (list *RoomList) Blur() {
+
+}
+
+func (list *RoomList) clickRoom(line, column int, mod bool) bool {
line += list.scrollOffset
if line < 0 {
- return "", nil
+ return false
}
for _, tag := range list.tags {
trl := list.items[tag]
@@ -391,7 +421,8 @@ func (list *RoomList) HandleClick(column, line int, mod bool) (string, *rooms.Ro
if line < 0 {
break
} else if line < trl.Length() {
- return tag, trl.Visible()[trl.Length()-1-line].Room
+ list.parent.SwitchRoom(tag, trl.Visible()[trl.Length()-1-line].Room)
+ return true
}
// Tag items
@@ -405,10 +436,9 @@ func (list *RoomList) HandleClick(column, line int, mod bool) (string, *rooms.Ro
if mod {
diff = 100
}
- _, _, width, _ := list.GetRect()
if column <= 6 && hasLess {
trl.maxShown -= diff
- } else if column >= width-6 && hasMore {
+ } else if column >= list.width-6 && hasMore {
trl.maxShown += diff
}
if trl.maxShown < 10 {
@@ -420,9 +450,8 @@ func (list *RoomList) HandleClick(column, line int, mod bool) (string, *rooms.Ro
// Tag footer
line--
}
- return "", nil
+ return false
}
-
var nsRegex = regexp.MustCompile("^[a-z]\\.[a-z](?:\\.[a-z])*$")
func (list *RoomList) GetTagDisplayName(tag string) string {
@@ -445,11 +474,10 @@ func (list *RoomList) GetTagDisplayName(tag string) string {
}
// Draw draws this primitive onto the screen.
-func (list *RoomList) Draw(screen tcell.Screen) {
- list.Box.Draw(screen)
-
- x, y, width, height := list.GetRect()
- yLimit := y + height
+func (list *RoomList) Draw(screen mauview.Screen) {
+ list.width, list.height = screen.Size()
+ y := 0
+ yLimit := y + list.height
y -= list.scrollOffset
// Draw the list items.
@@ -464,8 +492,7 @@ func (list *RoomList) Draw(screen tcell.Screen) {
if y+renderHeight >= yLimit {
renderHeight = yLimit - y
}
- trl.SetRect(x, y, width, renderHeight)
- trl.Draw(screen)
+ trl.Draw(mauview.NewProxyScreen(screen, 0, y, list.width, renderHeight))
y += renderHeight
if y >= yLimit {
break
diff --git a/ui/room-view.go b/ui/room-view.go
index 67d4e83..2931372 100644
--- a/ui/room-view.go
+++ b/ui/room-view.go
@@ -26,9 +26,10 @@ import (
"github.com/mattn/go-runewidth"
+ "maunium.net/go/mauview"
+
"maunium.net/go/mautrix"
"maunium.net/go/tcell"
- "maunium.net/go/tview"
"maunium.net/go/gomuks/config"
"maunium.net/go/gomuks/interface"
@@ -39,16 +40,23 @@ import (
)
type RoomView struct {
- *tview.Box
-
- topic *tview.TextView
+ topic *mauview.TextView
content *MessageView
- status *tview.TextView
- userList *tview.TextView
+ status *mauview.TextField
+ userList *mauview.TextView
ulBorder *widget.Border
- input *widget.AdvancedInputField
+ input *mauview.InputArea
Room *rooms.Room
+ topicScreen *mauview.ProxyScreen
+ contentScreen *mauview.ProxyScreen
+ statusScreen *mauview.ProxyScreen
+ inputScreen *mauview.ProxyScreen
+ ulBorderScreen *mauview.ProxyScreen
+ ulScreen *mauview.ProxyScreen
+
+ prevScreen mauview.Screen
+
parent *MainView
config *config.Config
@@ -63,27 +71,34 @@ type RoomView struct {
func NewRoomView(parent *MainView, room *rooms.Room) *RoomView {
view := &RoomView{
- Box: tview.NewBox(),
- topic: tview.NewTextView(),
- status: tview.NewTextView(),
- userList: tview.NewTextView(),
+ topic: mauview.NewTextView(),
+ status: mauview.NewTextField(),
+ userList: mauview.NewTextView(),
ulBorder: widget.NewBorder(),
- input: widget.NewAdvancedInputField(),
+ input: mauview.NewInputArea(),
Room: room,
+
+ topicScreen: &mauview.ProxyScreen{OffsetX: 0, OffsetY: 0, Height: TopicBarHeight},
+ contentScreen: &mauview.ProxyScreen{OffsetX: 0, OffsetY: StatusBarHeight},
+ statusScreen: &mauview.ProxyScreen{OffsetX: 0, Height: StatusBarHeight},
+ inputScreen: &mauview.ProxyScreen{OffsetX: 0},
+ ulBorderScreen: &mauview.ProxyScreen{OffsetY: StatusBarHeight, Width: UserListBorderWidth},
+ ulScreen: &mauview.ProxyScreen{OffsetY: StatusBarHeight, Width: UserListWidth},
+
parent: parent,
config: parent.config,
}
view.content = NewMessageView(view)
view.input.
- SetFieldBackgroundColor(tcell.ColorDefault).
+ SetBackgroundColor(tcell.ColorDefault).
SetPlaceholder("Send a message...").
- SetPlaceholderExtColor(tcell.ColorGray).
+ SetPlaceholderTextColor(tcell.ColorGray).
SetTabCompleteFunc(view.InputTabComplete)
view.topic.
SetText(strings.Replace(room.GetTopic(), "\n", " ", -1)).
- SetBackgroundColor(tcell.ColorDarkGreen)
+ SetTextColor(tcell.ColorDarkGreen)
view.status.SetBackgroundColor(tcell.ColorDimGray)
@@ -106,26 +121,13 @@ func (view *RoomView) LoadHistory(matrix ifc.MatrixContainer, dir string) (int,
return view.MessageView().LoadHistory(matrix, view.logPath(dir))
}
-func (view *RoomView) SetInputCapture(fn func(room *RoomView, event *tcell.EventKey) *tcell.EventKey) *RoomView {
- view.input.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
- return fn(view, event)
- })
- return view
-}
-
-func (view *RoomView) SetMouseCapture(fn func(room *RoomView, event *tcell.EventMouse) *tcell.EventMouse) *RoomView {
- view.input.SetMouseCapture(func(event *tcell.EventMouse) *tcell.EventMouse {
- return fn(view, event)
- })
- return view
-}
-
func (view *RoomView) SetInputSubmitFunc(fn func(room *RoomView, text string)) *RoomView {
- view.input.SetDoneFunc(func(key tcell.Key) {
+ // FIXME
+ /*view.input.SetDoneFunc(func(key tcell.Key) {
if key == tcell.KeyEnter {
fn(view, view.input.GetText())
}
- })
+ })*/
return view
}
@@ -137,7 +139,7 @@ func (view *RoomView) SetInputChangedFunc(fn func(room *RoomView, text string))
}
func (view *RoomView) SetInputText(newText string) *RoomView {
- view.input.SetText(newText)
+ view.input.SetTextAndMoveCursor(newText)
return view
}
@@ -145,12 +147,12 @@ func (view *RoomView) GetInputText() string {
return view.input.GetText()
}
-func (view *RoomView) GetInputField() *widget.AdvancedInputField {
- return view.input
+func (view *RoomView) Focus() {
+ view.input.Focus()
}
-func (view *RoomView) Focus(delegate func(p tview.Primitive)) {
- delegate(view.input)
+func (view *RoomView) Blur() {
+ view.input.Blur()
}
func (view *RoomView) GetStatus() string {
@@ -169,7 +171,7 @@ func (view *RoomView) GetStatus() string {
buf.WriteString("Typing: " + view.typing[0])
buf.WriteString(" - ")
} else if len(view.typing) > 1 {
- fmt.Fprintf(&buf,
+ _, _ = fmt.Fprintf(&buf,
"Typing: %s and %s - ",
strings.Join(view.typing[:len(view.typing)-1], ", "), view.typing[len(view.typing)-1])
}
@@ -177,64 +179,91 @@ func (view *RoomView) GetStatus() string {
return strings.TrimSuffix(buf.String(), " - ")
}
-func (view *RoomView) Draw(screen tcell.Screen) {
- x, y, width, height := view.GetRect()
+// Constants defining the size of the room view grid.
+const (
+ UserListBorderWidth = 1
+ UserListWidth = 20
+ StaticHorizontalSpace = UserListBorderWidth + UserListWidth
+
+ TopicBarHeight = 1
+ StatusBarHeight = 1
+ InputBarHeight = 1
+ StaticVerticalSpace = TopicBarHeight + StatusBarHeight + InputBarHeight
+
+ MaxInputHeight
+)
+
+func (view *RoomView) Draw(screen mauview.Screen) {
+ width, height := screen.Size()
if width <= 0 || height <= 0 {
return
}
- // Constants defining the size of the room view grid.
- const (
- UserListBorderWidth = 1
- UserListWidth = 20
- StaticHorizontalSpace = UserListBorderWidth + UserListWidth
-
- TopicBarHeight = 1
- StatusBarHeight = 1
- InputBarHeight = 1
- StaticVerticalSpace = TopicBarHeight + StatusBarHeight + InputBarHeight
- )
-
// Calculate actual grid based on view rectangle and constants defined above.
var (
contentHeight = height - StaticVerticalSpace
contentWidth = width - StaticHorizontalSpace
-
- userListBorderColumn = x + contentWidth
- userListColumn = userListBorderColumn + UserListBorderWidth
-
- topicRow = y
- contentRow = topicRow + TopicBarHeight
- statusRow = contentRow + contentHeight
- inputRow = statusRow + StatusBarHeight
)
if view.config.Preferences.HideUserList {
contentWidth = width
}
- // Update the rectangles of all the children.
- view.topic.SetRect(x, topicRow, width, TopicBarHeight)
- view.content.SetRect(x, contentRow, contentWidth, contentHeight)
- view.status.SetRect(x, statusRow, width, StatusBarHeight)
- if !view.config.Preferences.HideUserList && userListColumn > x {
- view.userList.SetRect(userListColumn, contentRow, UserListWidth, contentHeight)
- view.ulBorder.SetRect(userListBorderColumn, contentRow, UserListBorderWidth, contentHeight)
+ if view.prevScreen != screen {
+ view.topicScreen.Parent = screen
+ view.contentScreen.Parent = screen
+ view.statusScreen.Parent = screen
+ view.inputScreen.Parent = screen
+ view.ulBorderScreen.Parent = screen
+ view.ulScreen.Parent = screen
+ view.prevScreen = screen
}
- view.input.SetRect(x, inputRow, width, InputBarHeight)
+
+ view.input.PrepareDraw(width)
+ inputHeight := view.input.GetTextHeight()
+ if inputHeight > MaxInputHeight {
+ inputHeight = MaxInputHeight
+ } else if inputHeight < 1 {
+ inputHeight = 1
+ }
+ contentHeight -= inputHeight
+
+ view.topicScreen.Width = width
+ view.contentScreen.Width = contentWidth
+ view.contentScreen.Height = contentHeight
+ view.statusScreen.OffsetY = view.contentScreen.YEnd()
+ view.statusScreen.Width = width
+ view.inputScreen.Width = width
+ view.inputScreen.OffsetY = view.statusScreen.YEnd()
+ view.inputScreen.Height = inputHeight
+ view.ulBorderScreen.OffsetX = view.contentScreen.XEnd()
+ view.ulBorderScreen.Height = contentHeight
+ view.ulScreen.OffsetX = view.ulBorderScreen.XEnd()
+ view.ulScreen.Height = contentHeight
// Draw everything
- view.Box.Draw(screen)
- view.topic.Draw(screen)
- view.content.Draw(screen)
+ view.topic.Draw(view.topicScreen)
+ view.content.Draw(view.contentScreen)
view.status.SetText(view.GetStatus())
- view.status.Draw(screen)
- view.input.Draw(screen)
+ view.status.Draw(view.statusScreen)
+ view.input.Draw(view.inputScreen)
if !view.config.Preferences.HideUserList {
- view.ulBorder.Draw(screen)
- view.userList.Draw(screen)
+ view.ulBorder.Draw(view.ulBorderScreen)
+ view.userList.Draw(view.ulScreen)
}
}
+func (view *RoomView) OnKeyEvent(event mauview.KeyEvent) bool {
+ return view.input.OnKeyEvent(event)
+}
+
+func (view *RoomView) OnPasteEvent(event mauview.PasteEvent) bool {
+ return view.input.OnPasteEvent(event)
+}
+
+func (view *RoomView) OnMouseEvent(event mauview.MouseEvent) bool {
+ return view.content.OnMouseEvent(event)
+}
+
func (view *RoomView) SetCompletions(completions []string) {
view.completions.list = completions
view.completions.textCache = view.input.GetText()
diff --git a/ui/tag-room-list.go b/ui/tag-room-list.go
index 3c30914..c965f10 100644
--- a/ui/tag-room-list.go
+++ b/ui/tag-room-list.go
@@ -21,8 +21,8 @@ import (
"strconv"
"strings"
+ "maunium.net/go/mauview"
"maunium.net/go/tcell"
- "maunium.net/go/tview"
"maunium.net/go/gomuks/matrix/rooms"
"maunium.net/go/gomuks/ui/widget"
@@ -44,7 +44,7 @@ func NewDefaultOrderedRoom(room *rooms.Room) *OrderedRoom {
return NewOrderedRoom("0.5", room)
}
-func (or *OrderedRoom) Draw(roomList *RoomList, screen tcell.Screen, x, y, lineWidth int, isSelected bool) {
+func (or *OrderedRoom) Draw(roomList *RoomList, screen mauview.Screen, x, y, lineWidth int, isSelected bool) {
style := tcell.StyleDefault.
Foreground(roomList.mainTextColor).
Bold(or.HasNewMessages())
@@ -56,7 +56,7 @@ func (or *OrderedRoom) Draw(roomList *RoomList, screen tcell.Screen, x, y, lineW
unreadCount := or.UnreadCount()
- widget.WriteLinePadded(screen, tview.AlignLeft, or.GetTitle(), x, y, lineWidth, style)
+ widget.WriteLinePadded(screen, mauview.AlignLeft, or.GetTitle(), x, y, lineWidth, style)
if unreadCount > 0 {
unreadMessageCount := "99+"
@@ -67,13 +67,13 @@ func (or *OrderedRoom) Draw(roomList *RoomList, screen tcell.Screen, x, y, lineW
unreadMessageCount += "!"
}
unreadMessageCount = fmt.Sprintf("(%s)", unreadMessageCount)
- widget.WriteLine(screen, tview.AlignRight, unreadMessageCount, x+lineWidth-7, y, 7, style)
+ widget.WriteLine(screen, mauview.AlignRight, unreadMessageCount, x+lineWidth-7, y, 7, style)
lineWidth -= len(unreadMessageCount)
}
}
type TagRoomList struct {
- *tview.Box
+ mauview.NoopEventHandler
rooms []*OrderedRoom
maxShown int
name string
@@ -83,7 +83,6 @@ type TagRoomList struct {
func NewTagRoomList(parent *RoomList, name string, rooms ...*OrderedRoom) *TagRoomList {
return &TagRoomList{
- Box: tview.NewBox(),
maxShown: 10,
rooms: rooms,
name: name,
@@ -246,41 +245,40 @@ func (trl *TagRoomList) RenderHeight() int {
return height
}
-func (trl *TagRoomList) DrawHeader(screen tcell.Screen) {
- x, y, width, _ := trl.GetRect()
+func (trl *TagRoomList) DrawHeader(screen mauview.Screen) {
+ width, _ := screen.Size()
roomCount := strconv.Itoa(trl.TotalLength())
// Draw tag name
displayNameWidth := width - 1 - len(roomCount)
- widget.WriteLine(screen, tview.AlignLeft, trl.displayname, x, y, displayNameWidth, TagDisplayNameStyle)
+ widget.WriteLine(screen, mauview.AlignLeft, trl.displayname, 0, 0, displayNameWidth, TagDisplayNameStyle)
// Draw tag room count
- roomCountX := x + len(trl.displayname) + 1
+ roomCountX := len(trl.displayname) + 1
roomCountWidth := width - 2 - len(trl.displayname)
- widget.WriteLine(screen, tview.AlignLeft, roomCount, roomCountX, y, roomCountWidth, TagRoomCountStyle)
+ widget.WriteLine(screen, mauview.AlignLeft, roomCount, roomCountX, 0, roomCountWidth, TagRoomCountStyle)
}
-func (trl *TagRoomList) Draw(screen tcell.Screen) {
+func (trl *TagRoomList) Draw(screen mauview.Screen) {
if len(trl.displayname) == 0 {
return
}
trl.DrawHeader(screen)
- x, y, width, height := trl.GetRect()
- yLimit := y + height
+ width, height := screen.Size()
items := trl.Visible()
if trl.IsCollapsed() {
- screen.SetCell(x+width-1, y, tcell.StyleDefault, 'â–¶')
+ screen.SetCell(width-1, 0, tcell.StyleDefault, 'â–¶')
return
}
- screen.SetCell(x+width-1, y, tcell.StyleDefault, 'â–¼')
+ screen.SetCell(width-1, 0, tcell.StyleDefault, 'â–¼')
- offsetY := 1
+ y := 1
for i := trl.Length() - 1; i >= 0; i-- {
- if y+offsetY >= yLimit {
+ if y >= height {
return
}
@@ -288,18 +286,18 @@ func (trl *TagRoomList) Draw(screen tcell.Screen) {
lineWidth := width
isSelected := trl.name == trl.parent.selectedTag && item.Room == trl.parent.selected
- item.Draw(trl.parent, screen, x, y+offsetY, lineWidth, isSelected)
- offsetY++
+ item.Draw(trl.parent, screen, 0, y, lineWidth, isSelected)
+ y++
}
hasLess := trl.maxShown > 10
hasMore := trl.HasInvisibleRooms()
- if (hasLess || hasMore) && y+offsetY < yLimit {
+ if (hasLess || hasMore) && y < height {
if hasMore {
- widget.WriteLine(screen, tview.AlignRight, "More ↓", x, y+offsetY, width, tcell.StyleDefault)
+ widget.WriteLine(screen, mauview.AlignRight, "More ↓", 0, y, width, tcell.StyleDefault)
}
if hasLess {
- widget.WriteLine(screen, tview.AlignLeft, "↑ Less", x, y+offsetY, width, tcell.StyleDefault)
+ widget.WriteLine(screen, mauview.AlignLeft, "↑ Less", 0, y, width, tcell.StyleDefault)
}
- offsetY++
+ y++
}
}
diff --git a/ui/ui.go b/ui/ui.go
index 5188278..66a3524 100644
--- a/ui/ui.go
+++ b/ui/ui.go
@@ -19,8 +19,8 @@ package ui
import (
"os"
+ "maunium.net/go/mauview"
"maunium.net/go/tcell"
- "maunium.net/go/tview"
"maunium.net/go/gomuks/interface"
)
@@ -34,17 +34,18 @@ const (
)
type GomuksUI struct {
- gmx ifc.Gomuks
- app *tview.Application
- views *tview.Pages
+ gmx ifc.Gomuks
+ app *mauview.Application
mainView *MainView
loginView *LoginView
+
+ views map[View]mauview.Component
}
func init() {
- tview.Styles.PrimitiveBackgroundColor = tcell.ColorDefault
- tview.Styles.ContrastBackgroundColor = tcell.ColorDarkGreen
+ mauview.Styles.PrimitiveBackgroundColor = tcell.ColorDefault
+ mauview.Styles.ContrastBackgroundColor = tcell.ColorDarkGreen
if tcellDB := os.Getenv("TCELLDB"); len(tcellDB) == 0 {
if info, err := os.Stat("/usr/share/tcell/database"); err == nil && info.IsDir() {
os.Setenv("TCELLDB", "/usr/share/tcell/database")
@@ -54,20 +55,22 @@ func init() {
func NewGomuksUI(gmx ifc.Gomuks) ifc.GomuksUI {
ui := &GomuksUI{
- gmx: gmx,
- app: tview.NewApplication(),
- views: tview.NewPages(),
+ gmx: gmx,
+ app: mauview.NewApplication(),
}
- ui.views.SetChangedFunc(ui.Render)
return ui
}
func (ui *GomuksUI) Init() {
- ui.app.SetRoot(ui.InitViews(), true)
+ ui.views = map[View]mauview.Component{
+ ViewLogin: ui.NewLoginView(),
+ ViewMain: ui.NewMainView(),
+ }
+ ui.app.Root = ui.views[ViewLogin]
}
func (ui *GomuksUI) Start() error {
- return ui.app.Run()
+ return ui.app.Start()
}
func (ui *GomuksUI) Stop() {
@@ -75,23 +78,21 @@ func (ui *GomuksUI) Stop() {
}
func (ui *GomuksUI) Finish() {
- if ui.app.GetScreen() != nil {
- ui.app.GetScreen().Fini()
+ if ui.app.Screen() != nil {
+ ui.app.Screen().Fini()
}
}
func (ui *GomuksUI) Render() {
- ui.app.Draw()
+ ui.app.Redraw()
}
func (ui *GomuksUI) OnLogin() {
ui.SetView(ViewMain)
- ui.app.SetFocus(ui.mainView)
}
func (ui *GomuksUI) OnLogout() {
ui.SetView(ViewLogin)
- ui.app.SetFocus(ui.loginView)
}
func (ui *GomuksUI) HandleNewPreferences() {
@@ -99,13 +100,11 @@ func (ui *GomuksUI) HandleNewPreferences() {
}
func (ui *GomuksUI) SetView(name View) {
- ui.views.SwitchToPage(string(name))
-}
-
-func (ui *GomuksUI) InitViews() tview.Primitive {
- ui.views.AddPage(string(ViewLogin), ui.NewLoginView(), true, true)
- ui.views.AddPage(string(ViewMain), ui.NewMainView(), true, false)
- return ui.views
+ ui.app.Root = ui.views[name]
+ focusable, ok := ui.app.Root.(mauview.Focusable)
+ if ok {
+ focusable.Focus()
+ }
}
func (ui *GomuksUI) MainView() ifc.MainView {
diff --git a/ui/view-login.go b/ui/view-login.go
index 603899c..1cef96f 100644
--- a/ui/view-login.go
+++ b/ui/view-login.go
@@ -17,66 +17,97 @@
package ui
import (
+ "maunium.net/go/tcell"
+
"maunium.net/go/mautrix"
- "maunium.net/go/tview"
+ "maunium.net/go/mauview"
"maunium.net/go/gomuks/config"
"maunium.net/go/gomuks/debug"
"maunium.net/go/gomuks/interface"
- "maunium.net/go/gomuks/ui/widget"
)
type LoginView struct {
- *tview.Form
+ *mauview.Form
+
+ container *mauview.Centerer
+
+ homeserverLabel *mauview.TextField
+ usernameLabel *mauview.TextField
+ passwordLabel *mauview.TextField
- homeserver *widget.AdvancedInputField
- username *widget.AdvancedInputField
- password *widget.AdvancedInputField
- error *widget.FormTextView
+ homeserver *mauview.InputField
+ username *mauview.InputField
+ password *mauview.InputField
+ error *mauview.TextField
+
+ loginButton *mauview.Button
+ quitButton *mauview.Button
matrix ifc.MatrixContainer
config *config.Config
parent *GomuksUI
}
-func (ui *GomuksUI) NewLoginView() tview.Primitive {
+func (ui *GomuksUI) NewLoginView() mauview.Component {
view := &LoginView{
- Form: tview.NewForm(),
+ Form: mauview.NewForm(),
+
+ usernameLabel: mauview.NewTextField().SetText("Username"),
+ passwordLabel: mauview.NewTextField().SetText("Password"),
+ homeserverLabel: mauview.NewTextField().SetText("Homeserver"),
- homeserver: widget.NewAdvancedInputField(),
- username: widget.NewAdvancedInputField(),
- password: widget.NewAdvancedInputField(),
+ username: mauview.NewInputField(),
+ password: mauview.NewInputField(),
+ homeserver: mauview.NewInputField(),
+
+ loginButton: mauview.NewButton("Login"),
+ quitButton: mauview.NewButton("Quit"),
matrix: ui.gmx.Matrix(),
config: ui.gmx.Config(),
parent: ui,
}
+
hs := ui.gmx.Config().HS
- if len(hs) == 0 {
- hs = "https://matrix.org"
- }
- view.homeserver.SetLabel("Homeserver").SetText(hs).SetFieldWidth(30)
- view.username.SetLabel("Username").SetText(ui.gmx.Config().UserID).SetFieldWidth(30)
- view.password.SetLabel("Password").SetMaskCharacter('*').SetFieldWidth(30)
+ view.homeserver.SetText(hs)
+ view.username.SetText(ui.gmx.Config().UserID)
+ view.password.SetMaskCharacter('*')
- view.
- AddFormItem(view.homeserver).AddFormItem(view.username).AddFormItem(view.password).
- AddButton("Log in", view.Login).
- AddButton("Quit", ui.gmx.Stop).
- SetButtonsAlign(tview.AlignCenter).
- SetBorder(true).SetTitle("Log in to Matrix")
+ view.quitButton.SetOnClick(ui.gmx.Stop).SetBackgroundColor(tcell.ColorBlue)
+ view.loginButton.SetOnClick(view.Login).SetBackgroundColor(tcell.ColorBlue)
+ view.SetColumns([]int{1, 10, 1, 9, 1, 9, 1, 10, 1})
+ view.SetRows([]int{1, 1, 1, 1, 1, 1, 1, 1, 1, 1})
+ view.AddFormItem(view.username, 3, 1, 5, 1).
+ AddFormItem(view.password, 3, 3, 5, 1).
+ AddFormItem(view.homeserver, 3, 5, 5, 1).
+ AddFormItem(view.loginButton, 5, 7, 3, 1).
+ AddFormItem(view.quitButton, 1, 7, 3, 1).
+ AddComponent(view.usernameLabel, 1, 1, 1, 1).
+ AddComponent(view.passwordLabel, 1, 3, 1, 1).
+ AddComponent(view.homeserverLabel, 1, 5, 1, 1)
ui.loginView = view
- return widget.Center(45, 13, ui.loginView)
+ view.container = mauview.Center(mauview.NewBox(view).SetTitle("Log in to Matrix"), 45, 13)
+ return view.container
}
func (view *LoginView) Error(err string) {
+ if len(err) == 0 {
+ debug.Print("Hiding error")
+ view.RemoveComponent(view.error)
+ view.error = nil
+ return
+ }
+ debug.Print("Showing error", err)
if view.error == nil {
- view.error = &widget.FormTextView{TextView: tview.NewTextView()}
- view.AddFormItem(view.error)
+ view.error = mauview.NewTextField().SetTextColor(tcell.ColorRed)
+ view.AddComponent(view.error, 1, 9, 7, 1)
}
view.error.SetText(err)
+
+ view.parent.Render()
}
func (view *LoginView) Login() {
@@ -91,8 +122,8 @@ func (view *LoginView) Login() {
err = view.matrix.Login(mxid, password)
if err != nil {
if httpErr, ok := err.(mautrix.HTTPError); ok {
- if respErr, ok := httpErr.WrappedError.(mautrix.RespError); ok {
- view.Error(respErr.Err)
+ if httpErr.RespError != nil {
+ view.Error(httpErr.RespError.Err)
} else {
view.Error(httpErr.Message)
}
diff --git a/ui/view-main.go b/ui/view-main.go
index 254dc9f..8b2d895 100644
--- a/ui/view-main.go
+++ b/ui/view-main.go
@@ -26,8 +26,8 @@ import (
"github.com/kyokomi/emoji"
"maunium.net/go/mautrix"
+ "maunium.net/go/mauview"
"maunium.net/go/tcell"
- "maunium.net/go/tview"
"maunium.net/go/gomuks/config"
"maunium.net/go/gomuks/debug"
@@ -40,12 +40,16 @@ import (
)
type MainView struct {
- *tview.Flex
+ flex *mauview.Flex
roomList *RoomList
- roomView *tview.Pages
+ roomView *mauview.Box
+ currentRoom *RoomView
rooms map[string]*RoomView
cmdProcessor *CommandProcessor
+ focused mauview.Focusable
+
+ modal mauview.Component
lastFocusTime time.Time
@@ -55,11 +59,10 @@ type MainView struct {
parent *GomuksUI
}
-func (ui *GomuksUI) NewMainView() tview.Primitive {
+func (ui *GomuksUI) NewMainView() mauview.Component {
mainView := &MainView{
- Flex: tview.NewFlex(),
- roomList: NewRoomList(),
- roomView: tview.NewPages(),
+ flex: mauview.NewFlex().SetDirection(mauview.FlexColumn),
+ roomView: mauview.NewBox(nil).SetBorder(false),
rooms: make(map[string]*RoomView),
matrix: ui.gmx.Matrix(),
@@ -67,13 +70,13 @@ func (ui *GomuksUI) NewMainView() tview.Primitive {
config: ui.gmx.Config(),
parent: ui,
}
+ mainView.roomList = NewRoomList(mainView)
mainView.cmdProcessor = NewCommandProcessor(mainView)
- mainView.
- SetDirection(tview.FlexColumn).
- AddItem(mainView.roomList, 25, 0, false).
- AddItem(widget.NewBorder(), 1, 0, false).
- AddItem(mainView.roomView, 0, 1, true)
+ mainView.flex.
+ AddFixedComponent(mainView.roomList, 25).
+ AddFixedComponent(widget.NewBorder(), 1).
+ AddProportionalComponent(mainView.roomView, 1)
mainView.BumpFocus(nil)
ui.mainView = mainView
@@ -81,18 +84,37 @@ func (ui *GomuksUI) NewMainView() tview.Primitive {
return mainView
}
-func (view *MainView) Draw(screen tcell.Screen) {
+func (view *MainView) ShowModal(modal mauview.Component) {
+ view.modal = modal
+ var ok bool
+ view.focused, ok = modal.(mauview.Focusable)
+ if !ok {
+ view.focused = nil
+ }
+}
+
+func (view *MainView) HideModal() {
+ view.modal = nil
+ view.focused = view.roomView
+}
+
+func (view *MainView) Draw(screen mauview.Screen) {
if view.config.Preferences.HideRoomList {
- view.roomView.SetRect(view.GetRect())
view.roomView.Draw(screen)
} else {
- view.Flex.Draw(screen)
+ view.flex.Draw(screen)
+ }
+
+ if view.modal != nil {
+ view.modal.Draw(screen)
}
}
func (view *MainView) BumpFocus(roomView *RoomView) {
- view.lastFocusTime = time.Now()
- view.MarkRead(roomView)
+ if roomView != nil {
+ view.lastFocusTime = time.Now()
+ view.MarkRead(roomView)
+ }
}
func (view *MainView) MarkRead(roomView *RoomView) {
@@ -167,7 +189,10 @@ func (view *MainView) sendTempMessage(roomView *RoomView, tempMessage ifc.Messag
}
func (view *MainView) ShowBare(roomView *RoomView) {
- _, height := view.parent.app.GetScreen().Size()
+ if roomView == nil {
+ return
+ }
+ _, height := view.parent.app.Screen().Size()
view.parent.app.Suspend(func() {
print("\033[2J\033[0;0H")
// We don't know how much space there exactly is. Too few messages looks weird,
@@ -181,37 +206,54 @@ func (view *MainView) ShowBare(roomView *RoomView) {
})
}
-func (view *MainView) KeyEventHandler(roomView *RoomView, key *tcell.EventKey) *tcell.EventKey {
- view.BumpFocus(roomView)
+func (view *MainView) OnKeyEvent(event mauview.KeyEvent) bool {
+ view.BumpFocus(view.currentRoom)
+
+ if view.modal != nil {
+ return view.modal.OnKeyEvent(event)
+ }
- k := key.Key()
- c := key.Rune()
- if key.Modifiers() == tcell.ModCtrl || key.Modifiers() == tcell.ModAlt {
+ k := event.Key()
+ c := event.Rune()
+ if event.Modifiers() == tcell.ModCtrl || event.Modifiers() == tcell.ModAlt {
switch {
case k == tcell.KeyDown:
view.SwitchRoom(view.roomList.Next())
case k == tcell.KeyUp:
view.SwitchRoom(view.roomList.Previous())
case k == tcell.KeyEnter:
- searchModal := NewFuzzySearchModal(view, 42, 12)
- view.parent.views.AddPage("fuzzy-search-modal", searchModal, true, true)
- view.parent.app.SetFocus(searchModal)
+ view.ShowModal(NewFuzzySearchModal(view, 42, 12))
+ case k == tcell.KeyHome:
+ msgView := view.currentRoom.MessageView()
+ msgView.AddScrollOffset(msgView.TotalHeight())
+ case k == tcell.KeyEnd:
+ msgView := view.currentRoom.MessageView()
+ msgView.AddScrollOffset(-msgView.TotalHeight())
+ case k == tcell.KeyCtrlN:
+ return view.flex.OnKeyEvent(tcell.NewEventKey(tcell.KeyEnter, '\n', event.Modifiers()))
case c == 'a':
view.SwitchRoom(view.roomList.NextWithActivity())
case c == 'l':
- view.ShowBare(roomView)
+ view.ShowBare(view.currentRoom)
default:
- return key
+ goto defaultHandler
}
+ return true
} else if k == tcell.KeyAltDown || k == tcell.KeyCtrlDown {
view.SwitchRoom(view.roomList.Next())
+ return true
} else if k == tcell.KeyAltUp || k == tcell.KeyCtrlUp {
view.SwitchRoom(view.roomList.Previous())
- } else if k == tcell.KeyPgUp || k == tcell.KeyPgDn || k == tcell.KeyUp || k == tcell.KeyDown || k == tcell.KeyEnd || k == tcell.KeyHome {
- msgView := roomView.MessageView()
+ return true
+ } else if view.currentRoom != nil &&
+ (k == tcell.KeyPgUp || k == tcell.KeyPgDn ||
+ k == tcell.KeyUp || k == tcell.KeyDown ||
+ k == tcell.KeyEnd || k == tcell.KeyHome) {
+ // TODO these should be in the RoomView key handler
+ msgView := view.currentRoom.MessageView()
if msgView.IsAtTop() && (k == tcell.KeyPgUp || k == tcell.KeyUp) {
- go view.LoadHistory(roomView.Room.ID)
+ go view.LoadHistory(view.currentRoom.Room.ID)
}
switch k {
@@ -219,80 +261,72 @@ func (view *MainView) KeyEventHandler(roomView *RoomView, key *tcell.EventKey) *
msgView.AddScrollOffset(msgView.Height() / 2)
case tcell.KeyPgDn:
msgView.AddScrollOffset(-msgView.Height() / 2)
- case tcell.KeyUp:
- msgView.AddScrollOffset(1)
- case tcell.KeyDown:
- msgView.AddScrollOffset(-1)
- case tcell.KeyHome:
- msgView.AddScrollOffset(msgView.TotalHeight())
- case tcell.KeyEnd:
- msgView.AddScrollOffset(-msgView.TotalHeight())
+ default:
+ goto defaultHandler
}
- } else {
- return key
+ return true
+ } else if k == tcell.KeyEnter {
+ view.InputSubmit(view.currentRoom, view.currentRoom.input.GetText())
+ return true
+ }
+defaultHandler:
+ if view.config.Preferences.HideRoomList {
+ debug.Print("Key event going to default handler (direct to roomview)", event)
+ return view.roomView.OnKeyEvent(event)
}
- return nil
+ debug.Print("Key event going to default handler (flex)", event)
+ return view.flex.OnKeyEvent(event)
}
const WheelScrollOffsetDiff = 3
-func isInArea(x, y int, p tview.Primitive) bool {
- rx, ry, rw, rh := p.GetRect()
- return x >= rx && y >= ry && x < rx+rw && y < ry+rh
-}
-
-func (view *MainView) MouseEventHandler(roomView *RoomView, event *tcell.EventMouse) *tcell.EventMouse {
- if event.Buttons() == tcell.ButtonNone || event.HasMotion() {
- return event
+func (view *MainView) OnMouseEvent(event mauview.MouseEvent) bool {
+ if view.config.Preferences.HideRoomList {
+ return view.roomView.OnMouseEvent(event)
}
- view.BumpFocus(roomView)
+ return view.flex.OnMouseEvent(event)
+ /*if event.Buttons() == tcell.ButtonNone || event.HasMotion() {
+ return false
+ }
+
+ view.BumpFocus(view.currentRoom)
- msgView := roomView.MessageView()
x, y := event.Position()
switch {
- case isInArea(x, y, msgView):
- mx, my, _, _ := msgView.GetRect()
- switch event.Buttons() {
- case tcell.WheelUp:
- if msgView.IsAtTop() {
- go view.LoadHistory(roomView.Room.ID)
- } else {
- msgView.AddScrollOffset(WheelScrollOffsetDiff)
-
- view.parent.Render()
- }
- case tcell.WheelDown:
- msgView.AddScrollOffset(-WheelScrollOffsetDiff)
- view.parent.Render()
- view.MarkRead(roomView)
- default:
- if msgView.HandleClick(x-mx, y-my, event.Buttons()) {
- view.parent.Render()
- }
- }
- case isInArea(x, y, view.roomList):
- switch event.Buttons() {
- case tcell.WheelUp:
- view.roomList.AddScrollOffset(-WheelScrollOffsetDiff)
- view.parent.Render()
- case tcell.WheelDown:
- view.roomList.AddScrollOffset(WheelScrollOffsetDiff)
- view.parent.Render()
- case tcell.Button1:
- _, rly, _, _ := msgView.GetRect()
- line := y - rly + 1
- switchToTag, switchToRoom := view.roomList.HandleClick(x, line, event.Modifiers() == tcell.ModCtrl)
- if switchToRoom != nil {
- view.SwitchRoom(switchToTag, switchToRoom)
- } else {
- view.parent.Render()
- }
- }
+ case x >= 27:
+ view.roomView.OnMouseEvent(mauview.OffsetMouseEvent(event, -27, 0))
+ view.roomView.Focus()
+ view.focused = view.roomView
+ case x <= 25:
+ view.roomList.OnMouseEvent(event)
+ view.roomList.Focus()
+ view.focused = view.roomList
default:
debug.Print("Unhandled mouse event:", event.Buttons(), event.Modifiers(), x, y)
}
- return event
+ return false*/
+}
+
+func (view *MainView) OnPasteEvent(event mauview.PasteEvent) bool {
+ if view.modal != nil {
+ return view.modal.OnPasteEvent(event)
+ } else if view.config.Preferences.HideRoomList {
+ return view.roomView.OnPasteEvent(event)
+ }
+ return view.flex.OnPasteEvent(event)
+}
+
+func (view *MainView) Focus() {
+ if view.focused != nil {
+ view.focused.Focus()
+ }
+}
+
+func (view *MainView) Blur() {
+ if view.focused != nil {
+ view.focused.Blur()
+ }
}
func (view *MainView) SwitchRoom(tag string, room *rooms.Room) {
@@ -300,29 +334,19 @@ func (view *MainView) SwitchRoom(tag string, room *rooms.Room) {
return
}
- view.roomView.SwitchToPage(room.ID)
roomView := view.rooms[room.ID]
if roomView == nil {
debug.Print("Tried to switch to non-nil room with nil roomView!")
debug.Print(tag, room)
return
}
+ view.roomView.SetInnerComponent(roomView)
+ view.currentRoom = roomView
view.MarkRead(roomView)
view.roomList.SetSelected(tag, room)
- view.parent.app.SetFocus(view)
view.parent.Render()
}
-func (view *MainView) Focus(delegate func(p tview.Primitive)) {
- room := view.roomList.SelectedRoom()
- if room != nil {
- roomView, ok := view.rooms[room.ID]
- if ok {
- delegate(roomView)
- }
- }
-}
-
func (view *MainView) SaveAllHistory() {
for _, room := range view.rooms {
err := room.SaveHistory(view.config.HistoryDir)
@@ -333,14 +357,11 @@ func (view *MainView) SaveAllHistory() {
}
func (view *MainView) addRoomPage(room *rooms.Room) {
- if !view.roomView.HasPage(room.ID) {
+ if _, ok := view.rooms[room.ID]; !ok {
roomView := NewRoomView(view, room).
SetInputSubmitFunc(view.InputSubmit).
- SetInputChangedFunc(view.InputChanged).
- SetInputCapture(view.KeyEventHandler).
- SetMouseCapture(view.MouseEventHandler)
+ SetInputChangedFunc(view.InputChanged)
view.rooms[room.ID] = roomView
- view.roomView.AddPage(room.ID, roomView, true, false)
roomView.UpdateUserList()
_, err := roomView.LoadHistory(view.matrix, view.config.HistoryDir)
@@ -387,7 +408,6 @@ func (view *MainView) RemoveRoom(room *rooms.Room) {
view.roomList.Remove(room)
view.SwitchRoom(view.roomList.Selected())
- view.roomView.RemovePage(room.ID)
delete(view.rooms, room.ID)
view.parent.Render()
@@ -395,7 +415,6 @@ func (view *MainView) RemoveRoom(room *rooms.Room) {
func (view *MainView) SetRooms(rooms map[string]*rooms.Room) {
view.roomList.Clear()
- view.roomView.Clear()
view.rooms = make(map[string]*RoomView)
for _, room := range rooms {
if room.HasLeft {
diff --git a/ui/widget/advanced-inputfield.go b/ui/widget/advanced-inputfield.go
deleted file mode 100644
index 741ae3d..0000000
--- a/ui/widget/advanced-inputfield.go
+++ /dev/null
@@ -1,562 +0,0 @@
-// gomuks - A terminal Matrix client written in Go.
-// Copyright (C) 2019 Tulir Asokan
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Affero General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Affero General Public License for more details.
-//
-// You should have received a copy of the GNU Affero General Public License
-// along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-// Based on https://github.com/rivo/tview/blob/master/inputfield.go
-
-package widget
-
-import (
- "math"
- "regexp"
- "strings"
- "unicode/utf8"
-
- "github.com/mattn/go-runewidth"
- "github.com/zyedidia/clipboard"
-
- "maunium.net/go/tcell"
- "maunium.net/go/tview"
-)
-
-// AdvancedInputField is a multi-line user-editable text area.
-//
-// Use SetMaskCharacter() to hide input from onlookers (e.g. for password
-// input).
-type AdvancedInputField struct {
- *tview.Box
-
- // Cursor position
- cursorOffset int
- viewOffset int
-
- // The text that was entered.
- text string
-
- // The text to be displayed before the input area.
- label string
-
- // The text to be displayed in the input area when "text" is empty.
- placeholder string
-
- // The label color.
- labelColor tcell.Color
-
- // The background color of the input area.
- fieldBackgroundColor tcell.Color
-
- // The text color of the input area.
- fieldTextColor tcell.Color
-
- // The text color of the placeholder.
- placeholderTextColor tcell.Color
-
- // The screen width of the label area. A value of 0 means use the width of
- // the label text.
- labelWidth int
-
- // The screen width of the input area. A value of 0 means extend as much as
- // possible.
- fieldWidth int
-
- // A character to mask entered text (useful for password fields). A value of 0
- // disables masking.
- maskCharacter rune
-
- // Whether or not to enable vim-style keybindings.
- vimBindings bool
-
- // An optional function which may reject the last character that was entered.
- accept func(text string, ch rune) bool
-
- // An optional function which is called when the input has changed.
- changed func(text string)
-
- // An optional function which is called when the user indicated that they
- // are done entering text. The key which was pressed is provided (enter, tab, backtab or escape).
- done func(tcell.Key)
-
- // An optional function which is called when the user presses tab.
- tabComplete func(text string, cursorOffset int)
-}
-
-// NewAdvancedInputField returns a new input field.
-func NewAdvancedInputField() *AdvancedInputField {
- return &AdvancedInputField{
- Box: tview.NewBox(),
- labelColor: tview.Styles.SecondaryTextColor,
- fieldBackgroundColor: tview.Styles.ContrastBackgroundColor,
- fieldTextColor: tview.Styles.PrimaryTextColor,
- placeholderTextColor: tview.Styles.ContrastSecondaryTextColor,
- }
-}
-
-// SetText sets the current text of the input field.
-func (field *AdvancedInputField) SetText(text string) *AdvancedInputField {
- field.text = text
- if field.changed != nil {
- field.changed(text)
- }
- return field
-}
-
-// SetTextAndMoveCursor sets the current text of the input field and moves the cursor with the width difference.
-func (field *AdvancedInputField) SetTextAndMoveCursor(text string) *AdvancedInputField {
- oldWidth := runewidth.StringWidth(field.text)
- field.text = text
- newWidth := runewidth.StringWidth(field.text)
- if oldWidth != newWidth {
- field.cursorOffset += newWidth - oldWidth
- }
- if field.changed != nil {
- field.changed(field.text)
- }
- return field
-}
-
-// GetText returns the current text of the input field.
-func (field *AdvancedInputField) GetText() string {
- return field.text
-}
-
-// SetLabel sets the text to be displayed before the input area.
-func (field *AdvancedInputField) SetLabel(label string) *AdvancedInputField {
- field.label = label
- return field
-}
-
-// SetLabelWidth sets the screen width of the label. A value of 0 will cause the
-// primitive to use the width of the label string.
-func (field *AdvancedInputField) SetLabelWidth(width int) *AdvancedInputField {
- field.labelWidth = width
- return field
-}
-
-// GetLabel returns the text to be displayed before the input area.
-func (field *AdvancedInputField) GetLabel() string {
- return field.label
-}
-
-// SetPlaceholder sets the text to be displayed when the input text is empty.
-func (field *AdvancedInputField) SetPlaceholder(text string) *AdvancedInputField {
- field.placeholder = text
- return field
-}
-
-// SetLabelColor sets the color of the label.
-func (field *AdvancedInputField) SetLabelColor(color tcell.Color) *AdvancedInputField {
- field.labelColor = color
- return field
-}
-
-// SetFieldBackgroundColor sets the background color of the input area.
-func (field *AdvancedInputField) SetFieldBackgroundColor(color tcell.Color) *AdvancedInputField {
- field.fieldBackgroundColor = color
- return field
-}
-
-// SetFieldTextColor sets the text color of the input area.
-func (field *AdvancedInputField) SetFieldTextColor(color tcell.Color) *AdvancedInputField {
- field.fieldTextColor = color
- return field
-}
-
-// SetPlaceholderExtColor sets the text color of placeholder text.
-func (field *AdvancedInputField) SetPlaceholderExtColor(color tcell.Color) *AdvancedInputField {
- field.placeholderTextColor = color
- return field
-}
-
-// SetFormAttributes sets attributes shared by all form items.
-func (field *AdvancedInputField) SetFormAttributes(labelWidth int, labelColor, bgColor, fieldTextColor, fieldBgColor tcell.Color) tview.FormItem {
- field.labelWidth = labelWidth
- field.labelColor = labelColor
- field.SetBackgroundColor(bgColor)
- field.fieldTextColor = fieldTextColor
- field.fieldBackgroundColor = fieldBgColor
- return field
-}
-
-// SetFieldWidth sets the screen width of the input area. A value of 0 means
-// extend as much as possible.
-func (field *AdvancedInputField) SetFieldWidth(width int) *AdvancedInputField {
- field.fieldWidth = width
- return field
-}
-
-// GetFieldWidth returns this primitive's field width.
-func (field *AdvancedInputField) GetFieldWidth() int {
- return field.fieldWidth
-}
-
-// SetMaskCharacter sets a character that masks user input on a screen. A value
-// of 0 disables masking.
-func (field *AdvancedInputField) SetMaskCharacter(mask rune) *AdvancedInputField {
- field.maskCharacter = mask
- return field
-}
-
-// SetAcceptanceFunc sets a handler which may reject the last character that was
-// entered (by returning false).
-//
-// This package defines a number of variables Prefixed with AdvancedInputField which may
-// be used for common input (e.g. numbers, maximum text length).
-func (field *AdvancedInputField) SetAcceptanceFunc(handler func(textToCheck string, lastChar rune) bool) *AdvancedInputField {
- field.accept = handler
- return field
-}
-
-// SetChangedFunc sets a handler which is called whenever the text of the input
-// field has changed. It receives the current text (after the change).
-func (field *AdvancedInputField) SetChangedFunc(handler func(text string)) *AdvancedInputField {
- field.changed = handler
- return field
-}
-
-// SetDoneFunc sets a handler which is called when the user is done entering
-// text. The callback function is provided with the key that was pressed, which
-// is one of the following:
-//
-// - KeyEnter: Done entering text.
-// - KeyEscape: Abort text input.
-// - KeyTab: Tab
-// - KeyBacktab: Shift + Tab
-func (field *AdvancedInputField) SetDoneFunc(handler func(key tcell.Key)) *AdvancedInputField {
- field.done = handler
- return field
-}
-
-func (field *AdvancedInputField) SetTabCompleteFunc(handler func(text string, cursorOffset int)) *AdvancedInputField {
- field.tabComplete = handler
- return field
-}
-
-// SetFinishedFunc calls SetDoneFunc().
-func (field *AdvancedInputField) SetFinishedFunc(handler func(key tcell.Key)) tview.FormItem {
- return field.SetDoneFunc(handler)
-}
-
-// drawInput calculates the field width and draws the background.
-func (field *AdvancedInputField) drawInput(screen tcell.Screen, rightLimit, x, y int) (fieldWidth int) {
- fieldWidth = field.fieldWidth
- if fieldWidth == 0 {
- fieldWidth = math.MaxInt32
- }
- if rightLimit-x < fieldWidth {
- fieldWidth = rightLimit - x
- }
- fieldStyle := tcell.StyleDefault.Background(field.fieldBackgroundColor)
- for index := 0; index < fieldWidth; index++ {
- screen.SetContent(x+index, y, ' ', nil, fieldStyle)
- }
- return
-}
-
-// prepareText prepares the text to be displayed and recalculates the view and cursor offsets.
-func (field *AdvancedInputField) prepareText(screen tcell.Screen, fieldWidth, x, y int) (text string) {
- text = field.text
- if text == "" && field.placeholder != "" {
- tview.Print(screen, field.placeholder, x, y, fieldWidth, tview.AlignLeft, field.placeholderTextColor)
- }
-
- if field.maskCharacter > 0 {
- text = strings.Repeat(string(field.maskCharacter), utf8.RuneCountInString(field.text))
- }
- textWidth := runewidth.StringWidth(text)
- if field.cursorOffset >= textWidth {
- fieldWidth--
- }
-
- if field.cursorOffset < field.viewOffset {
- field.viewOffset = field.cursorOffset
- } else if field.cursorOffset > field.viewOffset+fieldWidth {
- field.viewOffset = field.cursorOffset - fieldWidth
- } else if textWidth-field.viewOffset < fieldWidth {
- field.viewOffset = textWidth - fieldWidth
- }
-
- if field.viewOffset < 0 {
- field.viewOffset = 0
- }
-
- return
-}
-
-// drawText draws the text and the cursor.
-func (field *AdvancedInputField) drawText(screen tcell.Screen, fieldWidth, x, y int, text string) {
- runes := []rune(text)
- relPos := 0
- for pos := field.viewOffset; pos <= fieldWidth+field.viewOffset && pos < len(runes); pos++ {
- ch := runes[pos]
- w := runewidth.RuneWidth(ch)
- _, _, style, _ := screen.GetContent(x+relPos, y)
- style = style.Foreground(field.fieldTextColor)
- for w > 0 {
- screen.SetContent(x+relPos, y, ch, nil, style)
- relPos++
- w--
- }
- }
-
- // Set cursor.
- if field.GetFocusable().HasFocus() {
- field.setCursor(screen)
- }
-}
-
-// Draw draws this primitive onto the screen.
-func (field *AdvancedInputField) Draw(screen tcell.Screen) {
- field.Box.Draw(screen)
-
- x, y, width, height := field.GetInnerRect()
- rightLimit := x + width
- if height < 1 || rightLimit <= x {
- return
- }
-
- // Draw label.
- if field.labelWidth > 0 {
- labelWidth := field.labelWidth
- if labelWidth > rightLimit-x {
- labelWidth = rightLimit - x
- }
- tview.Print(screen, field.label, x, y, labelWidth, tview.AlignLeft, field.labelColor)
- x += labelWidth
- } else {
- _, drawnWidth := tview.Print(screen, field.label, x, y, rightLimit-x, tview.AlignLeft, field.labelColor)
- x += drawnWidth
- }
-
- fieldWidth := field.drawInput(screen, rightLimit, x, y)
- text := field.prepareText(screen, fieldWidth, x, y)
- field.drawText(screen, fieldWidth, x, y, text)
-}
-
-func (field *AdvancedInputField) GetCursorOffset() int {
- return field.cursorOffset
-}
-
-func (field *AdvancedInputField) SetCursorOffset(offset int) *AdvancedInputField {
- if offset < 0 {
- offset = 0
- } else {
- width := runewidth.StringWidth(field.text)
- if offset >= width {
- offset = width
- }
- }
- field.cursorOffset = offset
- return field
-}
-
-// setCursor sets the cursor position.
-func (field *AdvancedInputField) setCursor(screen tcell.Screen) {
- x, y, width, _ := field.GetRect()
- origX, origY := x, y
- rightLimit := x + width
- if field.HasBorder() {
- x++
- y++
- rightLimit -= 2
- }
- labelWidth := field.labelWidth
- if labelWidth == 0 {
- labelWidth = tview.StringWidth(field.label)
- }
- x = x + labelWidth + field.cursorOffset - field.viewOffset
- if x >= rightLimit {
- x = rightLimit - 1
- } else if x < origX {
- x = origY
- }
- screen.ShowCursor(x, y)
-}
-
-var (
- lastWord = regexp.MustCompile(`\S+\s*$`)
- firstWord = regexp.MustCompile(`^\s*\S+`)
-)
-
-func SubstringBefore(s string, w int) string {
- return runewidth.Truncate(s, w, "")
-}
-
-func (field *AdvancedInputField) TypeRune(ch rune) {
- leftPart := SubstringBefore(field.text, field.cursorOffset)
- newText := leftPart + string(ch) + field.text[len(leftPart):]
- if field.accept != nil {
- if !field.accept(newText, ch) {
- return
- }
- }
- field.text = newText
- field.cursorOffset += runewidth.RuneWidth(ch)
-}
-
-func (field *AdvancedInputField) PasteClipboard() {
- clip, _ := clipboard.ReadAll("clipboard")
- leftPart := SubstringBefore(field.text, field.cursorOffset)
- field.text = leftPart + clip + field.text[len(leftPart):]
- field.cursorOffset += runewidth.StringWidth(clip)
-}
-
-func (field *AdvancedInputField) MoveCursorLeft(moveWord bool) {
- before := SubstringBefore(field.text, field.cursorOffset)
- if moveWord {
- found := lastWord.FindString(before)
- field.cursorOffset -= runewidth.StringWidth(found)
- } else if len(before) > 0 {
- beforeRunes := []rune(before)
- char := beforeRunes[len(beforeRunes)-1]
- field.cursorOffset -= runewidth.RuneWidth(char)
- }
-}
-
-func (field *AdvancedInputField) MoveCursorRight(moveWord bool) {
- before := SubstringBefore(field.text, field.cursorOffset)
- after := field.text[len(before):]
- if moveWord {
- found := firstWord.FindString(after)
- field.cursorOffset += runewidth.StringWidth(found)
- } else if len(after) > 0 {
- char := []rune(after)[0]
- field.cursorOffset += runewidth.RuneWidth(char)
- }
-}
-
-func (field *AdvancedInputField) RemoveNextCharacter() {
- if field.cursorOffset >= runewidth.StringWidth(field.text) {
- return
- }
- leftPart := SubstringBefore(field.text, field.cursorOffset)
- // Take everything after the left part minus the first character.
- rightPart := string([]rune(field.text[len(leftPart):])[1:])
-
- field.text = leftPart + rightPart
-}
-
-func (field *AdvancedInputField) Clear() {
- field.text = ""
- field.cursorOffset = 0
-}
-
-func (field *AdvancedInputField) RemovePreviousWord() {
- leftPart := SubstringBefore(field.text, field.cursorOffset)
- rightPart := field.text[len(leftPart):]
- replacement := lastWord.ReplaceAllString(leftPart, "")
- field.text = replacement + rightPart
-
- field.cursorOffset -= runewidth.StringWidth(leftPart) - runewidth.StringWidth(replacement)
-}
-
-func (field *AdvancedInputField) RemovePreviousCharacter() {
- if field.cursorOffset == 0 {
- return
- }
- leftPart := SubstringBefore(field.text, field.cursorOffset)
- rightPart := field.text[len(leftPart):]
-
- // Take everything before the right part minus the last character.
- leftPartRunes := []rune(leftPart)
- leftPartRunes = leftPartRunes[0 : len(leftPartRunes)-1]
- leftPart = string(leftPartRunes)
-
- // Figure out what character was removed to correctly decrease cursorOffset.
- removedChar := field.text[len(leftPart) : len(field.text)-len(rightPart)]
-
- field.text = leftPart + rightPart
-
- field.cursorOffset -= runewidth.StringWidth(removedChar)
-}
-
-func (field *AdvancedInputField) TriggerTabComplete() bool {
- if field.tabComplete != nil {
- field.tabComplete(field.text, field.cursorOffset)
- return true
- }
- return false
-}
-
-func (field *AdvancedInputField) handleInputChanges(originalText string) {
- // Trigger changed events.
- if field.text != originalText && field.changed != nil {
- field.changed(field.text)
- }
-
- // Make sure cursor offset is valid
- if field.cursorOffset < 0 {
- field.cursorOffset = 0
- }
- width := runewidth.StringWidth(field.text)
- if field.cursorOffset > width {
- field.cursorOffset = width
- }
-}
-
-// InputHandler returns the handler for this primitive.
-func (field *AdvancedInputField) InputHandler() func(event *tcell.EventKey, setFocus func(p tview.Primitive)) {
- return field.WrapInputHandler(field.inputHandler)
-}
-
-func (field *AdvancedInputField) PasteHandler() func(event *tcell.EventPaste) {
- return field.WrapPasteHandler(field.pasteHandler)
-}
-
-func (field *AdvancedInputField) pasteHandler(event *tcell.EventPaste) {
- defer field.handleInputChanges(field.text)
- clip := event.Text()
- leftPart := SubstringBefore(field.text, field.cursorOffset)
- field.text = leftPart + clip + field.text[len(leftPart):]
- field.cursorOffset += runewidth.StringWidth(clip)
-}
-
-func (field *AdvancedInputField) inputHandler(event *tcell.EventKey, setFocus func(p tview.Primitive)) {
- defer field.handleInputChanges(field.text)
-
- // Process key event.
- switch key := event.Key(); key {
- case tcell.KeyRune:
- field.TypeRune(event.Rune())
- case tcell.KeyCtrlV:
- field.PasteClipboard()
- case tcell.KeyLeft:
- field.MoveCursorLeft(event.Modifiers() == tcell.ModCtrl)
- case tcell.KeyRight:
- field.MoveCursorRight(event.Modifiers() == tcell.ModCtrl)
- case tcell.KeyDelete:
- field.RemoveNextCharacter()
- case tcell.KeyCtrlU:
- if field.vimBindings {
- field.Clear()
- }
- case tcell.KeyCtrlW:
- if field.vimBindings {
- field.RemovePreviousWord()
- }
- case tcell.KeyBackspace:
- field.RemovePreviousWord()
- case tcell.KeyBackspace2:
- field.RemovePreviousCharacter()
- case tcell.KeyTab:
- if field.TriggerTabComplete() {
- break
- }
- fallthrough
- case tcell.KeyEnter, tcell.KeyEscape, tcell.KeyBacktab:
- if field.done != nil {
- field.done(key)
- }
- }
-}
diff --git a/ui/widget/border.go b/ui/widget/border.go
index 8ec772d..f7f367f 100644
--- a/ui/widget/border.go
+++ b/ui/widget/border.go
@@ -17,8 +17,8 @@
package widget
import (
+ "maunium.net/go/mauview"
"maunium.net/go/tcell"
- "maunium.net/go/tview"
)
// Border is a simple tview widget that renders a horizontal or vertical bar.
@@ -27,24 +27,37 @@ import (
// If the height is 1, the bar will be horizontal.
// If the width nor the height are 1, nothing will be rendered.
type Border struct {
- *tview.Box
+ Style tcell.Style
}
// NewBorder wraps a new tview Box into a new Border.
func NewBorder() *Border {
- return &Border{tview.NewBox()}
+ return &Border{
+ Style: tcell.StyleDefault.Foreground(mauview.Styles.BorderColor),
+ }
}
-func (border *Border) Draw(screen tcell.Screen) {
- background := tcell.StyleDefault.Background(border.GetBackgroundColor()).Foreground(border.GetBorderColor())
- x, y, width, height := border.GetRect()
+func (border *Border) Draw(screen mauview.Screen) {
+ width, height := screen.Size()
if width == 1 {
- for borderY := y; borderY < y+height; borderY++ {
- screen.SetContent(x, borderY, tview.Borders.Vertical, nil, background)
+ for borderY := 0; borderY < height; borderY++ {
+ screen.SetContent(0, borderY, mauview.Borders.Vertical, nil, border.Style)
}
} else if height == 1 {
- for borderX := x; borderX < x+width; borderX++ {
- screen.SetContent(borderX, y, tview.Borders.Horizontal, nil, background)
+ for borderX := 0; borderX < width; borderX++ {
+ screen.SetContent(borderX, 0, mauview.Borders.Horizontal, nil, border.Style)
}
}
}
+
+func (border *Border) OnKeyEvent(event mauview.KeyEvent) bool {
+ return false
+}
+
+func (border *Border) OnPasteEvent(event mauview.PasteEvent) bool {
+ return false
+}
+
+func (border *Border) OnMouseEvent(event mauview.MouseEvent) bool {
+ return false
+}
diff --git a/ui/widget/center.go b/ui/widget/center.go
deleted file mode 100644
index cc994bb..0000000
--- a/ui/widget/center.go
+++ /dev/null
@@ -1,70 +0,0 @@
-// gomuks - A terminal Matrix client written in Go.
-// Copyright (C) 2019 Tulir Asokan
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Affero General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Affero General Public License for more details.
-//
-// You should have received a copy of the GNU Affero General Public License
-// along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-package widget
-
-import (
- "maunium.net/go/tcell"
- "maunium.net/go/tview"
-)
-
-// Center wraps the given tview primitive into a Flex element in order to
-// vertically and horizontally center the given primitive.
-func Center(width, height int, p tview.Primitive) tview.Primitive {
- return tview.NewFlex().
- AddItem(nil, 0, 1, false).
- AddItem(tview.NewFlex().
- SetDirection(tview.FlexRow).
- AddItem(nil, 0, 1, false).
- AddItem(p, height, 1, true).
- AddItem(nil, 0, 1, false), width, 1, true).
- AddItem(nil, 0, 1, false)
-}
-
-type transparentCenter struct {
- *tview.Box
- prefWidth, prefHeight int
- p tview.Primitive
-}
-
-func TransparentCenter(width, height int, p tview.Primitive) tview.Primitive {
- return &transparentCenter{
- Box: tview.NewBox(),
- prefWidth: width,
- prefHeight: height,
- p: p,
- }
-}
-
-func (tc *transparentCenter) Draw(screen tcell.Screen) {
- x, y, width, height := tc.GetRect()
- if width > tc.prefWidth {
- x += (width - tc.prefWidth) / 2
- width = tc.prefWidth
- }
- if height > tc.prefHeight {
- y += (height - tc.prefHeight) / 2
- height = tc.prefHeight
- }
- tc.p.SetRect(x, y, width, height)
- tc.p.Draw(screen)
-}
-
-func (tc *transparentCenter) Focus(delegate func(p tview.Primitive)) {
- if delegate != nil {
- delegate(tc.p)
- }
-}
diff --git a/ui/widget/form-text-view.go b/ui/widget/form-text-view.go
deleted file mode 100644
index 2a8683c..0000000
--- a/ui/widget/form-text-view.go
+++ /dev/null
@@ -1,44 +0,0 @@
-// gomuks - A terminal Matrix client written in Go.
-// Copyright (C) 2019 Tulir Asokan
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Affero General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Affero General Public License for more details.
-//
-// You should have received a copy of the GNU Affero General Public License
-// along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-package widget
-
-import (
- "maunium.net/go/tcell"
- "maunium.net/go/tview"
-)
-
-type FormTextView struct {
- *tview.TextView
-}
-
-func (ftv *FormTextView) GetLabel() string {
- return ""
-}
-
-func (ftv *FormTextView) SetFormAttributes(labelWidth int, labelColor, bgColor, fieldTextColor, fieldBgColor tcell.Color) tview.FormItem {
- return ftv
-}
-
-func (ftv *FormTextView) GetFieldWidth() int {
- _, _, w, _ := ftv.TextView.GetRect()
- return w
-}
-
-func (ftv *FormTextView) SetFinishedFunc(handler func(key tcell.Key)) tview.FormItem {
- ftv.SetDoneFunc(handler)
- return ftv
-}
diff --git a/ui/widget/util.go b/ui/widget/util.go
index ed51735..6a2173d 100644
--- a/ui/widget/util.go
+++ b/ui/widget/util.go
@@ -18,28 +18,31 @@ package widget
import (
"fmt"
+ "strconv"
+
"github.com/mattn/go-runewidth"
+
+ "maunium.net/go/mauview"
"maunium.net/go/tcell"
- "maunium.net/go/tview"
- "strconv"
)
-func WriteLineSimple(screen tcell.Screen, line string, x, y int) {
- WriteLine(screen, tview.AlignLeft, line, x, y, 1<<30, tcell.StyleDefault)
+func WriteLineSimple(screen mauview.Screen, line string, x, y int) {
+ WriteLine(screen, mauview.AlignLeft, line, x, y, 1<<30, tcell.StyleDefault)
}
-func WriteLineSimpleColor(screen tcell.Screen, line string, x, y int, color tcell.Color) {
- WriteLine(screen, tview.AlignLeft, line, x, y, 1<<30, tcell.StyleDefault.Foreground(color))
+func WriteLineSimpleColor(screen mauview.Screen, line string, x, y int, color tcell.Color) {
+ WriteLine(screen, mauview.AlignLeft, line, x, y, 1<<30, tcell.StyleDefault.Foreground(color))
}
-func WriteLineColor(screen tcell.Screen, align int, line string, x, y, maxWidth int, color tcell.Color) {
+func WriteLineColor(screen mauview.Screen, align int, line string, x, y, maxWidth int, color tcell.Color) {
WriteLine(screen, align, line, x, y, maxWidth, tcell.StyleDefault.Foreground(color))
}
-func WriteLine(screen tcell.Screen, align int, line string, x, y, maxWidth int, style tcell.Style) {
+func WriteLine(screen mauview.Screen, align int, line string, x, y, maxWidth int, style tcell.Style) {
offsetX := 0
- if align == tview.AlignRight {
- offsetX = maxWidth - runewidth.StringWidth(line)
+ if align == mauview.AlignRight {
+ // TODO is mauview.StringWidth correct here?
+ offsetX = maxWidth - mauview.StringWidth(line)
}
if offsetX < 0 {
offsetX = 0
@@ -60,12 +63,12 @@ func WriteLine(screen tcell.Screen, align int, line string, x, y, maxWidth int,
}
}
-func WriteLinePadded(screen tcell.Screen, align int, line string, x, y, maxWidth int, style tcell.Style) {
+func WriteLinePadded(screen mauview.Screen, align int, line string, x, y, maxWidth int, style tcell.Style) {
padding := strconv.Itoa(maxWidth)
- if align == tview.AlignRight {
+ if align == mauview.AlignRight {
line = fmt.Sprintf("%"+padding+"s", line)
} else {
line = fmt.Sprintf("%-"+padding+"s", line)
}
- WriteLine(screen, tview.AlignLeft, line, x, y, maxWidth, style)
+ WriteLine(screen, mauview.AlignLeft, line, x, y, maxWidth, style)
}