diff --git a/NOTES.md b/NOTES.md index 67883f8..d745a9b 100644 --- a/NOTES.md +++ b/NOTES.md @@ -10,3 +10,5 @@ - Filed [issue 25](https://gitlab.com/tslocum/cview/-/issues/25) - Modal styling messed up when wrapped - example occurence is the error modal for a long unsupported scheme URL - Filed [issue 26](https://gitlab.com/tslocum/cview/-/issues/26) +- Newtab page doesn't get margin +- Config funcs should become variables \ No newline at end of file diff --git a/README.md b/README.md index 3c4ed43..9aedfdf 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ Just call `amfora` or `amfora ` on the terminal. On Windows it The project keeps many standard terminal keybindings and is intuitive. Press ? inside the application to pull up the help menu with a list of all the keybindings, and Esc to leave it. If you have used Bombadillo you will find it similar. -It is designed with large or fullscreen terminals in mind. For optimal usage, make your terminal fullscreen. It was also designed with a dark background terminal in mind, but please file an issue if the colour choices look bad on your terminal setup. +It is designed with large or fullscreen terminals in mind. For optimal usage, make your terminal fullscreen. It was also designed with a dark background terminal in mind, but please file an issue if the colour choices look bad on your terminal setup. It was tested with left-to-right languages, and will likely not work as well with right-to-left languages like Arabic. ## Features / Roadmap Features in *italics* are in the master branch, but not in the latest release. diff --git a/config/config.go b/config/config.go index 58b6c26..fbe164d 100644 --- a/config/config.go +++ b/config/config.go @@ -95,7 +95,8 @@ func Init() error { viper.SetDefault("a-general.search", "gus.guru/search") viper.SetDefault("a-general.color", true) viper.SetDefault("a-general.bullets", true) - viper.SetDefault("a-general.wrap_width", 100) + viper.SetDefault("a-general.left_margin", 0.15) + viper.SetDefault("a-general.max_width", 100) viper.SetDefault("cache.max_size", 0) viper.SetDefault("cache.max_pages", 20) @@ -112,11 +113,3 @@ func Init() error { return nil } - -func GetWrapWidth() int { - i := viper.GetInt("a-general.wrap_width") - if i <= 0 { - return 100 // The default - } - return i -} diff --git a/config/default.go b/config/default.go index cc96d5a..08688c5 100644 --- a/config/default.go +++ b/config/default.go @@ -23,7 +23,10 @@ http = "default" search = "gemini://gus.guru/search" # Any URL that will accept a query string can be put here color = true # Whether colors will be used in the terminal bullets = true # Whether to replace list asterisks with unicode bullets -wrap_width = 100 # How many columns to wrap a page's text to. Preformatted blocks are not wrapped. + +# A number from 0 to 1, indicating what percentage of the terminal width the left margin should take up. +left_margin = 0.15 +max_width = 100 # The max number of columns to wrap a page's text to. Preformatted blocks are not wrapped. [bookmarks] # Make sure to quote the key names if you edit this part yourself diff --git a/default-config.toml b/default-config.toml index b74706b..d633b8b 100644 --- a/default-config.toml +++ b/default-config.toml @@ -20,7 +20,10 @@ http = "default" search = "gemini://gus.guru/search" # Any URL that will accept a query string can be put here color = true # Whether colors will be used in the terminal bullets = true # Whether to replace list asterisks with unicode bullets -wrap_width = 100 # How many columns to wrap a page's text to. Preformatted blocks are not wrapped. + +# A number from 0 to 1, indicating what percentage of the terminal width the left margin should take up. +left_margin = 0.15 +max_width = 100 # The max number of columns to wrap a page's text to. Preformatted blocks are not wrapped. [bookmarks] # Make sure to quote the key names if you edit this part yourself diff --git a/display/display.go b/display/display.go index 23a4ce5..edba031 100644 --- a/display/display.go +++ b/display/display.go @@ -13,7 +13,6 @@ import ( "github.com/makeworld-the-better-one/amfora/cache" "github.com/makeworld-the-better-one/amfora/structs" "gitlab.com/tslocum/cview" - //"github.com/makeworld-the-better-one/amfora/cview" ) var curTab = -1 // What number tab is currently visible, -1 means there are no tabs at all @@ -21,6 +20,9 @@ var tabMap = make(map[int]*structs.Page) // Map of tab number to page // Holds the actual tab primitives var tabViews = make(map[int]*cview.TextView) +var termW int +var termH int + // The user input and URL display bar at the bottom var bottomBar = cview.NewInputField(). SetFieldBackgroundColor(tcell.ColorWhite). @@ -64,17 +66,24 @@ var layout = cview.NewFlex(). SetDirection(cview.FlexRow). AddItem(tabRow, 1, 1, false). AddItem(nil, 1, 1, false). // One line of empty space above the page - //AddItem(tabPages, 0, 1, true). - AddItem(cview.NewFlex(). // The page text in the middle is held in another flex, to center it - SetDirection(cview.FlexColumn). - AddItem(nil, 0, 1, false). - AddItem(tabPages, 0, 7, true). // The text occupies 5/6 of the screen horizontally - AddItem(nil, 0, 1, false), - 0, 1, true). + AddItem(tabPages, 0, 1, true). + // AddItem(cview.NewFlex(). // The page text in the middle is held in another flex, to center it + // SetDirection(cview.FlexColumn). + // AddItem(nil, 0, 1, false). + // AddItem(tabPages, 0, 7, true). // The text occupies 7/9 of the screen horizontally + // AddItem(nil, 0, 1, false), + // 0, 1, true). AddItem(nil, 1, 1, false). // One line of empty space before bottomBar AddItem(bottomBar, 1, 1, false) -var App = cview.NewApplication().EnableMouse(false).SetRoot(layout, true) +var App = cview.NewApplication(). + EnableMouse(false). + SetRoot(layout, true). + SetAfterResizeFunc(func(width int, height int) { + // Store width and height for calculations + termW = width + termH = height + }) var renderedNewTabContent string var newTabLinks []string @@ -116,8 +125,8 @@ func Init() { bottomBar.SetDoneFunc(func(key tcell.Key) { switch key { case tcell.KeyEnter: - // TODO: Support search - // Send the URL/number typed in + // Figure out whether it's a URL, link number, or search + // And send out a request query := bottomBar.GetText() @@ -163,7 +172,7 @@ func Init() { }) // Render the default new tab content ONCE and store it for later - renderedNewTabContent, newTabLinks = renderer.RenderGemini(newTabContent) + renderedNewTabContent, newTabLinks = renderer.RenderGemini(newTabContent, textWidth()) newTabPage = structs.Page{Content: renderedNewTabContent, Links: newTabLinks} modalInit() diff --git a/display/private.go b/display/private.go index 8300cdc..a1649f4 100644 --- a/display/private.go +++ b/display/private.go @@ -13,11 +13,34 @@ import ( "github.com/makeworld-the-better-one/go-gemini" "github.com/spf13/viper" "gitlab.com/tslocum/cview" - //"github.com/makeworld-the-better-one/amfora/cview" ) // This file contains the functions that aren't part of the public API. +func leftMargin() int { + return int(float64(termW) * viper.GetFloat64("a-general.left_margin")) +} + +func textWidth() int { + if termW <= 0 { + // This prevent a flash of 1-column text on startup, when the terminal + // width hasn't been initialized. + return viper.GetInt("a-general.max_width") + } + + rightMargin := leftMargin() + if leftMargin() > 10 { + // 10 is the max right margin + rightMargin = 10 + } + + max := termW - leftMargin() - rightMargin + if max < viper.GetInt("a-general.max_width") { + return max + } + return viper.GetInt("a-general.max_width") +} + // pathEscape is the same as url.PathEscape, but it also replaces the +. func pathEscape(path string) string { return strings.ReplaceAll(url.PathEscape(path), "+", "%2B") @@ -81,7 +104,17 @@ func followLink(prev, next string) { func setPage(p *structs.Page) { saveScroll() // Save the scroll of the previous page - // Change page + if !p.Displayable { + // Add margin to page based on terminal width + var shifted string + for _, line := range strings.Split(p.Content, "\n") { + shifted += strings.Repeat(" ", leftMargin()) + line + "\n" + } + p.Content = shifted + p.Displayable = true + } + + // Change page on screen tabMap[curTab] = p tabViews[curTab].SetText(p.Content) tabViews[curTab].Highlight("") // Turn off highlights @@ -165,7 +198,7 @@ func handleURL(u string) (string, bool) { return "", false } if renderer.CanDisplay(res) { - page, err := renderer.MakePage(u, res) + page, err := renderer.MakePage(u, res, textWidth()) if err != nil { Error("Page Error", "Issuing creating page: "+err.Error()) // Set the bar back to original URL diff --git a/renderer/page.go b/renderer/page.go index 4f88a05..256d62c 100644 --- a/renderer/page.go +++ b/renderer/page.go @@ -46,7 +46,7 @@ func CanDisplay(res *gemini.Response) bool { return err == nil && enc != nil } -func MakePage(url string, res *gemini.Response) (*structs.Page, error) { +func MakePage(url string, res *gemini.Response, width int) (*structs.Page, error) { if !CanDisplay(res) { return nil, errors.New("not valid content for a Page") } @@ -83,7 +83,7 @@ func MakePage(url string, res *gemini.Response) (*structs.Page, error) { }, nil } if mediatype == "text/gemini" { - rendered, links := RenderGemini(utfText) + rendered, links := RenderGemini(utfText, width) return &structs.Page{ Url: url, Content: rendered, diff --git a/renderer/renderer.go b/renderer/renderer.go index d5227f0..f9bac79 100644 --- a/renderer/renderer.go +++ b/renderer/renderer.go @@ -9,7 +9,6 @@ import ( "strconv" "strings" - "github.com/makeworld-the-better-one/amfora/config" "github.com/spf13/viper" "gitlab.com/tslocum/cview" ) @@ -18,10 +17,11 @@ import ( // into a cview-compatible format. // It also returns a slice of link URLs. // numLinks is the number of links that exist so far. +// width is the number of columns to wrap to. // -// Since this only works on non-preformatted blocks, renderGemini +// Since this only works on non-preformatted blocks, RenderGemini // should always be used instead. -func convertRegularGemini(s string, numLinks int) (string, []string) { +func convertRegularGemini(s string, numLinks int, width int) (string, []string) { links := make([]string, 0) lines := strings.Split(s, "\n") wrappedLines := make([]string, 0) // Final result @@ -111,13 +111,13 @@ func convertRegularGemini(s string, numLinks int) (string, []string) { lines[i] = strings.TrimPrefix(lines[i], ">") lines[i] = strings.TrimPrefix(lines[i], " ") - temp := cview.WordWrap(lines[i], config.GetWrapWidth()) + temp := cview.WordWrap(lines[i], width) for i := range temp { temp[i] = "> " + temp[i] } wrappedLines = append(wrappedLines, temp...) } else { - wrappedLines = append(wrappedLines, cview.WordWrap(lines[i], config.GetWrapWidth())...) + wrappedLines = append(wrappedLines, cview.WordWrap(lines[i], width)...) } } } @@ -125,9 +125,9 @@ func convertRegularGemini(s string, numLinks int) (string, []string) { return strings.Join(wrappedLines, "\r\n"), links } -// renderGemini converts text/gemini into a cview displayable format. +// RenderGemini converts text/gemini into a cview displayable format. // It also returns a slice of link URLs. -func RenderGemini(s string) (string, []string) { +func RenderGemini(s string, width int) (string, []string) { s = cview.Escape(s) if viper.GetBool("a-general.color") { s = cview.TranslateANSI(s) @@ -148,7 +148,7 @@ func RenderGemini(s string) (string, []string) { rendered += buf } else { // Not preformatted, regular text - ren, lks := convertRegularGemini(buf, len(links)) + ren, lks := convertRegularGemini(buf, len(links), width) links = append(links, lks...) rendered += ren } @@ -166,7 +166,7 @@ func RenderGemini(s string) (string, []string) { } else { // Not preformatted, regular text // Same code as in the loop above - ren, lks := convertRegularGemini(buf, len(links)) + ren, lks := convertRegularGemini(buf, len(links), width) links = append(links, lks...) rendered += ren } diff --git a/structs/structs.go b/structs/structs.go index 29ec89a..5e35b37 100644 --- a/structs/structs.go +++ b/structs/structs.go @@ -2,11 +2,12 @@ package structs // Page is for storing UTF-8 text/gemini pages, as well as text/plain pages. type Page struct { - Url string - Content string // The processed content, NOT raw. Uses cview colour tags. All link/link texts must have region tags. - Links []string // URLs, for each region in the content. - Row int // Scroll position - Column int + Url string + Content string // The processed content, NOT raw. Uses cview colour tags. All link/link texts must have region tags. + Links []string // URLs, for each region in the content. + Row int // Scroll position + Column int // ditto + Displayable bool // Set to true once the content has been modified to display nicely on the screen - margins added } // Size returns an approx. size of a Page in bytes.