aboutsummaryrefslogtreecommitdiffstats
path: root/modules/fileicon
diff options
context:
space:
mode:
Diffstat (limited to 'modules/fileicon')
-rw-r--r--modules/fileicon/basic.go20
-rw-r--r--modules/fileicon/entry.go31
-rw-r--r--modules/fileicon/material.go70
-rw-r--r--modules/fileicon/material_test.go9
-rw-r--r--modules/fileicon/render.go17
5 files changed, 90 insertions, 57 deletions
diff --git a/modules/fileicon/basic.go b/modules/fileicon/basic.go
index 040a8e87de..9c513ccbd9 100644
--- a/modules/fileicon/basic.go
+++ b/modules/fileicon/basic.go
@@ -6,22 +6,26 @@ package fileicon
import (
"html/template"
- "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/svg"
+ "code.gitea.io/gitea/modules/util"
)
-func BasicThemeIcon(entry *git.TreeEntry) template.HTML {
+func BasicEntryIconName(entry *EntryInfo) string {
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.SymlinkToMode.IsDir() {
svgName = "octicon-file-directory-symlink"
}
- case entry.IsDir():
- svgName = "octicon-file-directory-fill"
- case entry.IsSubModule():
+ case entry.EntryMode.IsDir():
+ svgName = util.Iif(entry.IsOpen, "octicon-file-directory-open-fill", "octicon-file-directory-fill")
+ case entry.EntryMode.IsSubModule():
svgName = "octicon-file-submodule"
}
- return svg.RenderHTML(svgName)
+ return svgName
+}
+
+func BasicEntryIconHTML(entry *EntryInfo) template.HTML {
+ return svg.RenderHTML(BasicEntryIconName(entry))
}
diff --git a/modules/fileicon/entry.go b/modules/fileicon/entry.go
new file mode 100644
index 0000000000..e4ded363e5
--- /dev/null
+++ b/modules/fileicon/entry.go
@@ -0,0 +1,31 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package fileicon
+
+import "code.gitea.io/gitea/modules/git"
+
+type EntryInfo struct {
+ FullName string
+ EntryMode git.EntryMode
+ SymlinkToMode git.EntryMode
+ IsOpen bool
+}
+
+func EntryInfoFromGitTreeEntry(gitEntry *git.TreeEntry) *EntryInfo {
+ ret := &EntryInfo{FullName: gitEntry.Name(), EntryMode: gitEntry.Mode()}
+ if gitEntry.IsLink() {
+ if te, err := gitEntry.FollowLink(); err == nil && te.IsDir() {
+ ret.SymlinkToMode = te.Mode()
+ }
+ }
+ return ret
+}
+
+func EntryInfoFolder() *EntryInfo {
+ return &EntryInfo{EntryMode: git.EntryModeTree}
+}
+
+func EntryInfoFolderOpen() *EntryInfo {
+ return &EntryInfo{EntryMode: git.EntryModeTree, IsOpen: true}
+}
diff --git a/modules/fileicon/material.go b/modules/fileicon/material.go
index 557f7ca9e4..449f527ee8 100644
--- a/modules/fileicon/material.go
+++ b/modules/fileicon/material.go
@@ -9,11 +9,12 @@ 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"
+ "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/svg"
+ "code.gitea.io/gitea/modules/util"
)
type materialIconRulesData struct {
@@ -69,41 +70,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) EntryIconHTML(p *RenderedIconPool, entry *EntryInfo) template.HTML {
if m.rules == nil {
- return BasicThemeIcon(entry)
+ return BasicEntryIconHTML(entry)
}
- if entry.IsLink() {
- if te, err := entry.FollowLink(); err == nil && te.IsDir() {
+ if entry.EntryMode.IsLink() {
+ if entry.SymlinkToMode.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)
- // 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 != "" {
- // 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():
- extraClass = "octicon-file-submodule"
+ name := m.FindIconName(entry)
+ iconSVG := m.svgs[name]
+ if iconSVG == "" {
+ name = "file"
+ if entry.EntryMode.IsDir() {
+ name = util.Iif(entry.IsOpen, "folder-open", "folder")
+ }
+ iconSVG = m.svgs[name]
+ if iconSVG == "" {
+ setting.PanicInDevOrTesting("missing file icon for %s", name)
}
- return m.renderFileIconSVG(p, name, iconSVG, extraClass)
}
- // TODO: use an interface or wrapper for git.Entry to make the code testable.
- return BasicThemeIcon(entry)
+
+ // keep the old "octicon-xxx" class name to make some "theme plugin selector" could still work
+ extraClass := "octicon-file"
+ switch {
+ case entry.EntryMode.IsDir():
+ extraClass = BasicEntryIconName(entry)
+ case entry.EntryMode.IsSubModule():
+ extraClass = "octicon-file-submodule"
+ }
+ return m.renderFileIconSVG(p, name, iconSVG, extraClass)
}
func (m *MaterialIconProvider) findIconNameWithLangID(s string) string {
@@ -118,13 +129,17 @@ func (m *MaterialIconProvider) findIconNameWithLangID(s string) string {
return ""
}
-func (m *MaterialIconProvider) FindIconName(name string, isDir bool) string {
- fileNameLower := strings.ToLower(path.Base(name))
- if isDir {
+func (m *MaterialIconProvider) FindIconName(entry *EntryInfo) string {
+ if entry.EntryMode.IsSubModule() {
+ return "folder-git"
+ }
+
+ fileNameLower := strings.ToLower(path.Base(entry.FullName))
+ if entry.EntryMode.IsDir() {
if s, ok := m.rules.FolderNames[fileNameLower]; ok {
return s
}
- return "folder"
+ return util.Iif(entry.IsOpen, "folder-open", "folder")
}
if s, ok := m.rules.FileNames[fileNameLower]; ok {
@@ -146,10 +161,3 @@ func (m *MaterialIconProvider) FindIconName(name string, isDir bool) string {
return "file"
}
-
-func (m *MaterialIconProvider) findIconNameByGit(entry *git.TreeEntry) string {
- if entry.IsSubModule() {
- return "folder-git"
- }
- return m.FindIconName(entry.Name(), entry.IsDir())
-}
diff --git a/modules/fileicon/material_test.go b/modules/fileicon/material_test.go
index f36385aaf3..68353d2189 100644
--- a/modules/fileicon/material_test.go
+++ b/modules/fileicon/material_test.go
@@ -8,6 +8,7 @@ import (
"code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/fileicon"
+ "code.gitea.io/gitea/modules/git"
"github.com/stretchr/testify/assert"
)
@@ -19,8 +20,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(&fileicon.EntryInfo{FullName: "foo.php", EntryMode: git.EntryModeBlob}))
+ assert.Equal(t, "php", p.FindIconName(&fileicon.EntryInfo{FullName: "foo.PHP", EntryMode: git.EntryModeBlob}))
+ assert.Equal(t, "javascript", p.FindIconName(&fileicon.EntryInfo{FullName: "foo.js", EntryMode: git.EntryModeBlob}))
+ assert.Equal(t, "visualstudio", p.FindIconName(&fileicon.EntryInfo{FullName: "foo.vba", EntryMode: git.EntryModeBlob}))
}
diff --git a/modules/fileicon/render.go b/modules/fileicon/render.go
index 1d014693fd..8ed86b9ac0 100644
--- a/modules/fileicon/render.go
+++ b/modules/fileicon/render.go
@@ -7,7 +7,6 @@ import (
"html/template"
"strings"
- "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/setting"
)
@@ -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 RenderEntryIconHTML(renderedIconPool *RenderedIconPool, entry *EntryInfo) template.HTML {
if setting.UI.FileIconTheme == "material" {
- return DefaultMaterialIconProvider().FileIcon(renderedIconPool, entry)
+ return DefaultMaterialIconProvider().EntryIconHTML(renderedIconPool, entry)
}
- return BasicThemeIcon(entry)
+ return BasicEntryIconHTML(entry)
}