mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-06-24 12:05:21 -04:00
feat(pluginBrowser): Add inline image previews while browsing
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/mocks/net"
|
"github.com/AvengeMedia/DankMaterialShell/core/internal/mocks/net"
|
||||||
|
coreplugins "github.com/AvengeMedia/DankMaterialShell/core/internal/plugins"
|
||||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
|
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/mock"
|
"github.com/stretchr/testify/mock"
|
||||||
@@ -165,6 +166,7 @@ func TestPluginInfoJSON(t *testing.T) {
|
|||||||
info := PluginInfo{
|
info := PluginInfo{
|
||||||
Name: "test",
|
Name: "test",
|
||||||
Description: "test description",
|
Description: "test description",
|
||||||
|
Screenshot: "https://raw.githubusercontent.com/test/repo/main/screenshot.png",
|
||||||
Installed: true,
|
Installed: true,
|
||||||
FirstParty: true,
|
FirstParty: true,
|
||||||
}
|
}
|
||||||
@@ -177,6 +179,59 @@ func TestPluginInfoJSON(t *testing.T) {
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, info.Name, unmarshaled.Name)
|
assert.Equal(t, info.Name, unmarshaled.Name)
|
||||||
assert.Equal(t, info.Installed, unmarshaled.Installed)
|
assert.Equal(t, info.Installed, unmarshaled.Installed)
|
||||||
|
assert.Equal(t, info.Screenshot, unmarshaled.Screenshot)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNormalizeScreenshotURL(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
raw string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "raw github url is unchanged",
|
||||||
|
raw: "https://raw.githubusercontent.com/alcxyz/DankVault/main/docs/screenshot.png",
|
||||||
|
want: "https://raw.githubusercontent.com/alcxyz/DankVault/main/docs/screenshot.png",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "github blob url becomes raw content url",
|
||||||
|
raw: "https://github.com/acmagn/DMS-UPS-Monitor/blob/main/assets/screenshot.png",
|
||||||
|
want: "https://raw.githubusercontent.com/acmagn/DMS-UPS-Monitor/main/assets/screenshot.png",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "github raw url becomes raw content url",
|
||||||
|
raw: "https://github.com/antonjah/nix-monitor/raw/master/assets/scrot.png",
|
||||||
|
want: "https://raw.githubusercontent.com/antonjah/nix-monitor/master/assets/scrot.png",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "non github url is unchanged",
|
||||||
|
raw: "https://example.com/screenshot.png",
|
||||||
|
want: "https://example.com/screenshot.png",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty url is empty",
|
||||||
|
raw: " ",
|
||||||
|
want: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
assert.Equal(t, tt.want, normalizeScreenshotURL(tt.raw))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPluginInfoFromPluginIncludesScreenshot(t *testing.T) {
|
||||||
|
info := pluginInfoFromPlugin(coreplugins.Plugin{
|
||||||
|
ID: "dankVault",
|
||||||
|
Name: "Vault",
|
||||||
|
Repo: "https://github.com/AvengeMedia/dms-plugins",
|
||||||
|
Screenshot: "https://github.com/AvengeMedia/dms-plugins/blob/master/DankNotepadModule/screenshot.png",
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Equal(t, "https://raw.githubusercontent.com/AvengeMedia/dms-plugins/master/DankNotepadModule/screenshot.png", info.Screenshot)
|
||||||
|
assert.True(t, info.FirstParty)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSuccessResult(t *testing.T) {
|
func TestSuccessResult(t *testing.T) {
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package plugins
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/plugins"
|
"github.com/AvengeMedia/DankMaterialShell/core/internal/plugins"
|
||||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
|
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
|
||||||
@@ -34,25 +33,12 @@ func HandleList(conn net.Conn, req models.Request) {
|
|||||||
for i, p := range pluginList {
|
for i, p := range pluginList {
|
||||||
installed, _ := manager.IsInstalled(p)
|
installed, _ := manager.IsInstalled(p)
|
||||||
fb := feedback[p.ID]
|
fb := feedback[p.ID]
|
||||||
result[i] = PluginInfo{
|
info := pluginInfoFromPlugin(p)
|
||||||
ID: p.ID,
|
info.Installed = installed
|
||||||
Name: p.Name,
|
info.Upvotes = fb.Upvotes
|
||||||
Category: p.Category,
|
info.Status = fb.Status
|
||||||
Author: p.Author,
|
info.IssueURL = fb.IssueURL
|
||||||
Description: p.Description,
|
result[i] = info
|
||||||
Repo: p.Repo,
|
|
||||||
Path: p.Path,
|
|
||||||
Capabilities: p.Capabilities,
|
|
||||||
Compositors: p.Compositors,
|
|
||||||
Dependencies: p.Dependencies,
|
|
||||||
Installed: installed,
|
|
||||||
FirstParty: strings.HasPrefix(p.Repo, "https://github.com/AvengeMedia"),
|
|
||||||
Featured: p.Featured,
|
|
||||||
RequiresDMS: p.RequiresDMS,
|
|
||||||
Upvotes: fb.Upvotes,
|
|
||||||
Status: fb.Status,
|
|
||||||
IssueURL: fb.IssueURL,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
models.Respond(conn, req.ID, result)
|
models.Respond(conn, req.ID, result)
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package plugins
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/plugins"
|
"github.com/AvengeMedia/DankMaterialShell/core/internal/plugins"
|
||||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
|
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
|
||||||
@@ -47,21 +46,9 @@ func HandleListInstalled(conn net.Conn, req models.Request) {
|
|||||||
hasUpdate = hasUpdates
|
hasUpdate = hasUpdates
|
||||||
}
|
}
|
||||||
|
|
||||||
result = append(result, PluginInfo{
|
info := pluginInfoFromPlugin(plugin)
|
||||||
ID: plugin.ID,
|
info.HasUpdate = hasUpdate
|
||||||
Name: plugin.Name,
|
result = append(result, info)
|
||||||
Category: plugin.Category,
|
|
||||||
Author: plugin.Author,
|
|
||||||
Description: plugin.Description,
|
|
||||||
Repo: plugin.Repo,
|
|
||||||
Path: plugin.Path,
|
|
||||||
Capabilities: plugin.Capabilities,
|
|
||||||
Compositors: plugin.Compositors,
|
|
||||||
Dependencies: plugin.Dependencies,
|
|
||||||
FirstParty: strings.HasPrefix(plugin.Repo, "https://github.com/AvengeMedia"),
|
|
||||||
HasUpdate: hasUpdate,
|
|
||||||
RequiresDMS: plugin.RequiresDMS,
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
result = append(result, PluginInfo{
|
result = append(result, PluginInfo{
|
||||||
ID: id,
|
ID: id,
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package plugins
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/plugins"
|
"github.com/AvengeMedia/DankMaterialShell/core/internal/plugins"
|
||||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
|
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
|
||||||
@@ -53,21 +52,9 @@ func HandleSearch(conn net.Conn, req models.Request) {
|
|||||||
result := make([]PluginInfo, len(searchResults))
|
result := make([]PluginInfo, len(searchResults))
|
||||||
for i, p := range searchResults {
|
for i, p := range searchResults {
|
||||||
installed, _ := manager.IsInstalled(p)
|
installed, _ := manager.IsInstalled(p)
|
||||||
result[i] = PluginInfo{
|
info := pluginInfoFromPlugin(p)
|
||||||
ID: p.ID,
|
info.Installed = installed
|
||||||
Name: p.Name,
|
result[i] = info
|
||||||
Category: p.Category,
|
|
||||||
Author: p.Author,
|
|
||||||
Description: p.Description,
|
|
||||||
Repo: p.Repo,
|
|
||||||
Path: p.Path,
|
|
||||||
Capabilities: p.Capabilities,
|
|
||||||
Compositors: p.Compositors,
|
|
||||||
Dependencies: p.Dependencies,
|
|
||||||
Installed: installed,
|
|
||||||
FirstParty: strings.HasPrefix(p.Repo, "https://github.com/AvengeMedia"),
|
|
||||||
RequiresDMS: p.RequiresDMS,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
models.Respond(conn, req.ID, result)
|
models.Respond(conn, req.ID, result)
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ type PluginInfo struct {
|
|||||||
Description string `json:"description,omitempty"`
|
Description string `json:"description,omitempty"`
|
||||||
Repo string `json:"repo,omitempty"`
|
Repo string `json:"repo,omitempty"`
|
||||||
Path string `json:"path,omitempty"`
|
Path string `json:"path,omitempty"`
|
||||||
|
Screenshot string `json:"screenshot,omitempty"`
|
||||||
Capabilities []string `json:"capabilities,omitempty"`
|
Capabilities []string `json:"capabilities,omitempty"`
|
||||||
Compositors []string `json:"compositors,omitempty"`
|
Compositors []string `json:"compositors,omitempty"`
|
||||||
Dependencies []string `json:"dependencies,omitempty"`
|
Dependencies []string `json:"dependencies,omitempty"`
|
||||||
|
|||||||
@@ -1,14 +1,65 @@
|
|||||||
package plugins
|
package plugins
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/url"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
coreplugins "github.com/AvengeMedia/DankMaterialShell/core/internal/plugins"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func pluginInfoFromPlugin(plugin coreplugins.Plugin) PluginInfo {
|
||||||
|
return PluginInfo{
|
||||||
|
ID: plugin.ID,
|
||||||
|
Name: plugin.Name,
|
||||||
|
Category: plugin.Category,
|
||||||
|
Author: plugin.Author,
|
||||||
|
Description: plugin.Description,
|
||||||
|
Repo: plugin.Repo,
|
||||||
|
Path: plugin.Path,
|
||||||
|
Screenshot: normalizeScreenshotURL(plugin.Screenshot),
|
||||||
|
Capabilities: plugin.Capabilities,
|
||||||
|
Compositors: plugin.Compositors,
|
||||||
|
Dependencies: plugin.Dependencies,
|
||||||
|
FirstParty: isFirstPartyRepo(plugin.Repo),
|
||||||
|
Featured: plugin.Featured,
|
||||||
|
RequiresDMS: plugin.RequiresDMS,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isFirstPartyRepo(repo string) bool {
|
||||||
|
return strings.HasPrefix(repo, "https://github.com/AvengeMedia")
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalizeScreenshotURL(raw string) string {
|
||||||
|
screenshotURL := strings.TrimSpace(raw)
|
||||||
|
if screenshotURL == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
parsed, err := url.Parse(screenshotURL)
|
||||||
|
if err != nil {
|
||||||
|
return screenshotURL
|
||||||
|
}
|
||||||
|
|
||||||
|
host := strings.ToLower(parsed.Host)
|
||||||
|
if host != "github.com" && host != "www.github.com" {
|
||||||
|
return screenshotURL
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := strings.Split(strings.Trim(parsed.EscapedPath(), "/"), "/")
|
||||||
|
if len(parts) < 5 || (parts[2] != "blob" && parts[2] != "raw") {
|
||||||
|
return screenshotURL
|
||||||
|
}
|
||||||
|
|
||||||
|
rawParts := append([]string{parts[0], parts[1], parts[3]}, parts[4:]...)
|
||||||
|
return "https://raw.githubusercontent.com/" + strings.Join(rawParts, "/")
|
||||||
|
}
|
||||||
|
|
||||||
func SortPluginInfoByFirstParty(pluginInfos []PluginInfo) {
|
func SortPluginInfoByFirstParty(pluginInfos []PluginInfo) {
|
||||||
sort.SliceStable(pluginInfos, func(i, j int) bool {
|
sort.SliceStable(pluginInfos, func(i, j int) bool {
|
||||||
isFirstPartyI := strings.HasPrefix(pluginInfos[i].Repo, "https://github.com/AvengeMedia")
|
isFirstPartyI := isFirstPartyRepo(pluginInfos[i].Repo)
|
||||||
isFirstPartyJ := strings.HasPrefix(pluginInfos[j].Repo, "https://github.com/AvengeMedia")
|
isFirstPartyJ := isFirstPartyRepo(pluginInfos[j].Repo)
|
||||||
if isFirstPartyI != isFirstPartyJ {
|
if isFirstPartyI != isFirstPartyJ {
|
||||||
return isFirstPartyI
|
return isFirstPartyI
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,12 +18,14 @@ FloatingWindow {
|
|||||||
property bool keyboardNavigationActive: false
|
property bool keyboardNavigationActive: false
|
||||||
property bool isLoading: false
|
property bool isLoading: false
|
||||||
property var parentModal: null
|
property var parentModal: null
|
||||||
parentWindow: parentModal
|
parentWindow: null
|
||||||
property bool pendingInstallHandled: false
|
property bool pendingInstallHandled: false
|
||||||
property string typeFilter: ""
|
property string typeFilter: ""
|
||||||
property string categoryFilter: "all"
|
property string categoryFilter: "all"
|
||||||
property var categoryFilterOptions: []
|
property var categoryFilterOptions: []
|
||||||
property var availableLetters: []
|
property var availableLetters: []
|
||||||
|
property string expandedPluginId: ""
|
||||||
|
property string enlargedPreviewPluginId: ""
|
||||||
|
|
||||||
readonly property bool activeCategorySort: normalizedSortMode(SessionData.pluginBrowserSortMode) === "category"
|
readonly property bool activeCategorySort: normalizedSortMode(SessionData.pluginBrowserSortMode) === "category"
|
||||||
readonly property bool showCategoryFilters: activeCategorySort && categoryFilterOptions.length > 1
|
readonly property bool showCategoryFilters: activeCategorySort && categoryFilterOptions.length > 1
|
||||||
@@ -255,6 +257,9 @@ FloatingWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function updateFilteredPlugins() {
|
function updateFilteredPlugins() {
|
||||||
|
expandedPluginId = "";
|
||||||
|
enlargedPreviewPluginId = "";
|
||||||
|
|
||||||
var baseFiltered = [];
|
var baseFiltered = [];
|
||||||
var query = searchQuery ? searchQuery.toLowerCase() : "";
|
var query = searchQuery ? searchQuery.toLowerCase() : "";
|
||||||
|
|
||||||
@@ -340,6 +345,35 @@ FloatingWindow {
|
|||||||
refreshListLayout();
|
refreshListLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function pluginKey(plugin, fallbackIndex) {
|
||||||
|
if (!plugin)
|
||||||
|
return "plugin-" + fallbackIndex;
|
||||||
|
return plugin.id || plugin.name || ("plugin-" + fallbackIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleExpandedPlugin(pluginId) {
|
||||||
|
if (expandedPluginId === pluginId) {
|
||||||
|
expandedPluginId = "";
|
||||||
|
enlargedPreviewPluginId = "";
|
||||||
|
} else {
|
||||||
|
expandedPluginId = pluginId;
|
||||||
|
enlargedPreviewPluginId = "";
|
||||||
|
}
|
||||||
|
keyboardNavigationActive = false;
|
||||||
|
Qt.callLater(() => {
|
||||||
|
if (pluginBrowserList)
|
||||||
|
pluginBrowserList.forceLayout();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleEnlargedPreview(pluginId) {
|
||||||
|
enlargedPreviewPluginId = enlargedPreviewPluginId === pluginId ? "" : pluginId;
|
||||||
|
Qt.callLater(() => {
|
||||||
|
if (pluginBrowserList)
|
||||||
|
pluginBrowserList.forceLayout();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function selectNext() {
|
function selectNext() {
|
||||||
if (filteredPlugins.length === 0)
|
if (filteredPlugins.length === 0)
|
||||||
return;
|
return;
|
||||||
@@ -445,6 +479,8 @@ FloatingWindow {
|
|||||||
selectedIndex = -1;
|
selectedIndex = -1;
|
||||||
keyboardNavigationActive = false;
|
keyboardNavigationActive = false;
|
||||||
isLoading = false;
|
isLoading = false;
|
||||||
|
expandedPluginId = "";
|
||||||
|
enlargedPreviewPluginId = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
@@ -828,6 +864,8 @@ FloatingWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
delegate: Rectangle {
|
delegate: Rectangle {
|
||||||
|
id: pluginDelegate
|
||||||
|
|
||||||
width: pluginBrowserList.width
|
width: pluginBrowserList.width
|
||||||
height: pluginDelegateColumn.implicitHeight + Theme.spacingM * 2
|
height: pluginDelegateColumn.implicitHeight + Theme.spacingM * 2
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
@@ -836,12 +874,26 @@ FloatingWindow {
|
|||||||
property bool isFirstParty: modelData.firstParty || false
|
property bool isFirstParty: modelData.firstParty || false
|
||||||
property bool isFeatured: modelData.featured || false
|
property bool isFeatured: modelData.featured || false
|
||||||
property bool isCompatible: PluginService.checkPluginCompatibility(modelData.requires_dms)
|
property bool isCompatible: PluginService.checkPluginCompatibility(modelData.requires_dms)
|
||||||
color: isSelected ? Theme.primarySelected : Theme.withAlpha(Theme.surfaceVariant, 0.3)
|
property string pluginId: root.pluginKey(modelData, index)
|
||||||
|
property bool isExpanded: root.expandedPluginId === pluginId
|
||||||
|
property bool isPreviewEnlarged: root.enlargedPreviewPluginId === pluginId
|
||||||
|
property string screenshotUrl: modelData.screenshot || ""
|
||||||
|
color: isSelected ? Theme.primarySelected : rowMouseArea.containsMouse ? Theme.withAlpha(Theme.surfaceVariant, 0.45) : Theme.withAlpha(Theme.surfaceVariant, 0.3)
|
||||||
border.color: isSelected ? Theme.primary : Theme.withAlpha(Theme.outline, 0.2)
|
border.color: isSelected ? Theme.primary : Theme.withAlpha(Theme.outline, 0.2)
|
||||||
border.width: isSelected ? 2 : 1
|
border.width: isSelected ? 2 : 1
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: rowMouseArea
|
||||||
|
z: 0
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: root.toggleExpandedPlugin(pluginDelegate.pluginId)
|
||||||
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
id: pluginDelegateColumn
|
id: pluginDelegateColumn
|
||||||
|
z: 1
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Theme.spacingM
|
anchors.margins: Theme.spacingM
|
||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
@@ -1171,6 +1223,76 @@ FloatingWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: screenshotPreview
|
||||||
|
width: parent.width
|
||||||
|
height: pluginDelegate.isExpanded ? (pluginDelegate.isPreviewEnlarged ? Math.min(620, Math.max(320, width * 0.78)) : Math.min(260, Math.max(150, width * 0.42))) : 0
|
||||||
|
visible: height > 0
|
||||||
|
clip: true
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: Theme.surfaceContainerHigh
|
||||||
|
border.color: Theme.withAlpha(Theme.outline, 0.2)
|
||||||
|
border.width: 1
|
||||||
|
|
||||||
|
Behavior on height {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.standardEasing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: screenshotImageLoader
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 1
|
||||||
|
active: pluginDelegate.isExpanded && pluginDelegate.screenshotUrl.length > 0
|
||||||
|
|
||||||
|
sourceComponent: CachingImage {
|
||||||
|
imagePath: pluginDelegate.screenshotUrl
|
||||||
|
maxCacheSize: pluginDelegate.isPreviewEnlarged ? 1600 : 960
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
visible: status !== Image.Error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
enabled: screenshotImageLoader.item && screenshotImageLoader.item.status === Image.Ready
|
||||||
|
hoverEnabled: enabled
|
||||||
|
cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||||
|
onClicked: mouse => {
|
||||||
|
mouse.accepted = true;
|
||||||
|
root.toggleEnlargedPreview(pluginDelegate.pluginId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DankSpinner {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
running: screenshotImageLoader.active && screenshotImageLoader.item && screenshotImageLoader.item.status === Image.Loading
|
||||||
|
visible: running
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
visible: pluginDelegate.isExpanded && (pluginDelegate.screenshotUrl.length === 0 || (screenshotImageLoader.item && screenshotImageLoader.item.status === Image.Error))
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
name: screenshotImageLoader.item && screenshotImageLoader.item.status === Image.Error ? "broken_image" : "image_not_supported"
|
||||||
|
size: Theme.iconSize
|
||||||
|
color: Theme.outline
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
text: screenshotImageLoader.item && screenshotImageLoader.item.status === Image.Error ? I18n.tr("Screenshot unavailable", "plugin browser screenshot error") : I18n.tr("No screenshot provided", "plugin browser no screenshot")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.outline
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user