aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--interface/matrix.go2
-rw-r--r--interface/ui.go2
-rw-r--r--lib/ansimage/ansimage.go287
-rw-r--r--matrix/matrix.go25
-rw-r--r--ui/message-view.go12
-rw-r--r--ui/messages/imagemessage.go26
-rw-r--r--ui/messages/message.go3
-rw-r--r--ui/messages/meta.go2
-rw-r--r--ui/messages/parser.go19
-rw-r--r--ui/messages/textmessage.go31
-rw-r--r--ui/messages/tstring/cell.go (renamed from ui/messages/cell.go)4
-rw-r--r--ui/messages/tstring/string.go (renamed from ui/messages/string.go)32
-rw-r--r--ui/room-list.go2
-rw-r--r--ui/room-view.go2
-rw-r--r--ui/ui.go2
-rw-r--r--ui/view-main.go7
-rw-r--r--ui/widget/advanced-inputfield.go2
-rw-r--r--ui/widget/border.go2
-rw-r--r--ui/widget/color.go2
-rw-r--r--ui/widget/util.go2
20 files changed, 378 insertions, 88 deletions
diff --git a/interface/matrix.go b/interface/matrix.go
index b7b52c3..cf011cf 100644
--- a/interface/matrix.go
+++ b/interface/matrix.go
@@ -34,5 +34,5 @@ type MatrixContainer interface {
LeaveRoom(roomID string) error
GetHistory(roomID, prevBatch string, limit int) ([]gomatrix.Event, string, error)
GetRoom(roomID string) *rooms.Room
- Download(mxcURL string) ([]byte, error)
+ Download(mxcURL string) ([]byte, string, error)
}
diff --git a/interface/ui.go b/interface/ui.go
index df92308..8d3836d 100644
--- a/interface/ui.go
+++ b/interface/ui.go
@@ -19,7 +19,7 @@ package ifc
import (
"time"
- "github.com/gdamore/tcell"
+ "maunium.net/go/tcell"
"maunium.net/go/gomatrix"
"maunium.net/go/gomuks/matrix/pushrules"
"maunium.net/go/gomuks/matrix/rooms"
diff --git a/lib/ansimage/ansimage.go b/lib/ansimage/ansimage.go
new file mode 100644
index 0000000..a41c833
--- /dev/null
+++ b/lib/ansimage/ansimage.go
@@ -0,0 +1,287 @@
+// ___ _____ ____
+// / _ \/ _/ |/_/ /____ ______ _
+// / ___// /_> </ __/ -_) __/ ' \
+// /_/ /___/_/|_|\__/\__/_/ /_/_/_/
+//
+// Copyright 2017 Eliuk Blau
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package ansimage
+
+import (
+ "errors"
+ "image"
+ "image/color"
+ "image/draw"
+ _ "image/gif" // initialize decoder
+ _ "image/jpeg" // initialize decoder
+ _ "image/png" // initialize decoder
+ "io"
+ "os"
+
+ "github.com/disintegration/imaging"
+ _ "golang.org/x/image/bmp" // initialize decoder
+ _ "golang.org/x/image/tiff" // initialize decoder
+ _ "golang.org/x/image/webp" // initialize decoder
+ "maunium.net/go/gomuks/ui/messages/tstring"
+ "maunium.net/go/tcell"
+)
+
+var (
+ // ErrHeightNonMoT happens when ANSImage height is not a Multiple of Two value.
+ ErrHeightNonMoT = errors.New("ANSImage: height must be a Multiple of Two value")
+
+ // ErrInvalidBoundsMoT happens when ANSImage height or width are invalid values (Multiple of Two).
+ ErrInvalidBoundsMoT = errors.New("ANSImage: height or width must be >=2")
+
+ // ErrOutOfBounds happens when ANSI-pixel coordinates are out of ANSImage bounds.
+ ErrOutOfBounds = errors.New("ANSImage: out of bounds")
+)
+
+// ANSIpixel represents a pixel of an ANSImage.
+type ANSIpixel struct {
+ Brightness uint8
+ R, G, B uint8
+ upper bool
+ source *ANSImage
+}
+
+// ANSImage represents an image encoded in ANSI escape codes.
+type ANSImage struct {
+ h, w int
+ maxprocs int
+ bgR uint8
+ bgG uint8
+ bgB uint8
+ pixmap [][]*ANSIpixel
+}
+
+func (ai *ANSImage) Pixmap() [][]*ANSIpixel {
+ return ai.pixmap
+}
+
+// Height gets total rows of ANSImage.
+func (ai *ANSImage) Height() int {
+ return ai.h
+}
+
+// Width gets total columns of ANSImage.
+func (ai *ANSImage) Width() int {
+ return ai.w
+}
+
+// SetMaxProcs sets the maximum number of parallel goroutines to render the ANSImage
+// (user should manually sets `runtime.GOMAXPROCS(max)` before to this change takes effect).
+func (ai *ANSImage) SetMaxProcs(max int) {
+ ai.maxprocs = max
+}
+
+// GetMaxProcs gets the maximum number of parallels goroutines to render the ANSImage.
+func (ai *ANSImage) GetMaxProcs() int {
+ return ai.maxprocs
+}
+
+// SetAt sets ANSI-pixel color (RBG) and brightness in coordinates (y,x).
+func (ai *ANSImage) SetAt(y, x int, r, g, b, brightness uint8) error {
+ if y >= 0 && y < ai.h && x >= 0 && x < ai.w {
+ ai.pixmap[y][x].R = r
+ ai.pixmap[y][x].G = g
+ ai.pixmap[y][x].B = b
+ ai.pixmap[y][x].Brightness = brightness
+ ai.pixmap[y][x].upper = y%2 == 0
+ return nil
+ }
+ return ErrOutOfBounds
+}
+
+// GetAt gets ANSI-pixel in coordinates (y,x).
+func (ai *ANSImage) GetAt(y, x int) (*ANSIpixel, error) {
+ if y >= 0 && y < ai.h && x >= 0 && x < ai.w {
+ return &ANSIpixel{
+ R: ai.pixmap[y][x].R,
+ G: ai.pixmap[y][x].G,
+ B: ai.pixmap[y][x].B,
+ Brightness: ai.pixmap[y][x].Brightness,
+ upper: ai.pixmap[y][x].upper,
+ source: ai.pixmap[y][x].source,
+ },
+ nil
+ }
+ return nil, ErrOutOfBounds
+}
+
+// Render returns the ANSI-compatible string form of ANSImage.
+// (Nice info for ANSI True Colour - https://gist.github.com/XVilka/8346728)
+func (ai *ANSImage) Render() []tstring.TString {
+ type renderData struct {
+ row int
+ render tstring.TString
+ }
+
+ rows := make([]tstring.TString, ai.h/2)
+ for y := 0; y < ai.h; y += ai.maxprocs {
+ ch := make(chan renderData, ai.maxprocs)
+ for n, r := 0, y+1; (n <= ai.maxprocs) && (2*r+1 < ai.h); n, r = n+1, y+n+1 {
+ go func(r, y int) {
+ str := make(tstring.TString, ai.w)
+ for x := 0; x < ai.w; x++ {
+ topPixel := ai.pixmap[y][x]
+ topColor := tcell.NewRGBColor(int32(topPixel.R), int32(topPixel.G), int32(topPixel.B))
+
+ bottomPixel := ai.pixmap[y+1][x]
+ bottomColor := tcell.NewRGBColor(int32(bottomPixel.R), int32(bottomPixel.G), int32(bottomPixel.B))
+
+ str[x] = tstring.Cell{
+ Char: '▄',
+ Style: tcell.StyleDefault.Background(topColor).Foreground(bottomColor),
+ }
+ }
+ ch <- renderData{row: r, render: str}
+ }(r, 2*r)
+ }
+ for n, r := 0, y+1; (n <= ai.maxprocs) && (2*r+1 < ai.h); n, r = n+1, y+n+1 {
+ data := <-ch
+ rows[data.row] = data.render
+ }
+ }
+ return rows
+}
+
+// New creates a new empty ANSImage ready to draw on it.
+func New(h, w int, bg color.Color) (*ANSImage, error) {
+ if h%2 != 0 {
+ return nil, ErrHeightNonMoT
+ }
+
+ if h < 2 || w < 2 {
+ return nil, ErrInvalidBoundsMoT
+ }
+
+ r, g, b, _ := bg.RGBA()
+ ansimage := &ANSImage{
+ h: h, w: w,
+ maxprocs: 1,
+ bgR: uint8(r),
+ bgG: uint8(g),
+ bgB: uint8(b),
+ pixmap: nil,
+ }
+
+ ansimage.pixmap = func() [][]*ANSIpixel {
+ v := make([][]*ANSIpixel, h)
+ for y := 0; y < h; y++ {
+ v[y] = make([]*ANSIpixel, w)
+ for x := 0; x < w; x++ {
+ v[y][x] = &ANSIpixel{
+ R: 0,
+ G: 0,
+ B: 0,
+ Brightness: 0,
+ source: ansimage,
+ upper: y%2 == 0,
+ }
+ }
+ }
+ return v
+ }()
+
+ return ansimage, nil
+}
+
+// NewFromReader creates a new ANSImage from an io.Reader.
+// Background color is used to fill when image has transparency or dithering mode is enabled
+// Dithering mode is used to specify the way that ANSImage render ANSI-pixels (char/block elements).
+func NewFromReader(reader io.Reader, bg color.Color) (*ANSImage, error) {
+ img, _, err := image.Decode(reader)
+ if err != nil {
+ return nil, err
+ }
+
+ return createANSImage(img, bg)
+}
+
+// NewScaledFromReader creates a new scaled ANSImage from an io.Reader.
+// Background color is used to fill when image has transparency or dithering mode is enabled
+// Dithering mode is used to specify the way that ANSImage render ANSI-pixels (char/block elements).
+func NewScaledFromReader(reader io.Reader, y, x int, bg color.Color) (*ANSImage, error) {
+ img, _, err := image.Decode(reader)
+ if err != nil {
+ return nil, err
+ }
+
+ img = imaging.Resize(img, x, y, imaging.Lanczos)
+
+ return createANSImage(img, bg)
+}
+
+// NewFromFile creates a new ANSImage from a file.
+// Background color is used to fill when image has transparency or dithering mode is enabled
+// Dithering mode is used to specify the way that ANSImage render ANSI-pixels (char/block elements).
+func NewFromFile(name string, bg color.Color) (*ANSImage, error) {
+ reader, err := os.Open(name)
+ if err != nil {
+ return nil, err
+ }
+ defer reader.Close()
+ return NewFromReader(reader, bg)
+}
+
+// NewScaledFromFile creates a new scaled ANSImage from a file.
+// Background color is used to fill when image has transparency or dithering mode is enabled
+// Dithering mode is used to specify the way that ANSImage render ANSI-pixels (char/block elements).
+func NewScaledFromFile(name string, y, x int, bg color.Color) (*ANSImage, error) {
+ reader, err := os.Open(name)
+ if err != nil {
+ return nil, err
+ }
+ defer reader.Close()
+ return NewScaledFromReader(reader, y, x, bg)
+}
+
+// createANSImage loads data from an image and returns an ANSImage.
+// Background color is used to fill when image has transparency or dithering mode is enabled
+// Dithering mode is used to specify the way that ANSImage render ANSI-pixels (char/block elements).
+func createANSImage(img image.Image, bg color.Color) (*ANSImage, error) {
+ var rgbaOut *image.RGBA
+ bounds := img.Bounds()
+
+ // do compositing only if background color has no transparency (thank you @disq for the idea!)
+ // (info - http://stackoverflow.com/questions/36595687/transparent-pixel-color-go-lang-image)
+ if _, _, _, a := bg.RGBA(); a >= 0xffff {
+ rgbaOut = image.NewRGBA(bounds)
+ draw.Draw(rgbaOut, bounds, image.NewUniform(bg), image.ZP, draw.Src)
+ draw.Draw(rgbaOut, bounds, img, image.ZP, draw.Over)
+ } else {
+ if v, ok := img.(*image.RGBA); ok {
+ rgbaOut = v
+ } else {
+ rgbaOut = image.NewRGBA(bounds)
+ draw.Draw(rgbaOut, bounds, img, image.ZP, draw.Src)
+ }
+ }
+
+ yMin, xMin := bounds.Min.Y, bounds.Min.X
+ yMax, xMax := bounds.Max.Y, bounds.Max.X
+
+ // always sets an even number of ANSIPixel rows...
+ yMax = yMax - yMax%2 // one for upper pixel and another for lower pixel --> without dithering
+
+ ansimage, err := New(yMax, xMax, bg)
+ if err != nil {
+ return nil, err
+ }
+
+ for y := yMin; y < yMax; y++ {
+ for x := xMin; x < xMax; x++ {
+ v := rgbaOut.RGBAAt(x, y)
+ if err := ansimage.SetAt(y, x, v.R, v.G, v.B, 0); err != nil {
+ return nil, err
+ }
+ }
+ }
+
+ return ansimage, nil
+}
diff --git a/matrix/matrix.go b/matrix/matrix.go
index 29419b0..b4d5775 100644
--- a/matrix/matrix.go
+++ b/matrix/matrix.go
@@ -22,6 +22,7 @@ import (
"fmt"
"io"
"io/ioutil"
+ "net/http"
"net/url"
"os"
"path"
@@ -412,42 +413,44 @@ func (c *Container) GetRoom(roomID string) *rooms.Room {
var mxcRegex = regexp.MustCompile("mxc://(.+)/(.+)")
-func (c *Container) Download(mxcURL string) ([]byte, error) {
+func (c *Container) Download(mxcURL string) (data []byte, fullID string, err error) {
parts := mxcRegex.FindStringSubmatch(mxcURL)
if parts == nil || len(parts) != 3 {
- debug.Print(parts)
- return nil, fmt.Errorf("invalid matrix content URL")
+ err = fmt.Errorf("invalid matrix content URL")
+ return
}
hs := parts[1]
id := parts[2]
+ fullID = fmt.Sprintf("%s/%s", hs, id)
cacheFile := c.getCachePath(hs, id)
- if _, err := os.Stat(cacheFile); err != nil {
- data, err := ioutil.ReadFile(cacheFile)
+ if _, err = os.Stat(cacheFile); err != nil {
+ data, err = ioutil.ReadFile(cacheFile)
if err == nil {
- return data, nil
+ return
}
}
dlURL, _ := url.Parse(c.client.HomeserverURL.String())
dlURL.Path = path.Join(dlURL.Path, "/_matrix/media/v1/download", hs, id)
- resp, err := c.client.Client.Get(dlURL.String())
+ var resp *http.Response
+ resp, err = c.client.Client.Get(dlURL.String())
if err != nil {
- return nil, err
+ return
}
defer resp.Body.Close()
var buf bytes.Buffer
_, err = io.Copy(&buf, resp.Body)
if err != nil {
- return nil, err
+ return
}
- data := buf.Bytes()
+ data = buf.Bytes()
err = ioutil.WriteFile(cacheFile, data, 0600)
- return data, err
+ return
}
func (c *Container) getCachePath(homeserver, fileID string) string {
dir := filepath.Join(c.config.MediaDir, homeserver)
diff --git a/ui/message-view.go b/ui/message-view.go
index dd3edb5..87dc993 100644
--- a/ui/message-view.go
+++ b/ui/message-view.go
@@ -22,7 +22,8 @@ import (
"math"
"os"
- "github.com/gdamore/tcell"
+ "maunium.net/go/gomuks/ui/messages/tstring"
+ "maunium.net/go/tcell"
"maunium.net/go/gomuks/debug"
"maunium.net/go/gomuks/interface"
"maunium.net/go/gomuks/ui/messages"
@@ -48,7 +49,7 @@ type MessageView struct {
messageIDs map[string]messages.UIMessage
messages []messages.UIMessage
- textBuffer []messages.UIString
+ textBuffer []tstring.TString
metaBuffer []ifc.MessageMeta
}
@@ -61,7 +62,7 @@ func NewMessageView() *MessageView {
messages: make([]messages.UIMessage, 0),
messageIDs: make(map[string]messages.UIMessage),
- textBuffer: make([]messages.UIString, 0),
+ textBuffer: make([]tstring.TString, 0),
metaBuffer: make([]ifc.MessageMeta, 0),
widestSender: 5,
@@ -183,7 +184,7 @@ func (view *MessageView) appendBuffer(message messages.UIMessage) {
if len(view.metaBuffer) > 0 {
prevMeta := view.metaBuffer[len(view.metaBuffer)-1]
if prevMeta != nil && prevMeta.FormatDate() != message.FormatDate() {
- view.textBuffer = append(view.textBuffer, messages.NewColorUIString(
+ view.textBuffer = append(view.textBuffer, tstring.NewColorTString(
fmt.Sprintf("Date changed to %s", message.FormatDate()),
tcell.ColorGreen))
view.metaBuffer = append(view.metaBuffer, &messages.BasicMeta{
@@ -218,7 +219,6 @@ func (view *MessageView) replaceBuffer(message messages.UIMessage) {
view.textBuffer = append(append(view.textBuffer[0:start], message.Buffer()...), view.textBuffer[end:]...)
if len(message.Buffer()) != end-start+1 {
- debug.Print(end, "-", start, "!=", len(message.Buffer()))
metaBuffer := view.metaBuffer[0:start]
for range message.Buffer() {
metaBuffer = append(metaBuffer, message)
@@ -233,7 +233,7 @@ func (view *MessageView) recalculateBuffers() {
width -= view.TimestampWidth + TimestampSenderGap + view.widestSender + SenderMessageGap
recalculateMessageBuffers := width != view.prevWidth
if height != view.prevHeight || recalculateMessageBuffers || len(view.messages) != view.prevMsgCount {
- view.textBuffer = []messages.UIString{}
+ view.textBuffer = []tstring.TString{}
view.metaBuffer = []ifc.MessageMeta{}
view.prevMsgCount = 0
for i, message := range view.messages {
diff --git a/ui/messages/imagemessage.go b/ui/messages/imagemessage.go
index 53c0588..7129e5a 100644
--- a/ui/messages/imagemessage.go
+++ b/ui/messages/imagemessage.go
@@ -23,11 +23,12 @@ import (
"image/color"
- "github.com/gdamore/tcell"
"maunium.net/go/gomuks/debug"
"maunium.net/go/gomuks/interface"
+ "maunium.net/go/gomuks/ui/messages/tstring"
"maunium.net/go/gomuks/ui/widget"
- "maunium.net/go/pixterm/ansimage"
+ "maunium.net/go/gomuks/lib/ansimage"
+ "maunium.net/go/tcell"
)
func init() {
@@ -36,11 +37,12 @@ func init() {
type UIImageMessage struct {
UITextMessage
+ Path string
data []byte
}
// NewImageMessage creates a new UIImageMessage object with the provided values and the default state.
-func NewImageMessage(id, sender, msgtype string, data []byte, timestamp time.Time) UIMessage {
+func NewImageMessage(id, sender, msgtype string, path string, data []byte, timestamp time.Time) UIMessage {
return &UIImageMessage{
UITextMessage{
MsgSender: sender,
@@ -53,6 +55,7 @@ func NewImageMessage(id, sender, msgtype string, data []byte, timestamp time.Tim
MsgIsHighlight: false,
MsgIsService: false,
},
+ path,
data,
}
}
@@ -90,24 +93,13 @@ func (msg *UIImageMessage) CalculateBuffer(width int) {
return
}
- image, err := ansimage.NewScaledFromReader(bytes.NewReader(msg.data), -1, width, color.Black, ansimage.ScaleModeResize, ansimage.NoDithering)
+ image, err := ansimage.NewScaledFromReader(bytes.NewReader(msg.data), 0, width, color.Black)
if err != nil {
- msg.buffer = []UIString{NewColorUIString("Failed to display image", tcell.ColorRed)}
+ msg.buffer = []tstring.TString{tstring.NewColorTString("Failed to display image", tcell.ColorRed)}
debug.Print("Failed to display image:", err)
return
}
- msg.buffer = make([]UIString, image.Height())
- pixels := image.Pixmap()
- for row, pixelRow := range pixels {
- msg.buffer[row] = make(UIString, len(pixelRow))
- for column, pixel := range pixelRow {
- pixelColor := tcell.NewRGBColor(int32(pixel.R), int32(pixel.G), int32(pixel.B))
- msg.buffer[row][column] = Cell{
- Char: ' ',
- Style: tcell.StyleDefault.Background(pixelColor),
- }
- }
- }
+ msg.buffer = image.Render()
msg.prevBufferWidth = width
}
diff --git a/ui/messages/message.go b/ui/messages/message.go
index f116d84..ac118ad 100644
--- a/ui/messages/message.go
+++ b/ui/messages/message.go
@@ -18,6 +18,7 @@ package messages
import (
"maunium.net/go/gomuks/interface"
+ "maunium.net/go/gomuks/ui/messages/tstring"
)
// Message is a wrapper for the content and metadata of a Matrix message intended to be displayed.
@@ -26,7 +27,7 @@ type UIMessage interface {
CalculateBuffer(width int)
RecalculateBuffer()
- Buffer() []UIString
+ Buffer() []tstring.TString
Height() int
RealSender() string
diff --git a/ui/messages/meta.go b/ui/messages/meta.go
index 3f9c9ab..7e2f29f 100644
--- a/ui/messages/meta.go
+++ b/ui/messages/meta.go
@@ -19,7 +19,7 @@ package messages
import (
"time"
- "github.com/gdamore/tcell"
+ "maunium.net/go/tcell"
"maunium.net/go/gomuks/interface"
)
diff --git a/ui/messages/parser.go b/ui/messages/parser.go
index 4300c86..22ba4e7 100644
--- a/ui/messages/parser.go
+++ b/ui/messages/parser.go
@@ -20,12 +20,13 @@ import (
"fmt"
"time"
- "github.com/gdamore/tcell"
"maunium.net/go/gomatrix"
"maunium.net/go/gomuks/debug"
"maunium.net/go/gomuks/interface"
"maunium.net/go/gomuks/matrix/rooms"
+ "maunium.net/go/gomuks/ui/messages/tstring"
"maunium.net/go/gomuks/ui/widget"
+ "maunium.net/go/tcell"
)
func ParseEvent(mx ifc.MatrixContainer, room *rooms.Room, evt *gomatrix.Event) UIMessage {
@@ -59,16 +60,16 @@ func ParseMessage(mx ifc.MatrixContainer, evt *gomatrix.Event) UIMessage {
return NewTextMessage(evt.ID, evt.Sender, msgtype, text, ts)
case "m.image":
url, _ := evt.Content["url"].(string)
- data, err := mx.Download(url)
+ data, path, err := mx.Download(url)
if err != nil {
debug.Printf("Failed to download %s: %v", url, err)
}
- return NewImageMessage(evt.ID, evt.Sender, msgtype, data, ts)
+ return NewImageMessage(evt.ID, evt.Sender, msgtype, path, data, ts)
}
return nil
}
-func getMembershipEventContent(evt *gomatrix.Event) (sender string, text UIString) {
+func getMembershipEventContent(evt *gomatrix.Event) (sender string, text tstring.TString) {
membership, _ := evt.Content["membership"].(string)
displayname, _ := evt.Content["displayname"].(string)
if len(displayname) == 0 {
@@ -85,28 +86,28 @@ func getMembershipEventContent(evt *gomatrix.Event) (sender string, text UIStrin
switch membership {
case "invite":
sender = "---"
- text = NewColorUIString(fmt.Sprintf("%s invited %s.", evt.Sender, displayname), tcell.ColorYellow)
+ text = tstring.NewColorTString(fmt.Sprintf("%s invited %s.", evt.Sender, displayname), tcell.ColorYellow)
text.Colorize(0, len(evt.Sender), widget.GetHashColor(evt.Sender))
text.Colorize(len(evt.Sender)+len(" invited "), len(displayname), widget.GetHashColor(displayname))
case "join":
sender = "-->"
- text = NewColorUIString(fmt.Sprintf("%s joined the room.", displayname), tcell.ColorGreen)
+ text = tstring.NewColorTString(fmt.Sprintf("%s joined the room.", displayname), tcell.ColorGreen)
text.Colorize(0, len(displayname), widget.GetHashColor(displayname))
case "leave":
sender = "<--"
if evt.Sender != *evt.StateKey {
reason, _ := evt.Content["reason"].(string)
- text = NewColorUIString(fmt.Sprintf("%s kicked %s: %s", evt.Sender, displayname, reason), tcell.ColorRed)
+ text = tstring.NewColorTString(fmt.Sprintf("%s kicked %s: %s", evt.Sender, displayname, reason), tcell.ColorRed)
text.Colorize(0, len(evt.Sender), widget.GetHashColor(evt.Sender))
text.Colorize(len(evt.Sender)+len(" kicked "), len(displayname), widget.GetHashColor(displayname))
} else {
- text = NewColorUIString(fmt.Sprintf("%s left the room.", displayname), tcell.ColorRed)
+ text = tstring.NewColorTString(fmt.Sprintf("%s left the room.", displayname), tcell.ColorRed)
text.Colorize(0, len(displayname), widget.GetHashColor(displayname))
}
}
} else if displayname != prevDisplayname {
sender = "---"
- text = NewColorUIString(fmt.Sprintf("%s changed their display name to %s.", prevDisplayname, displayname), tcell.ColorYellow)
+ text = tstring.NewColorTString(fmt.Sprintf("%s changed their display name to %s.", prevDisplayname, displayname), tcell.ColorYellow)
text.Colorize(0, len(prevDisplayname), widget.GetHashColor(prevDisplayname))
text.Colorize(len(prevDisplayname)+len(" changed their display name to "), len(displayname), widget.GetHashColor(displayname))
}
diff --git a/ui/messages/textmessage.go b/ui/messages/textmessage.go
index 8ad3168..12b098d 100644
--- a/ui/messages/textmessage.go
+++ b/ui/messages/textmessage.go
@@ -22,7 +22,8 @@ import (
"regexp"
"time"
- "github.com/gdamore/tcell"
+ "maunium.net/go/gomuks/ui/messages/tstring"
+ "maunium.net/go/tcell"
"maunium.net/go/gomuks/interface"
"maunium.net/go/gomuks/ui/widget"
)
@@ -34,11 +35,11 @@ func init() {
type UIExpandedTextMessage struct {
UITextMessage
- MsgUIStringText UIString
+ MsgTStringText tstring.TString
}
// NewExpandedTextMessage creates a new UIExpandedTextMessage object with the provided values and the default state.
-func NewExpandedTextMessage(id, sender, msgtype string, text UIString, timestamp time.Time) UIMessage {
+func NewExpandedTextMessage(id, sender, msgtype string, text tstring.TString, timestamp time.Time) UIMessage {
return &UIExpandedTextMessage{
UITextMessage{
MsgSender: sender,
@@ -56,8 +57,8 @@ func NewExpandedTextMessage(id, sender, msgtype string, text UIString, timestamp
}
}
-func (msg *UIExpandedTextMessage) GetUIStringText() UIString {
- return msg.MsgUIStringText
+func (msg *UIExpandedTextMessage) GetTStringText() tstring.TString {
+ return msg.MsgTStringText
}
// CopyFrom replaces the content of this message object with the content of the given object.
@@ -78,9 +79,9 @@ func (msg *UIExpandedTextMessage) CopyFrom(from ifc.MessageMeta) {
fromExpandedMsg, ok := from.(*UIExpandedTextMessage)
if ok {
- msg.MsgUIStringText = fromExpandedMsg.MsgUIStringText
+ msg.MsgTStringText = fromExpandedMsg.MsgTStringText
} else {
- msg.MsgUIStringText = NewColorUIString(fromMsg.Text(), from.TextColor())
+ msg.MsgTStringText = tstring.NewColorTString(fromMsg.Text(), from.TextColor())
}
msg.RecalculateBuffer()
@@ -97,7 +98,7 @@ type UITextMessage struct {
MsgState ifc.MessageState
MsgIsHighlight bool
MsgIsService bool
- buffer []UIString
+ buffer []tstring.TString
prevBufferWidth int
}
@@ -235,7 +236,7 @@ func (msg *UITextMessage) RecalculateBuffer() {
//
// N.B. This will NOT automatically calculate the buffer if it hasn't been
// calculated already, as that requires the target width.
-func (msg *UITextMessage) Buffer() []UIString {
+func (msg *UITextMessage) Buffer() []tstring.TString {
return msg.buffer
}
@@ -307,8 +308,8 @@ func (msg *UITextMessage) SetIsService(isService bool) {
msg.MsgIsService = isService
}
-func (msg *UITextMessage) GetUIStringText() UIString {
- return NewColorUIString(msg.Text(), msg.TextColor())
+func (msg *UITextMessage) GetTStringText() tstring.TString {
+ return tstring.NewColorTString(msg.Text(), msg.TextColor())
}
// Regular expressions used to split lines when calculating the buffer.
@@ -327,10 +328,10 @@ func (msg *UITextMessage) CalculateBuffer(width int) {
return
}
- msg.buffer = []UIString{}
- text := msg.GetUIStringText()
+ msg.buffer = []tstring.TString{}
+ text := msg.GetTStringText()
if msg.MsgType == "m.emote" {
- text = NewColorUIString(fmt.Sprintf("* %s %s", msg.MsgSender, text.String()), msg.TextColor())
+ text = tstring.NewColorTString(fmt.Sprintf("* %s %s", msg.MsgSender, text.String()), msg.TextColor())
text.Colorize(2, len(msg.MsgSender), msg.SenderColor())
}
@@ -338,7 +339,7 @@ func (msg *UITextMessage) CalculateBuffer(width int) {
newlines := 0
for _, str := range forcedLinebreaks {
if len(str) == 0 && newlines < 1 {
- msg.buffer = append(msg.buffer, UIString{})
+ msg.buffer = append(msg.buffer, tstring.TString{})
newlines++
} else {
newlines = 0
diff --git a/ui/messages/cell.go b/ui/messages/tstring/cell.go
index a919da7..8a400ee 100644
--- a/ui/messages/cell.go
+++ b/ui/messages/tstring/cell.go
@@ -14,10 +14,10 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
-package messages
+package tstring
import (
- "github.com/gdamore/tcell"
+ "maunium.net/go/tcell"
"github.com/mattn/go-runewidth"
)
diff --git a/ui/messages/string.go b/ui/messages/tstring/string.go
index 7c3143b..ad0b2c8 100644
--- a/ui/messages/string.go
+++ b/ui/messages/tstring/string.go
@@ -14,18 +14,18 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
-package messages
+package tstring
import (
"strings"
- "github.com/gdamore/tcell"
+ "maunium.net/go/tcell"
"github.com/mattn/go-runewidth"
)
-type UIString []Cell
+type TString []Cell
-func NewUIString(str string) UIString {
+func NewTString(str string) TString {
newStr := make([]Cell, len(str))
for i, char := range str {
newStr[i] = NewCell(char)
@@ -33,7 +33,7 @@ func NewUIString(str string) UIString {
return newStr
}
-func NewColorUIString(str string, color tcell.Color) UIString {
+func NewColorTString(str string, color tcell.Color) TString {
newStr := make([]Cell, len(str))
for i, char := range str {
newStr[i] = NewColorCell(char, color)
@@ -41,7 +41,7 @@ func NewColorUIString(str string, color tcell.Color) UIString {
return newStr
}
-func NewStyleUIString(str string, style tcell.Style) UIString {
+func NewStyleTString(str string, style tcell.Style) TString {
newStr := make([]Cell, len(str))
for i, char := range str {
newStr[i] = NewStyleCell(char, style)
@@ -49,27 +49,27 @@ func NewStyleUIString(str string, style tcell.Style) UIString {
return newStr
}
-func (str UIString) Colorize(from, length int, color tcell.Color) {
+func (str TString) Colorize(from, length int, color tcell.Color) {
for i := from; i < from+length; i++ {
str[i].Style = str[i].Style.Foreground(color)
}
}
-func (str UIString) Draw(screen tcell.Screen, x, y int) {
+func (str TString) Draw(screen tcell.Screen, x, y int) {
offsetX := 0
for _, cell := range str {
offsetX += cell.Draw(screen, x+offsetX, y)
}
}
-func (str UIString) RuneWidth() (width int) {
+func (str TString) RuneWidth() (width int) {
for _, cell := range str {
width += runewidth.RuneWidth(cell.Char)
}
return width
}
-func (str UIString) String() string {
+func (str TString) String() string {
var buf strings.Builder
for _, cell := range str {
buf.WriteRune(cell.Char)
@@ -78,7 +78,7 @@ func (str UIString) String() string {
}
// Truncate return string truncated with w cells
-func (str UIString) Truncate(w int) UIString {
+func (str TString) Truncate(w int) TString {
if str.RuneWidth() <= w {
return str[:]
}
@@ -94,7 +94,7 @@ func (str UIString) Truncate(w int) UIString {
return str[0:i]
}
-func (str UIString) IndexFrom(r rune, from int) int {
+func (str TString) IndexFrom(r rune, from int) int {
for i := from; i < len(str); i++ {
if str[i].Char == r {
return i
@@ -103,11 +103,11 @@ func (str UIString) IndexFrom(r rune, from int) int {
return -1
}
-func (str UIString) Index(r rune) int {
+func (str TString) Index(r rune) int {
return str.IndexFrom(r, 0)
}
-func (str UIString) Count(r rune) (counter int) {
+func (str TString) Count(r rune) (counter int) {
index := 0
for {
index = str.IndexFrom(r, index)
@@ -120,8 +120,8 @@ func (str UIString) Count(r rune) (counter int) {
return
}
-func (str UIString) Split(sep rune) []UIString {
- a := make([]UIString, str.Count(sep)+1)
+func (str TString) Split(sep rune) []TString {
+ a := make([]TString, str.Count(sep)+1)
i := 0
orig := str
for {
diff --git a/ui/room-list.go b/ui/room-list.go
index 2ff3ed7..b85ed24 100644
--- a/ui/room-list.go
+++ b/ui/room-list.go
@@ -20,7 +20,7 @@ import (
"fmt"
"strconv"
- "github.com/gdamore/tcell"
+ "maunium.net/go/tcell"
"maunium.net/go/gomuks/matrix/rooms"
"maunium.net/go/gomuks/ui/widget"
"maunium.net/go/tview"
diff --git a/ui/room-view.go b/ui/room-view.go
index 749a432..b7c9a34 100644
--- a/ui/room-view.go
+++ b/ui/room-view.go
@@ -23,7 +23,7 @@ import (
"strings"
"time"
- "github.com/gdamore/tcell"
+ "maunium.net/go/tcell"
"maunium.net/go/gomuks/interface"
"maunium.net/go/gomuks/matrix/rooms"
"maunium.net/go/gomuks/ui/messages"
diff --git a/ui/ui.go b/ui/ui.go
index 16321b8..b5f847d 100644
--- a/ui/ui.go
+++ b/ui/ui.go
@@ -17,7 +17,7 @@
package ui
import (
- "github.com/gdamore/tcell"
+ "maunium.net/go/tcell"
"maunium.net/go/gomuks/interface"
"maunium.net/go/tview"
)
diff --git a/ui/view-main.go b/ui/view-main.go
index 3a6c38d..056f8a0 100644
--- a/ui/view-main.go
+++ b/ui/view-main.go
@@ -23,7 +23,7 @@ import (
"time"
"unicode"
- "github.com/gdamore/tcell"
+ "maunium.net/go/tcell"
"github.com/mattn/go-runewidth"
"maunium.net/go/gomatrix"
"maunium.net/go/gomuks/config"
@@ -230,6 +230,11 @@ func (view *MainView) MouseEventHandler(roomView *RoomView, event *tcell.EventMo
x, y := event.Position()
switch event.Buttons() {
+ case tcell.Button1:
+ mx, my, mw, mh := msgView.GetRect()
+ if x >= mx && y >= my && x < mx+mw && y < my+mh {
+ debug.Print("Message view clicked")
+ }
case tcell.WheelUp:
if msgView.IsAtTop() {
go view.LoadHistory(roomView.Room.ID, false)
diff --git a/ui/widget/advanced-inputfield.go b/ui/widget/advanced-inputfield.go
index f74ce29..7e01478 100644
--- a/ui/widget/advanced-inputfield.go
+++ b/ui/widget/advanced-inputfield.go
@@ -24,7 +24,7 @@ import (
"strings"
"unicode/utf8"
- "github.com/gdamore/tcell"
+ "maunium.net/go/tcell"
"github.com/mattn/go-runewidth"
"github.com/zyedidia/clipboard"
"maunium.net/go/tview"
diff --git a/ui/widget/border.go b/ui/widget/border.go
index 7c42f3d..b3eb65d 100644
--- a/ui/widget/border.go
+++ b/ui/widget/border.go
@@ -17,7 +17,7 @@
package widget
import (
- "github.com/gdamore/tcell"
+ "maunium.net/go/tcell"
"maunium.net/go/tview"
)
diff --git a/ui/widget/color.go b/ui/widget/color.go
index 12ee791..c4f1abf 100644
--- a/ui/widget/color.go
+++ b/ui/widget/color.go
@@ -21,7 +21,7 @@ import (
"hash/fnv"
"sort"
- "github.com/gdamore/tcell"
+ "maunium.net/go/tcell"
)
var colorNames []string
diff --git a/ui/widget/util.go b/ui/widget/util.go
index bd80903..920afad 100644
--- a/ui/widget/util.go
+++ b/ui/widget/util.go
@@ -17,7 +17,7 @@
package widget
import (
- "github.com/gdamore/tcell"
+ "maunium.net/go/tcell"
"github.com/mattn/go-runewidth"
"maunium.net/go/tview"
)