mirror of
https://github.com/makew0rld/amfora.git
synced 2025-02-02 15:07:34 -05:00
🚧 Initial work
This commit is contained in:
parent
4514f8b8c0
commit
8e7300726d
@ -34,13 +34,14 @@ func aboutInit(version, commit, builtBy string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func createAboutPage(url string, content string) structs.Page {
|
func createAboutPage(url string, content string) structs.Page {
|
||||||
renderContent, links := renderer.RenderGemini(content, textWidth(), false)
|
renderContent, links, maxPreCols := renderer.RenderGemini(content, textWidth(), false)
|
||||||
return structs.Page{
|
return structs.Page{
|
||||||
Raw: content,
|
Raw: content,
|
||||||
Content: renderContent,
|
Content: renderContent,
|
||||||
|
MaxPreCols: maxPreCols,
|
||||||
Links: links,
|
Links: links,
|
||||||
URL: url,
|
URL: url,
|
||||||
Width: -1, // Force reformatting on first display
|
TermWidth: -1, // Force reformatting on first display
|
||||||
Mediatype: structs.TextGemini,
|
Mediatype: structs.TextGemini,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -132,13 +132,14 @@ func Bookmarks(t *tab) {
|
|||||||
bkmkPageRaw += fmt.Sprintf("=> %s %s\r\n", keys[i], m[keys[i]])
|
bkmkPageRaw += fmt.Sprintf("=> %s %s\r\n", keys[i], m[keys[i]])
|
||||||
}
|
}
|
||||||
// Render and display
|
// Render and display
|
||||||
content, links := renderer.RenderGemini(bkmkPageRaw, textWidth(), false)
|
content, links, maxPreCols := renderer.RenderGemini(bkmkPageRaw, textWidth(), false)
|
||||||
page := structs.Page{
|
page := structs.Page{
|
||||||
Raw: bkmkPageRaw,
|
Raw: bkmkPageRaw,
|
||||||
Content: content,
|
Content: content,
|
||||||
|
MaxPreCols: maxPreCols,
|
||||||
Links: links,
|
Links: links,
|
||||||
URL: "about:bookmarks",
|
URL: "about:bookmarks",
|
||||||
Width: termW,
|
TermWidth: termW,
|
||||||
Mediatype: structs.TextGemini,
|
Mediatype: structs.TextGemini,
|
||||||
}
|
}
|
||||||
setPage(t, &page)
|
setPage(t, &page)
|
||||||
|
@ -72,7 +72,11 @@ func Init(version, commit, builtBy string) {
|
|||||||
reformatMu.Lock() // Only allow one reformat job at a time
|
reformatMu.Lock() // Only allow one reformat job at a time
|
||||||
for i := range tabs {
|
for i := range tabs {
|
||||||
// Overwrite all tabs with a new, differently sized, left margin
|
// Overwrite all tabs with a new, differently sized, left margin
|
||||||
browser.AddTab(strconv.Itoa(i), makeTabLabel(strconv.Itoa(i+1)), makeContentLayout(tabs[i].view))
|
browser.AddTab(
|
||||||
|
strconv.Itoa(i),
|
||||||
|
makeTabLabel(strconv.Itoa(i+1)),
|
||||||
|
makeContentLayout(tabs[i].view, leftMargin()),
|
||||||
|
)
|
||||||
if tabs[i] == t {
|
if tabs[i] == t {
|
||||||
// Reformat page ASAP, in the middle of loop
|
// Reformat page ASAP, in the middle of loop
|
||||||
reformatPageAndSetView(t, t.page)
|
reformatPageAndSetView(t, t.page)
|
||||||
@ -129,8 +133,6 @@ func Init(version, commit, builtBy string) {
|
|||||||
bottomBar.SetDoneFunc(func(key tcell.Key) {
|
bottomBar.SetDoneFunc(func(key tcell.Key) {
|
||||||
tab := curTab
|
tab := curTab
|
||||||
|
|
||||||
tabs[tab].saveScroll()
|
|
||||||
|
|
||||||
// Reset func to set the bottomBar back to what it was before
|
// Reset func to set the bottomBar back to what it was before
|
||||||
// Use for errors.
|
// Use for errors.
|
||||||
reset := func() {
|
reset := func() {
|
||||||
@ -247,13 +249,14 @@ func Init(version, commit, builtBy string) {
|
|||||||
// Render the default new tab content ONCE and store it for later
|
// Render the default new tab content ONCE and store it for later
|
||||||
// This code is repeated in Reload()
|
// This code is repeated in Reload()
|
||||||
newTabContent := getNewTabContent()
|
newTabContent := getNewTabContent()
|
||||||
renderedNewTabContent, newTabLinks := renderer.RenderGemini(newTabContent, textWidth(), false)
|
renderedNewTabContent, newTabLinks, maxPreCols := renderer.RenderGemini(newTabContent, textWidth(), false)
|
||||||
newTabPage = structs.Page{
|
newTabPage = structs.Page{
|
||||||
Raw: newTabContent,
|
Raw: newTabContent,
|
||||||
Content: renderedNewTabContent,
|
Content: renderedNewTabContent,
|
||||||
|
MaxPreCols: maxPreCols,
|
||||||
Links: newTabLinks,
|
Links: newTabLinks,
|
||||||
URL: "about:newtab",
|
URL: "about:newtab",
|
||||||
Width: -1, // Force reformatting on first display
|
TermWidth: -1, // Force reformatting on first display
|
||||||
Mediatype: structs.TextGemini,
|
Mediatype: structs.TextGemini,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -432,7 +435,6 @@ func NewTab() {
|
|||||||
tabs[curTab].view.Highlight("")
|
tabs[curTab].view.Highlight("")
|
||||||
// Save bottomBar state
|
// Save bottomBar state
|
||||||
tabs[curTab].saveBottomBar()
|
tabs[curTab].saveBottomBar()
|
||||||
tabs[curTab].saveScroll()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
curTab = NumTabs()
|
curTab = NumTabs()
|
||||||
@ -443,7 +445,11 @@ func NewTab() {
|
|||||||
tabs[curTab].addToHistory("about:newtab")
|
tabs[curTab].addToHistory("about:newtab")
|
||||||
tabs[curTab].history.pos = 0 // Manually set as first page
|
tabs[curTab].history.pos = 0 // Manually set as first page
|
||||||
|
|
||||||
browser.AddTab(strconv.Itoa(curTab), makeTabLabel(strconv.Itoa(curTab+1)), makeContentLayout(tabs[curTab].view))
|
browser.AddTab(
|
||||||
|
strconv.Itoa(curTab),
|
||||||
|
makeTabLabel(strconv.Itoa(curTab+1)),
|
||||||
|
makeContentLayout(tabs[curTab].view, leftMargin()),
|
||||||
|
)
|
||||||
browser.SetCurrentTab(strconv.Itoa(curTab))
|
browser.SetCurrentTab(strconv.Itoa(curTab))
|
||||||
App.SetFocus(tabs[curTab].view)
|
App.SetFocus(tabs[curTab].view)
|
||||||
|
|
||||||
@ -506,7 +512,6 @@ func SwitchTab(tab int) {
|
|||||||
if curTab > -1 {
|
if curTab > -1 {
|
||||||
// Save bottomBar state
|
// Save bottomBar state
|
||||||
tabs[curTab].saveBottomBar()
|
tabs[curTab].saveBottomBar()
|
||||||
tabs[curTab].saveScroll()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
curTab = tab % NumTabs()
|
curTab = tab % NumTabs()
|
||||||
@ -527,13 +532,14 @@ func Reload() {
|
|||||||
// Re-render new tab, similar to Init()
|
// Re-render new tab, similar to Init()
|
||||||
newTabContent := getNewTabContent()
|
newTabContent := getNewTabContent()
|
||||||
tmpTermW := termW
|
tmpTermW := termW
|
||||||
renderedNewTabContent, newTabLinks := renderer.RenderGemini(newTabContent, textWidth(), false)
|
renderedNewTabContent, newTabLinks, maxPreCols := renderer.RenderGemini(newTabContent, textWidth(), false)
|
||||||
newTabPage = structs.Page{
|
newTabPage = structs.Page{
|
||||||
Raw: newTabContent,
|
Raw: newTabContent,
|
||||||
Content: renderedNewTabContent,
|
Content: renderedNewTabContent,
|
||||||
|
MaxPreCols: maxPreCols,
|
||||||
Links: newTabLinks,
|
Links: newTabLinks,
|
||||||
URL: "about:newtab",
|
URL: "about:newtab",
|
||||||
Width: tmpTermW,
|
TermWidth: tmpTermW,
|
||||||
Mediatype: structs.TextGemini,
|
Mediatype: structs.TextGemini,
|
||||||
}
|
}
|
||||||
temp := newTabPage // Copy
|
temp := newTabPage // Copy
|
||||||
|
@ -59,14 +59,15 @@ func handleFile(u string) (*structs.Page, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if mimetype == "text/gemini" {
|
if mimetype == "text/gemini" {
|
||||||
rendered, links := renderer.RenderGemini(string(content), textWidth(), false)
|
rendered, links, maxPreCols := renderer.RenderGemini(string(content), textWidth(), false)
|
||||||
page = &structs.Page{
|
page = &structs.Page{
|
||||||
Mediatype: structs.TextGemini,
|
Mediatype: structs.TextGemini,
|
||||||
URL: u,
|
URL: u,
|
||||||
Raw: string(content),
|
Raw: string(content),
|
||||||
Content: rendered,
|
Content: rendered,
|
||||||
|
MaxPreCols: maxPreCols,
|
||||||
Links: links,
|
Links: links,
|
||||||
Width: termW,
|
TermWidth: termW,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
page = &structs.Page{
|
page = &structs.Page{
|
||||||
@ -74,8 +75,9 @@ func handleFile(u string) (*structs.Page, bool) {
|
|||||||
URL: u,
|
URL: u,
|
||||||
Raw: string(content),
|
Raw: string(content),
|
||||||
Content: renderer.RenderPlainText(string(content)),
|
Content: renderer.RenderPlainText(string(content)),
|
||||||
|
MaxPreCols: -1,
|
||||||
Links: []string{},
|
Links: []string{},
|
||||||
Width: termW,
|
TermWidth: termW,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -107,14 +109,15 @@ func createDirectoryListing(u string) (*structs.Page, bool) {
|
|||||||
content += fmt.Sprintf("=> %s%s %s%s\n", f.Name(), separator, f.Name(), separator)
|
content += fmt.Sprintf("=> %s%s %s%s\n", f.Name(), separator, f.Name(), separator)
|
||||||
}
|
}
|
||||||
|
|
||||||
rendered, links := renderer.RenderGemini(content, textWidth(), false)
|
rendered, links, maxPreCols := renderer.RenderGemini(content, textWidth(), false)
|
||||||
page = &structs.Page{
|
page = &structs.Page{
|
||||||
Mediatype: structs.TextGemini,
|
Mediatype: structs.TextGemini,
|
||||||
URL: u,
|
URL: u,
|
||||||
Raw: content,
|
Raw: content,
|
||||||
Content: rendered,
|
Content: rendered,
|
||||||
|
MaxPreCols: maxPreCols,
|
||||||
Links: links,
|
Links: links,
|
||||||
Width: termW,
|
TermWidth: termW,
|
||||||
}
|
}
|
||||||
return page, true
|
return page, true
|
||||||
}
|
}
|
||||||
|
@ -337,7 +337,7 @@ func handleURL(t *tab, u string, numRedirects int) (string, bool) {
|
|||||||
return ret("", false)
|
return ret("", false)
|
||||||
}
|
}
|
||||||
|
|
||||||
page.Width = termW
|
page.TermWidth = termW
|
||||||
|
|
||||||
if !client.HasClientCert(parsed.Host) {
|
if !client.HasClientCert(parsed.Host) {
|
||||||
// Don't cache pages with client certs
|
// Don't cache pages with client certs
|
||||||
|
@ -24,7 +24,6 @@ func followLink(t *tab, prev, next string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if t.hasContent() {
|
if t.hasContent() {
|
||||||
t.saveScroll() // Likely called later on, it's here just in case
|
|
||||||
nextURL, err := resolveRelLink(t, prev, next)
|
nextURL, err := resolveRelLink(t, prev, next)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Error("URL Error", err.Error())
|
Error("URL Error", err.Error())
|
||||||
@ -48,7 +47,7 @@ func followLink(t *tab, prev, next string) {
|
|||||||
// It will not waste resources if the passed page is already fitted to the current terminal width, and can be
|
// It will not waste resources if the passed page is already fitted to the current terminal width, and can be
|
||||||
// called safely even when the page might be already formatted properly.
|
// called safely even when the page might be already formatted properly.
|
||||||
func reformatPage(p *structs.Page) {
|
func reformatPage(p *structs.Page) {
|
||||||
if p.Width == termW {
|
if p.TermWidth == termW {
|
||||||
// No changes to make
|
// No changes to make
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -65,7 +64,7 @@ func reformatPage(p *structs.Page) {
|
|||||||
strings.HasPrefix(p.URL, "file") {
|
strings.HasPrefix(p.URL, "file") {
|
||||||
proxied = false
|
proxied = false
|
||||||
}
|
}
|
||||||
rendered, _ = renderer.RenderGemini(p.Raw, textWidth(), proxied)
|
rendered, _, _ = renderer.RenderGemini(p.Raw, textWidth(), proxied)
|
||||||
case structs.TextPlain:
|
case structs.TextPlain:
|
||||||
rendered = renderer.RenderPlainText(p.Raw)
|
rendered = renderer.RenderPlainText(p.Raw)
|
||||||
case structs.TextAnsi:
|
case structs.TextAnsi:
|
||||||
@ -75,17 +74,16 @@ func reformatPage(p *structs.Page) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
p.Content = rendered
|
p.Content = rendered
|
||||||
p.Width = termW
|
p.TermWidth = termW
|
||||||
}
|
}
|
||||||
|
|
||||||
// reformatPageAndSetView is for reformatting a page that is already being displayed.
|
// reformatPageAndSetView is for reformatting a page that is already being displayed.
|
||||||
// setPage should be used when a page is being loaded for the first time.
|
// setPage should be used when a page is being loaded for the first time.
|
||||||
func reformatPageAndSetView(t *tab, p *structs.Page) {
|
func reformatPageAndSetView(t *tab, p *structs.Page) {
|
||||||
if p.Width == termW {
|
if p.TermWidth == termW {
|
||||||
// No changes to make
|
// No changes to make
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
t.saveScroll()
|
|
||||||
reformatPage(p)
|
reformatPage(p)
|
||||||
t.view.SetText(p.Content)
|
t.view.SetText(p.Content)
|
||||||
t.applyScroll() // Go back to where you were, roughly
|
t.applyScroll() // Go back to where you were, roughly
|
||||||
@ -101,8 +99,6 @@ func setPage(t *tab, p *structs.Page) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
t.saveScroll() // Save the scroll of the previous page
|
|
||||||
|
|
||||||
// Make sure the page content is fitted to the terminal every time it's displayed
|
// Make sure the page content is fitted to the terminal every time it's displayed
|
||||||
reformatPage(p)
|
reformatPage(p)
|
||||||
|
|
||||||
@ -112,10 +108,13 @@ func setPage(t *tab, p *structs.Page) {
|
|||||||
t.view.SetText(p.Content)
|
t.view.SetText(p.Content)
|
||||||
t.view.Highlight("") // Turn off highlights, other funcs may restore if necessary
|
t.view.Highlight("") // Turn off highlights, other funcs may restore if necessary
|
||||||
t.view.ScrollToBeginning()
|
t.view.ScrollToBeginning()
|
||||||
|
// Reset page left margin
|
||||||
// Set tab number in case a favicon from before overwrote it
|
|
||||||
tabNum := tabNumber(t)
|
tabNum := tabNumber(t)
|
||||||
browser.SetTabLabel(strconv.Itoa(tabNum), makeTabLabel(strconv.Itoa(tabNum+1)))
|
browser.AddTab(
|
||||||
|
strconv.Itoa(tabNum),
|
||||||
|
makeTabLabel(strconv.Itoa(tabNum+1)),
|
||||||
|
makeContentLayout(t.view, leftMargin()),
|
||||||
|
)
|
||||||
App.Draw()
|
App.Draw()
|
||||||
|
|
||||||
// Setup display
|
// Setup display
|
||||||
|
@ -149,13 +149,14 @@ func Subscriptions(t *tab, u string) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
content, links := renderer.RenderGemini(rawPage, textWidth(), false)
|
content, links, maxPreCols := renderer.RenderGemini(rawPage, textWidth(), false)
|
||||||
page := structs.Page{
|
page := structs.Page{
|
||||||
Raw: rawPage,
|
Raw: rawPage,
|
||||||
Content: content,
|
Content: content,
|
||||||
|
MaxPreCols: maxPreCols,
|
||||||
Links: links,
|
Links: links,
|
||||||
URL: u,
|
URL: u,
|
||||||
Width: termW,
|
TermWidth: termW,
|
||||||
Mediatype: structs.TextGemini,
|
Mediatype: structs.TextGemini,
|
||||||
}
|
}
|
||||||
go cache.AddPage(&page)
|
go cache.AddPage(&page)
|
||||||
@ -191,13 +192,14 @@ func ManageSubscriptions(t *tab, u string) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
content, links := renderer.RenderGemini(rawPage, textWidth(), false)
|
content, links, maxPreCols := renderer.RenderGemini(rawPage, textWidth(), false)
|
||||||
page := structs.Page{
|
page := structs.Page{
|
||||||
Raw: rawPage,
|
Raw: rawPage,
|
||||||
Content: content,
|
Content: content,
|
||||||
|
MaxPreCols: maxPreCols,
|
||||||
Links: links,
|
Links: links,
|
||||||
URL: "about:manage-subscriptions",
|
URL: "about:manage-subscriptions",
|
||||||
Width: termW,
|
TermWidth: termW,
|
||||||
Mediatype: structs.TextGemini,
|
Mediatype: structs.TextGemini,
|
||||||
}
|
}
|
||||||
go cache.AddPage(&page)
|
go cache.AddPage(&page)
|
||||||
|
@ -121,6 +121,58 @@ func makeNewTab() *tab {
|
|||||||
tabs[tab].page.SelectedID = strconv.Itoa(index)
|
tabs[tab].page.SelectedID = strconv.Itoa(index)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
t.view.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
||||||
|
// Capture left/right scrolling and change the left margin size accordingly
|
||||||
|
// See #197
|
||||||
|
// Up/down scrolling is saved in this func to keep them in sync, but the keys
|
||||||
|
// are passed and no extra behaviour happens.
|
||||||
|
|
||||||
|
key := event.Key()
|
||||||
|
mod := event.Modifiers()
|
||||||
|
ru := event.Rune()
|
||||||
|
|
||||||
|
oldCol := t.page.Column
|
||||||
|
|
||||||
|
if (key == tcell.KeyRight && mod == tcell.ModNone) ||
|
||||||
|
(key == tcell.KeyRune && mod == tcell.ModNone && ru == 'l') {
|
||||||
|
// Scrolling to the right
|
||||||
|
// TODO check if already scrolled to the end
|
||||||
|
t.page.Column++
|
||||||
|
} else if (key == tcell.KeyLeft && mod == tcell.ModNone) ||
|
||||||
|
(key == tcell.KeyRune && mod == tcell.ModNone && ru == 'h') {
|
||||||
|
// Scrolling to the left
|
||||||
|
if t.page.Column == 0 {
|
||||||
|
// Can't scroll to the left anymore
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
t.page.Column--
|
||||||
|
} else if (key == tcell.KeyUp && mod == tcell.ModNone) ||
|
||||||
|
(key == tcell.KeyRune && mod == tcell.ModNone && ru == 'k') {
|
||||||
|
// Scrolling up
|
||||||
|
if t.page.Row > 0 {
|
||||||
|
t.page.Row--
|
||||||
|
}
|
||||||
|
return event
|
||||||
|
} else if (key == tcell.KeyDown && mod == tcell.ModNone) ||
|
||||||
|
(key == tcell.KeyRune && mod == tcell.ModNone && ru == 'j') {
|
||||||
|
// Scrolling down
|
||||||
|
// TODO need to check for max vertical scroll before doing this
|
||||||
|
return event
|
||||||
|
} else {
|
||||||
|
// Some other key, stop processing it
|
||||||
|
return event
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.page.MaxPreCols <= termW && t.page.MaxPreCols > -1 {
|
||||||
|
// No scrolling is actually necessary
|
||||||
|
t.page.Column = oldCol // Reset
|
||||||
|
return nil // Ignore keys
|
||||||
|
}
|
||||||
|
|
||||||
|
t.applyHorizontalScroll()
|
||||||
|
App.Draw()
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
return &t
|
return &t
|
||||||
}
|
}
|
||||||
@ -167,19 +219,39 @@ func (t *tab) hasContent() bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// saveScroll saves where in the page the user was.
|
// applyHorizontalScroll handles horizontal scroll logic including left margin resizing,
|
||||||
// It should be used whenever moving from one page to another.
|
// see #197 for details. Use applyScroll instead.
|
||||||
func (t *tab) saveScroll() {
|
//
|
||||||
// It will also be saved in the cache because the cache uses the same pointer
|
// In certain cases it will still use and apply the saved Row.
|
||||||
row, col := t.view.GetScrollOffset()
|
func (t *tab) applyHorizontalScroll() {
|
||||||
t.page.Row = row
|
i := tabNumber(t)
|
||||||
t.page.Column = col
|
if i == -1 {
|
||||||
|
// Tab is not actually being used and should not be (re)added to the browser
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if t.page.Column >= leftMargin() {
|
||||||
|
// Scrolled to the right far enough that no left margin is needed
|
||||||
|
browser.AddTab(
|
||||||
|
strconv.Itoa(i),
|
||||||
|
makeTabLabel(strconv.Itoa(i+1)),
|
||||||
|
makeContentLayout(t.view, 0),
|
||||||
|
)
|
||||||
|
t.view.ScrollTo(t.page.Row, t.page.Column-leftMargin())
|
||||||
|
} else {
|
||||||
|
// Left margin is still needed, but is not necessarily at the right size by default
|
||||||
|
browser.AddTab(
|
||||||
|
strconv.Itoa(i),
|
||||||
|
makeTabLabel(strconv.Itoa(i+1)),
|
||||||
|
makeContentLayout(t.view, leftMargin()-t.page.Column),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// applyScroll applies the saved scroll values to the page and tab.
|
// applyScroll applies the saved scroll values to the page and tab.
|
||||||
// It should only be used when going backward and forward.
|
// It should only be used when going backward and forward.
|
||||||
func (t *tab) applyScroll() {
|
func (t *tab) applyScroll() {
|
||||||
t.view.ScrollTo(t.page.Row, t.page.Column)
|
t.view.ScrollTo(t.page.Row, 0)
|
||||||
|
t.applyHorizontalScroll()
|
||||||
}
|
}
|
||||||
|
|
||||||
// saveBottomBar saves the current bottomBar values in the tab.
|
// saveBottomBar saves the current bottomBar values in the tab.
|
||||||
|
@ -14,13 +14,15 @@ import (
|
|||||||
// This file contains funcs that are small, self-contained utilities.
|
// This file contains funcs that are small, self-contained utilities.
|
||||||
|
|
||||||
// makeContentLayout returns a flex that contains the given TextView
|
// makeContentLayout returns a flex that contains the given TextView
|
||||||
// along with the current correct left margin, as well as a single empty
|
// along with the provided left margin, as well as a single empty
|
||||||
// line at the top, for a top margin.
|
// line at the top, for a top margin.
|
||||||
func makeContentLayout(tv *cview.TextView) *cview.Flex {
|
func makeContentLayout(tv *cview.TextView, leftMargin int) *cview.Flex {
|
||||||
// Create horizontal flex with the left margin as an empty space
|
// Create horizontal flex with the left margin as an empty space
|
||||||
horiz := cview.NewFlex()
|
horiz := cview.NewFlex()
|
||||||
horiz.SetDirection(cview.FlexColumn)
|
horiz.SetDirection(cview.FlexColumn)
|
||||||
horiz.AddItem(nil, leftMargin(), 0, false)
|
if leftMargin > 0 {
|
||||||
|
horiz.AddItem(nil, leftMargin, 0, false)
|
||||||
|
}
|
||||||
horiz.AddItem(tv, 0, 1, true)
|
horiz.AddItem(tv, 0, 1, true)
|
||||||
|
|
||||||
// Create a vertical flex with the other one and a top margin
|
// Create a vertical flex with the other one and a top margin
|
||||||
|
1
go.mod
1
go.mod
@ -8,6 +8,7 @@ require (
|
|||||||
github.com/gdamore/tcell/v2 v2.1.1-0.20210125004847-19e17097d8fe
|
github.com/gdamore/tcell/v2 v2.1.1-0.20210125004847-19e17097d8fe
|
||||||
github.com/google/go-cmp v0.5.0 // indirect
|
github.com/google/go-cmp v0.5.0 // indirect
|
||||||
github.com/makeworld-the-better-one/go-gemini v0.11.0
|
github.com/makeworld-the-better-one/go-gemini v0.11.0
|
||||||
|
github.com/mattn/go-runewidth v0.0.10
|
||||||
github.com/mitchellh/go-homedir v1.1.0
|
github.com/mitchellh/go-homedir v1.1.0
|
||||||
github.com/mitchellh/mapstructure v1.3.1 // indirect
|
github.com/mitchellh/mapstructure v1.3.1 // indirect
|
||||||
github.com/mmcdole/gofeed v1.1.0
|
github.com/mmcdole/gofeed v1.1.0
|
||||||
|
@ -118,13 +118,14 @@ func MakePage(url string, res *gemini.Response, width int, proxied bool) (*struc
|
|||||||
}
|
}
|
||||||
|
|
||||||
if mediatype == "text/gemini" {
|
if mediatype == "text/gemini" {
|
||||||
rendered, links := RenderGemini(utfText, width, proxied)
|
rendered, links, maxPreCols := RenderGemini(utfText, width, proxied)
|
||||||
return &structs.Page{
|
return &structs.Page{
|
||||||
Mediatype: structs.TextGemini,
|
Mediatype: structs.TextGemini,
|
||||||
RawMediatype: mediatype,
|
RawMediatype: mediatype,
|
||||||
URL: url,
|
URL: url,
|
||||||
Raw: utfText,
|
Raw: utfText,
|
||||||
Content: rendered,
|
Content: rendered,
|
||||||
|
MaxPreCols: maxPreCols,
|
||||||
Links: links,
|
Links: links,
|
||||||
MadeAt: time.Now(),
|
MadeAt: time.Now(),
|
||||||
}, nil
|
}, nil
|
||||||
@ -137,6 +138,7 @@ func MakePage(url string, res *gemini.Response, width int, proxied bool) (*struc
|
|||||||
URL: url,
|
URL: url,
|
||||||
Raw: utfText,
|
Raw: utfText,
|
||||||
Content: RenderANSI(utfText),
|
Content: RenderANSI(utfText),
|
||||||
|
MaxPreCols: -1,
|
||||||
Links: []string{},
|
Links: []string{},
|
||||||
MadeAt: time.Now(),
|
MadeAt: time.Now(),
|
||||||
}, nil
|
}, nil
|
||||||
@ -149,6 +151,7 @@ func MakePage(url string, res *gemini.Response, width int, proxied bool) (*struc
|
|||||||
URL: url,
|
URL: url,
|
||||||
Raw: utfText,
|
Raw: utfText,
|
||||||
Content: RenderPlainText(utfText),
|
Content: RenderPlainText(utfText),
|
||||||
|
MaxPreCols: -1,
|
||||||
Links: []string{},
|
Links: []string{},
|
||||||
MadeAt: time.Now(),
|
MadeAt: time.Now(),
|
||||||
}, nil
|
}, nil
|
||||||
|
@ -12,6 +12,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/makeworld-the-better-one/amfora/config"
|
"github.com/makeworld-the-better-one/amfora/config"
|
||||||
|
"github.com/mattn/go-runewidth"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"gitlab.com/tslocum/cview"
|
"gitlab.com/tslocum/cview"
|
||||||
)
|
)
|
||||||
@ -267,19 +268,19 @@ func convertRegularGemini(s string, numLinks, width int, proxied bool) (string,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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.
|
// It also returns a slice of link URLs, and the Page.MaxPreCols value.
|
||||||
//
|
//
|
||||||
// width is the number of columns to wrap to.
|
// width is the number of columns to wrap to.
|
||||||
// leftMargin is the number of blank spaces to prepend to each line.
|
// leftMargin is the number of blank spaces to prepend to each line.
|
||||||
//
|
//
|
||||||
// proxied is whether the request is through the gemini:// scheme.
|
// proxied is whether the request is through the gemini:// scheme.
|
||||||
// If it's not a gemini:// page, set this to true.
|
// If it's not a gemini:// page, set this to true.
|
||||||
func RenderGemini(s string, width int, proxied bool) (string, []string) {
|
func RenderGemini(s string, width int, proxied bool) (string, []string, int) {
|
||||||
s = cview.Escape(s)
|
s = cview.Escape(s)
|
||||||
|
|
||||||
lines := strings.Split(s, "\n")
|
lines := strings.Split(s, "\n")
|
||||||
|
|
||||||
links := make([]string, 0)
|
links := make([]string, 0)
|
||||||
|
maxPreCols := 0
|
||||||
|
|
||||||
// Process and wrap non preformatted lines
|
// Process and wrap non preformatted lines
|
||||||
rendered := "" // Final result
|
rendered := "" // Final result
|
||||||
@ -288,6 +289,11 @@ func RenderGemini(s string, width int, proxied bool) (string, []string) {
|
|||||||
|
|
||||||
// processPre is for rendering preformatted blocks
|
// processPre is for rendering preformatted blocks
|
||||||
processPre := func() {
|
processPre := func() {
|
||||||
|
lineWidth := runewidth.StringWidth(buf)
|
||||||
|
if lineWidth > maxPreCols {
|
||||||
|
maxPreCols = lineWidth
|
||||||
|
}
|
||||||
|
|
||||||
// Support ANSI color codes in preformatted blocks - see #59
|
// Support ANSI color codes in preformatted blocks - see #59
|
||||||
if viper.GetBool("a-general.color") && viper.GetBool("a-general.ansi") {
|
if viper.GetBool("a-general.color") && viper.GetBool("a-general.ansi") {
|
||||||
buf = cview.TranslateANSI(buf)
|
buf = cview.TranslateANSI(buf)
|
||||||
@ -348,5 +354,5 @@ func RenderGemini(s string, width int, proxied bool) (string, []string) {
|
|||||||
processRegular()
|
processRegular()
|
||||||
}
|
}
|
||||||
|
|
||||||
return rendered, links
|
return rendered, links, maxPreCols
|
||||||
}
|
}
|
||||||
|
@ -25,10 +25,11 @@ type Page struct {
|
|||||||
RawMediatype string // The actual mediatype sent by the server
|
RawMediatype string // The actual mediatype sent by the server
|
||||||
Raw string // The raw response, as received over the network
|
Raw string // The raw response, as received over the network
|
||||||
Content string // The processed content, NOT raw. Uses cview color tags. It will also have a left margin.
|
Content string // The processed content, NOT raw. Uses cview color tags. It will also have a left margin.
|
||||||
|
MaxPreCols int // The amount of the terminal columns the longest preformatted line in Raw takes up, used for #197. -1 means infinite length lines, AKA always allow scrolling.
|
||||||
Links []string // URLs, for each region in the content.
|
Links []string // URLs, for each region in the content.
|
||||||
Row int // Scroll position
|
Row int // Vertical scroll position
|
||||||
Column int // ditto
|
Column int // Horizontal scroll position - does not map exactly to a cview.TextView because it includes left margin size changes, see #197
|
||||||
Width int // The terminal width when the Content was set, to know when reformatting should happen.
|
TermWidth int // The terminal width when the Content was set, to know when reformatting should happen.
|
||||||
Selected string // The current text or link selected
|
Selected string // The current text or link selected
|
||||||
SelectedID string // The cview region ID for the selected text/link
|
SelectedID string // The cview region ID for the selected text/link
|
||||||
Mode PageMode
|
Mode PageMode
|
||||||
|
Loading…
x
Reference in New Issue
Block a user