1
0
mirror of https://github.com/makew0rld/amfora.git synced 2024-11-03 02:37:23 -05:00

Side-scrolling and margin UX greatly improved

#1 will be fixed completely once margins are added to the new tab page
This commit is contained in:
makeworld 2020-06-21 16:53:12 -04:00
parent 847bd6eefa
commit 850d6f0454
10 changed files with 87 additions and 43 deletions

View File

@ -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

View File

@ -31,7 +31,7 @@ Just call `amfora` or `amfora <url> <other-url>` on the terminal. On Windows it
The project keeps many standard terminal keybindings and is intuitive. Press <kbd>?</kbd> inside the application to pull up the help menu with a list of all the keybindings, and <kbd>Esc</kbd> 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.

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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,

View File

@ -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
}

View File

@ -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.