diff --git a/bookmarks.go b/bookmarks.go index 503e648..3e08eab 100644 --- a/bookmarks.go +++ b/bookmarks.go @@ -106,11 +106,11 @@ func (b Bookmarks) Render(termwidth, termheight int) []string { bl = cui.Shapes["bl"] } - out := make([]string, 5) + out := make([]string, 0, 5) top := fmt.Sprintf("%s%s%s", tl, strings.Repeat(ceil, width-2), tr) out = append(out, top) marks := b.List() - contentWidth := termwidth - 2 + contentWidth := width - 2 for i := 0; i < termheight - 2; i++ { if i + b.Position >= b.Length { out = append(out, fmt.Sprintf("%s%-*.*s%s", wall, contentWidth, contentWidth, "", wall )) diff --git a/client.go b/client.go index f326fbc..c59195b 100644 --- a/client.go +++ b/client.go @@ -59,32 +59,45 @@ func (c *client) GetSize() { c.Height = h c.Width = w + if redraw { c.Draw() } - time.Sleep(1 * time.Second) + time.Sleep(500 * time.Millisecond) } } func (c *client) Draw() { var screen strings.Builder screen.Grow(c.Height * c.Width) - screen.WriteString(c.TopBar.Render(c.Width, "This is a test")) + screen.WriteString(c.TopBar.Render(c.Width)) screen.WriteString("\n") - pageContent := c.PageState.Render(c.Height) + pageContent := c.PageState.Render(c.Height, c.Width) if c.BookMarks.IsOpen { bm := c.BookMarks.Render(c.Width, c.Height) - bmWidth := len([]rune(bm[0])) - for i, ln := range pageContent { - screen.WriteString(ln[:len(ln) - bmWidth]) + bmWidth := 40 + for i := 0; i < c.Height - 3; i++ { + if c.Width > bmWidth { + contentWidth := c.Width - bmWidth - 1 + if i < len(pageContent) - 1 { + screen.WriteString(fmt.Sprintf("%-*.*s", contentWidth, contentWidth, pageContent[i])) + } else { + screen.WriteString(fmt.Sprintf("%-*.*s", contentWidth, contentWidth, " ")) + } + } screen.WriteString(bm[i]) screen.WriteString("\n") } } else { - for _, ln := range pageContent { - screen.WriteString(ln) - screen.WriteString("\n") + for i := 0; i < c.Height - 3; i++ { + if i < len(pageContent) - 1 { + screen.WriteString(pageContent[i]) + screen.WriteString("\n") + } else { + screen.WriteString(fmt.Sprintf("%*s", c.Width, " ")) + screen.WriteString("\n") + } } } screen.WriteString("\n") // for the input line @@ -92,6 +105,7 @@ func (c *client) Draw() { cui.Clear("screen") cui.MoveCursorTo(0,0) fmt.Print(screen.String()) + c.DrawMessage() } func (c *client) TakeControlInput() { @@ -100,34 +114,49 @@ func (c *client) TakeControlInput() { switch input { case 'j', 'J': // scroll down one line + c.ClearMessage() + c.ClearMessageLine() c.Scroll(1) case 'k', 'K': // scroll up one line + c.ClearMessage() + c.ClearMessageLine() c.Scroll(-1) case 'q', 'Q': // quite bombadillo cui.Exit() case 'g': // scroll to top + c.ClearMessage() + c.ClearMessageLine() c.Scroll(-len(c.PageState.History[c.PageState.Position].WrappedContent)) case 'G': // scroll to bottom + c.ClearMessage() + c.ClearMessageLine() c.Scroll(len(c.PageState.History[c.PageState.Position].WrappedContent)) case 'd': // scroll down 75% + c.ClearMessage() + c.ClearMessageLine() distance := c.Height - c.Height / 4 c.Scroll(distance) case 'u': // scroll up 75% + c.ClearMessage() + c.ClearMessageLine() distance := c.Height - c.Height / 4 c.Scroll(-distance) case 'b': // go back + c.ClearMessage() + c.ClearMessageLine() err := c.PageState.NavigateHistory(-1) if err != nil { c.SetMessage(err.Error(), false) c.DrawMessage() } else { + c.SetHeaderUrl() c.Draw() } case 'B': @@ -136,11 +165,14 @@ func (c *client) TakeControlInput() { c.Draw() case 'f', 'F': // go forward + c.ClearMessage() + c.ClearMessageLine() err := c.PageState.NavigateHistory(1) if err != nil { c.SetMessage(err.Error(), false) c.DrawMessage() } else { + c.SetHeaderUrl() c.Draw() } case '\t': @@ -151,7 +183,6 @@ func (c *client) TakeControlInput() { // Process a command c.ClearMessage() c.ClearMessageLine() - cui.MoveCursorTo(c.Height-2, 0) entry, err := cui.GetLine() c.ClearMessageLine() if err != nil { @@ -378,13 +409,13 @@ func (c *client) search() { func (c *client) Scroll(amount int) { page := c.PageState.History[c.PageState.Position] - bottom := len(page.WrappedContent) - c.Height + bottom := len(page.WrappedContent) - c.Height + 3 // 3 for the three bars: top, msg, bottom if amount < 0 && page.ScrollPosition == 0 { c.SetMessage("You are already at the top", false) c.DrawMessage() fmt.Print("\a") return - } else if amount > 0 && page.ScrollPosition == bottom || bottom < 0 { + } else if (amount > 0 && page.ScrollPosition == bottom) || bottom < 0 { c.SetMessage("You are already at the bottom", false) c.DrawMessage() fmt.Print("\a") @@ -398,7 +429,7 @@ func (c *client) Scroll(amount int) { newScrollPosition = bottom } - page.ScrollPosition = newScrollPosition + c.PageState.History[c.PageState.Position].ScrollPosition = newScrollPosition c.Draw() } @@ -464,9 +495,15 @@ func (c *client) goToLink(l string) { return } } +} - c.SetMessage(fmt.Sprintf("Invalid link id: %s", l), true) - c.DrawMessage() +func (c *client) SetHeaderUrl() { + if c.PageState.Length > 0 { + u := c.PageState.History[c.PageState.Position].Location.Full + c.TopBar.url = u + } else { + c.TopBar.url = "" + } } func (c *client) Visit(url string) { @@ -487,6 +524,8 @@ func (c *client) Visit(url string) { c.DrawMessage() return } + c.SetMessage("Loading...", false) + c.DrawMessage() content, links, err := gopher.Visit(u.Mime, u.Host, u.Port, u.Resource) if err != nil { c.SetMessage(err.Error(), true) @@ -496,6 +535,9 @@ func (c *client) Visit(url string) { pg := MakePage(u, content, links) pg.WrapContent(c.Width) c.PageState.Add(pg) + c.ClearMessage() + c.ClearMessageLine() + c.SetHeaderUrl() c.Draw() case "gemini": // TODO send over to gemini request diff --git a/headbar.go b/headbar.go index d467a43..8782857 100644 --- a/headbar.go +++ b/headbar.go @@ -10,6 +10,7 @@ import ( type Headbar struct { title string + url string } @@ -27,9 +28,9 @@ func (h *Headbar) Draw() { // without having to redraw everything else } -func (h *Headbar) Render(width int, message string) string { +func (h *Headbar) Render(width int) string { maxMsgWidth := width - len([]rune(h.title)) - return fmt.Sprintf("\033[7m%s%-*.*s\033[0m", h.title, maxMsgWidth, maxMsgWidth, message) + return fmt.Sprintf("\033[7m%s%-*.*s\033[0m", h.title, maxMsgWidth, maxMsgWidth, h.url) } @@ -38,6 +39,6 @@ func (h *Headbar) Render(width int, message string) string { //--------------------------------------------------\\ func MakeHeadbar(title string) Headbar { - return Headbar{title} + return Headbar{title, ""} } diff --git a/main.go b/main.go index 9f2e642..780189f 100644 --- a/main.go +++ b/main.go @@ -163,6 +163,10 @@ func loadConfig() error { } } + for i, v := range settings.Bookmarks.Titles { + bombadillo.BookMarks.Add([]string{v, settings.Bookmarks.Links[i]}) + } + return nil } @@ -174,8 +178,8 @@ func initClient() error { } func main() { - // cui.HandleAlternateScreen("smcup") - // defer cui.Exit() + cui.HandleAlternateScreen("smcup") + defer cui.Exit() err := initClient() if err != nil { // if we can't initialize we should bail out diff --git a/page.go b/page.go index e67124d..3fe0a0e 100644 --- a/page.go +++ b/page.go @@ -2,7 +2,6 @@ package main import ( "strings" - "bytes" ) //------------------------------------------------\\ @@ -42,35 +41,31 @@ func (p *Page) ScrollPositionRange(termHeight int) (int, int) { func (p *Page) WrapContent(width int) { // TODO this is a temporary wrapping function // in order to test. Rebuild it. - src := strings.Split(p.RawContent, "\n") - out := []string{} - for _, ln := range src { - if len([]rune(ln)) <= width { - out = append(out, ln) + counter := 0 + var content strings.Builder + content.Grow(len(p.RawContent)) + for _, ch := range p.RawContent { + if ch == '\n' { + content.WriteRune(ch) + counter = 0 } else { - words := strings.SplitAfter(ln, " ") - var subout bytes.Buffer - for i, wd := range words { - sublen := subout.Len() - wdlen := len([]rune(wd)) - if sublen+wdlen <= width { - subout.WriteString(wd) - if i == len(words)-1 { - out = append(out, subout.String()) - } - } else { - out = append(out, subout.String()) - subout.Reset() - subout.WriteString(wd) - if i == len(words)-1 { - out = append(out, subout.String()) - subout.Reset() - } + if counter < width { + content.WriteRune(ch) + counter++ + } else { + content.WriteRune('\n') + counter = 0 + if p.Location.Mime == "1" { + spacer := " " + content.WriteString(spacer) + counter += len(spacer) } + content.WriteRune(ch) } } } - p.WrappedContent = out + + p.WrappedContent = strings.Split(content.String(), "\n") } //------------------------------------------------\\ diff --git a/pages.go b/pages.go index a4f12c3..9fde2d2 100644 --- a/pages.go +++ b/pages.go @@ -2,7 +2,6 @@ package main import ( "fmt" - "strings" ) //------------------------------------------------\\ @@ -49,13 +48,32 @@ func (p *Pages) Add(pg Page) { } } -func (p *Pages) Render(termHeight int) []string { +func (p *Pages) Render(termHeight, termWidth int) []string { if p.Length < 1 { - msg := "Welcome to Bombadillo,\nif this is your first time here\ntype:\n\n:help\n(and then press enter)" - return strings.Split(msg, "\n") + return []string{""} } - beg, end := p.History[p.Position].ScrollPositionRange(termHeight) - return p.History[p.Position].WrappedContent[beg:end] + pos := p.History[p.Position].ScrollPosition + prev := len(p.History[p.Position].WrappedContent) + p.History[p.Position].WrapContent(termWidth) + now := len(p.History[p.Position].WrappedContent) + if prev > now { + diff := prev - now + pos = pos - diff + } else if prev < now { + diff := now - prev + pos = pos + diff + if pos > now - termHeight { + pos = now - termHeight + } + } + + if pos < 0 || now < termHeight - 3 { + pos = 0 + } + + p.History[p.Position].ScrollPosition = pos + + return p.History[p.Position].WrappedContent[pos:] } //------------------------------------------------\\ diff --git a/url.go b/url.go index 58673fd..7956d94 100644 --- a/url.go +++ b/url.go @@ -78,7 +78,7 @@ func MakeUrl(u string) (Url, error) { } if out.Scheme == "gopher" && out.Mime == "" { - out.Mime = "0" + out.Mime = "1" } if out.Mime == "" && (out.Resource == "" || out.Resource == "/") && out.Scheme == "gopher" {