Skip to content

support the open-icon of folder #34168

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
46 changes: 40 additions & 6 deletions modules/fileicon/basic.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,51 @@ import (
"code.gitea.io/gitea/modules/svg"
)

func BasicThemeIcon(entry *git.TreeEntry) template.HTML {
type FileEntry struct {
Name string
EntryMode git.EntryMode
FollowEntryMode git.EntryMode
}

func GetFileEntryByTreeEntry(entry *git.TreeEntry) *FileEntry {
if entry.IsLink() {
if te, err := entry.FollowLink(); err == nil && te.IsDir() {
return &FileEntry{
Name: entry.Name(),
EntryMode: entry.Mode(),
FollowEntryMode: te.Mode(),
}
}
}
return &FileEntry{
Name: entry.Name(),
EntryMode: entry.Mode(),
}
}

func BasicThemeFolderIconName(isOpen bool) string {
if isOpen {
return "octicon-file-directory-open-fill"
}
return "octicon-file-directory-fill"
}

func BasicThemeFolderIconWithOpenStatus(isOpen bool) template.HTML {
return svg.RenderHTML(BasicThemeFolderIconName(isOpen))
}

func BasicThemeIconWithOpenStatus(entry *FileEntry, isOpen bool) template.HTML {
// TODO: add "open icon" support
svgName := "octicon-file"
switch {
case entry.IsLink():
case entry.EntryMode.IsLink():
svgName = "octicon-file-symlink-file"
if te, err := entry.FollowLink(); err == nil && te.IsDir() {
if entry.FollowEntryMode.IsDir() {
svgName = "octicon-file-directory-symlink"
}
case entry.IsDir():
svgName = "octicon-file-directory-fill"
case entry.IsSubModule():
case entry.EntryMode.IsDir():
svgName = BasicThemeFolderIconName(isOpen)
case entry.EntryMode.IsSubModule():
svgName = "octicon-file-submodule"
}
return svg.RenderHTML(svgName)
Expand Down
44 changes: 28 additions & 16 deletions modules/fileicon/material.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"strings"
"sync"

"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/options"
Expand Down Expand Up @@ -69,41 +68,51 @@ func (m *MaterialIconProvider) renderFileIconSVG(p *RenderedIconPool, name, svg,
}
svgID := "svg-mfi-" + name
svgCommonAttrs := `class="svg git-entry-icon ` + extraClass + `" width="16" height="16" aria-hidden="true"`
svgHTML := template.HTML(`<svg id="` + svgID + `" ` + svgCommonAttrs + svg[4:])
if p == nil {
return svgHTML
}
if p.IconSVGs[svgID] == "" {
p.IconSVGs[svgID] = template.HTML(`<svg id="` + svgID + `" ` + svgCommonAttrs + svg[4:])
p.IconSVGs[svgID] = svgHTML
}
return template.HTML(`<svg ` + svgCommonAttrs + `><use xlink:href="#` + svgID + `"></use></svg>`)
}

func (m *MaterialIconProvider) FileIcon(p *RenderedIconPool, entry *git.TreeEntry) template.HTML {
func (m *MaterialIconProvider) FolderIconWithOpenStatus(p *RenderedIconPool, isOpen bool) template.HTML {
name := m.FindIconName("folder", true, isOpen)
return m.renderFileIconSVG(p, name, m.svgs[name], BasicThemeFolderIconName(isOpen))
}

func (m *MaterialIconProvider) FileIconWithOpenStatus(p *RenderedIconPool, entry *FileEntry, isOpen bool) template.HTML {
if m.rules == nil {
return BasicThemeIcon(entry)
return BasicThemeIconWithOpenStatus(entry, isOpen)
}

if entry.IsLink() {
if te, err := entry.FollowLink(); err == nil && te.IsDir() {
if entry.EntryMode.IsLink() {
if entry.FollowEntryMode.IsDir() {
// keep the old "octicon-xxx" class name to make some "theme plugin selector" could still work
return svg.RenderHTML("material-folder-symlink", 16, "octicon-file-directory-symlink")
}
return svg.RenderHTML("octicon-file-symlink-file") // TODO: find some better icons for them
}

name := m.findIconNameByGit(entry)
// TODO: add "open icon" support
name := m.findIconNameByGit(entry, isOpen)
// the material icon pack's "folder" icon doesn't look good, so use our built-in one
// keep the old "octicon-xxx" class name to make some "theme plugin selector" could still work
if iconSVG, ok := m.svgs[name]; ok && name != "folder" && iconSVG != "" {
if iconSVG, ok := m.svgs[name]; ok && iconSVG != "" {
// keep the old "octicon-xxx" class name to make some "theme plugin selector" could still work
extraClass := "octicon-file"
switch {
case entry.IsDir():
extraClass = "octicon-file-directory-fill"
case entry.IsSubModule():
case entry.EntryMode.IsDir():
extraClass = BasicThemeFolderIconName(isOpen)
case entry.EntryMode.IsSubModule():
extraClass = "octicon-file-submodule"
}
return m.renderFileIconSVG(p, name, iconSVG, extraClass)
}
// TODO: use an interface or wrapper for git.Entry to make the code testable.
return BasicThemeIcon(entry)
return BasicThemeIconWithOpenStatus(entry, isOpen)
}

func (m *MaterialIconProvider) findIconNameWithLangID(s string) string {
Expand All @@ -118,12 +127,15 @@ func (m *MaterialIconProvider) findIconNameWithLangID(s string) string {
return ""
}

func (m *MaterialIconProvider) FindIconName(name string, isDir bool) string {
func (m *MaterialIconProvider) FindIconName(name string, isDir, isOpen bool) string {
fileNameLower := strings.ToLower(path.Base(name))
if isDir {
if s, ok := m.rules.FolderNames[fileNameLower]; ok {
return s
}
if isOpen {
return "folder-open"
}
return "folder"
}

Expand All @@ -147,9 +159,9 @@ func (m *MaterialIconProvider) FindIconName(name string, isDir bool) string {
return "file"
}

func (m *MaterialIconProvider) findIconNameByGit(entry *git.TreeEntry) string {
if entry.IsSubModule() {
func (m *MaterialIconProvider) findIconNameByGit(entry *FileEntry, isOpen bool) string {
if entry.EntryMode.IsSubModule() {
return "folder-git"
}
return m.FindIconName(entry.Name(), entry.IsDir())
return m.FindIconName(entry.Name, entry.EntryMode.IsDir(), isOpen)
}
8 changes: 4 additions & 4 deletions modules/fileicon/material_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ func TestMain(m *testing.M) {
func TestFindIconName(t *testing.T) {
unittest.PrepareTestEnv(t)
p := fileicon.DefaultMaterialIconProvider()
assert.Equal(t, "php", p.FindIconName("foo.php", false))
assert.Equal(t, "php", p.FindIconName("foo.PHP", false))
assert.Equal(t, "javascript", p.FindIconName("foo.js", false))
assert.Equal(t, "visualstudio", p.FindIconName("foo.vba", false))
assert.Equal(t, "php", p.FindIconName("foo.php", false, false))
assert.Equal(t, "php", p.FindIconName("foo.PHP", false, false))
assert.Equal(t, "javascript", p.FindIconName("foo.js", false, false))
assert.Equal(t, "visualstudio", p.FindIconName("foo.vba", false, false))
}
17 changes: 3 additions & 14 deletions modules/fileicon/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"html/template"
"strings"

"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/setting"
)

Expand All @@ -34,19 +33,9 @@ func (p *RenderedIconPool) RenderToHTML() template.HTML {
return template.HTML(sb.String())
}

// TODO: use an interface or struct to replace "*git.TreeEntry", to decouple the fileicon module from git module

func RenderEntryIcon(renderedIconPool *RenderedIconPool, entry *git.TreeEntry) template.HTML {
if setting.UI.FileIconTheme == "material" {
return DefaultMaterialIconProvider().FileIcon(renderedIconPool, entry)
}
return BasicThemeIcon(entry)
}

func RenderEntryIconOpen(renderedIconPool *RenderedIconPool, entry *git.TreeEntry) template.HTML {
// TODO: add "open icon" support
func RenderEntryIconWithOpenStatus(renderedIconPool *RenderedIconPool, entry *FileEntry, isOpen bool) template.HTML {
if setting.UI.FileIconTheme == "material" {
return DefaultMaterialIconProvider().FileIcon(renderedIconPool, entry)
return DefaultMaterialIconProvider().FileIconWithOpenStatus(renderedIconPool, entry, isOpen)
}
return BasicThemeIcon(entry)
return BasicThemeIconWithOpenStatus(entry, isOpen)
}
25 changes: 25 additions & 0 deletions modules/git/tree_entry_mode.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,31 @@ func (e EntryMode) String() string {
return strconv.FormatInt(int64(e), 8)
}

// IsSubModule if the entry is a sub module
func (e EntryMode) IsSubModule() bool {
return e == EntryModeCommit
}

// IsDir if the entry is a sub dir
func (e EntryMode) IsDir() bool {
return e == EntryModeTree
}

// IsLink if the entry is a symlink
func (e EntryMode) IsLink() bool {
return e == EntryModeSymlink
}

// IsRegular if the entry is a regular file
func (e EntryMode) IsRegular() bool {
return e == EntryModeBlob
}

// IsExecutable if the entry is an executable file (not necessarily binary)
func (e EntryMode) IsExecutable() bool {
return e == EntryModeExec
}

func ParseEntryMode(mode string) (EntryMode, error) {
switch mode {
case "000000":
Expand Down
10 changes: 5 additions & 5 deletions modules/git/tree_entry_nogogit.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,27 +59,27 @@ func (te *TreeEntry) Size() int64 {

// IsSubModule if the entry is a sub module
func (te *TreeEntry) IsSubModule() bool {
return te.entryMode == EntryModeCommit
return te.entryMode.IsSubModule()
}

// IsDir if the entry is a sub dir
func (te *TreeEntry) IsDir() bool {
return te.entryMode == EntryModeTree
return te.entryMode.IsDir()
}

// IsLink if the entry is a symlink
func (te *TreeEntry) IsLink() bool {
return te.entryMode == EntryModeSymlink
return te.entryMode.IsLink()
}

// IsRegular if the entry is a regular file
func (te *TreeEntry) IsRegular() bool {
return te.entryMode == EntryModeBlob
return te.entryMode.IsRegular()
}

// IsExecutable if the entry is an executable file (not necessarily binary)
func (te *TreeEntry) IsExecutable() bool {
return te.entryMode == EntryModeExec
return te.entryMode.IsExecutable()
}

// Blob returns the blob object the entry
Expand Down
11 changes: 6 additions & 5 deletions modules/templates/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,12 @@ func NewFuncMap() template.FuncMap {

// -----------------------------------------------------------------
// svg / avatar / icon / color
"svg": svg.RenderHTML,
"MigrationIcon": migrationIcon,
"ActionIcon": actionIcon,
"SortArrow": sortArrow,
"ContrastColor": util.ContrastColor,
"svg": svg.RenderHTML,
"folderIconHTMLByOpenStatus": FolderIconHTMLByOpenStatus,
"MigrationIcon": migrationIcon,
"ActionIcon": actionIcon,
"SortArrow": sortArrow,
"ContrastColor": util.ContrastColor,

// -----------------------------------------------------------------
// time / number / format
Expand Down
9 changes: 9 additions & 0 deletions modules/templates/util_misc.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@ import (

activities_model "code.gitea.io/gitea/models/activities"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/fileicon"
"code.gitea.io/gitea/modules/git"
giturl "code.gitea.io/gitea/modules/git/url"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/svg"

"github.com/editorconfig/editorconfig-core-go/v2"
Expand Down Expand Up @@ -192,3 +194,10 @@ func tabSizeClass(ec *editorconfig.Editorconfig, filename string) string {
}
return "tab-size-4"
}

func FolderIconHTMLByOpenStatus(isOpen bool) template.HTML {
if setting.UI.FileIconTheme == "material" {
return fileicon.DefaultMaterialIconProvider().FolderIconWithOpenStatus(nil, isOpen)
}
return fileicon.BasicThemeFolderIconWithOpenStatus(isOpen)
}
7 changes: 6 additions & 1 deletion routers/web/repo/commit.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/charset"
"code.gitea.io/gitea/modules/fileicon"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/log"
Expand Down Expand Up @@ -369,7 +370,11 @@ func Diff(ctx *context.Context) {
return
}

ctx.PageData["DiffFileTree"] = transformDiffTreeForWeb(diffTree, nil)
renderedIconPool := fileicon.NewRenderedIconPool()
ctx.PageData["DiffFileTree"] = transformDiffTreeForWeb(renderedIconPool, diffTree, nil)
ctx.PageData["FolderIcon"] = templates.FolderIconHTMLByOpenStatus(false)
ctx.PageData["FolderOpenIcon"] = templates.FolderIconHTMLByOpenStatus(true)
ctx.Data["FileIconPoolHTML"] = renderedIconPool.RenderToHTML()
}

statuses, _, err := git_model.GetLatestCommitStatus(ctx, ctx.Repo.Repository.ID, commitID, db.ListOptionsAll)
Expand Down
7 changes: 6 additions & 1 deletion routers/web/repo/compare.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/charset"
csv_module "code.gitea.io/gitea/modules/csv"
"code.gitea.io/gitea/modules/fileicon"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/log"
Expand Down Expand Up @@ -639,7 +640,11 @@ func PrepareCompareDiff(
return false
}

ctx.PageData["DiffFileTree"] = transformDiffTreeForWeb(diffTree, nil)
renderedIconPool := fileicon.NewRenderedIconPool()
ctx.PageData["DiffFileTree"] = transformDiffTreeForWeb(renderedIconPool, diffTree, nil)
ctx.PageData["FolderIcon"] = templates.FolderIconHTMLByOpenStatus(false)
ctx.PageData["FolderOpenIcon"] = templates.FolderIconHTMLByOpenStatus(true)
ctx.Data["FileIconPoolHTML"] = renderedIconPool.RenderToHTML()
}

headCommit, err := ci.HeadGitRepo.GetCommit(headCommitID)
Expand Down
8 changes: 7 additions & 1 deletion routers/web/repo/pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/emoji"
"code.gitea.io/gitea/modules/fileicon"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
issue_template "code.gitea.io/gitea/modules/issue/template"
Expand Down Expand Up @@ -824,7 +825,12 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi
if reviewState != nil {
filesViewedState = reviewState.UpdatedFiles
}
ctx.PageData["DiffFileTree"] = transformDiffTreeForWeb(diffTree, filesViewedState)

renderedIconPool := fileicon.NewRenderedIconPool()
ctx.PageData["DiffFileTree"] = transformDiffTreeForWeb(renderedIconPool, diffTree, filesViewedState)
ctx.PageData["FolderIcon"] = templates.FolderIconHTMLByOpenStatus(false)
ctx.PageData["FolderOpenIcon"] = templates.FolderIconHTMLByOpenStatus(true)
ctx.Data["FileIconPoolHTML"] = renderedIconPool.RenderToHTML()
}

ctx.Data["Diff"] = diff
Expand Down
Loading