mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-05-02 02:22:06 -04:00
launcher: add elide helpers for RichText
This commit is contained in:
93
quickshell/Common/htmlElide.js
Normal file
93
quickshell/Common/htmlElide.js
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
.pragma library
|
||||||
|
|
||||||
|
function stripHtmlTags(html) {
|
||||||
|
if (!html)
|
||||||
|
return "";
|
||||||
|
return String(html)
|
||||||
|
.replace(/<[^>]+>/g, "")
|
||||||
|
.replace(/ /g, " ")
|
||||||
|
.replace(/&/g, "&")
|
||||||
|
.replace(/</g, "<")
|
||||||
|
.replace(/>/g, ">")
|
||||||
|
.replace(/"/g, "\"")
|
||||||
|
.replace(/'/g, "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
function elideRichText(html, visibleBudget) {
|
||||||
|
if (!html)
|
||||||
|
return "";
|
||||||
|
if (visibleBudget <= 0)
|
||||||
|
return "";
|
||||||
|
|
||||||
|
var out = "";
|
||||||
|
var visible = 0;
|
||||||
|
var i = 0;
|
||||||
|
var openTags = [];
|
||||||
|
var len = html.length;
|
||||||
|
|
||||||
|
while (i < len && visible < visibleBudget) {
|
||||||
|
var ch = html.charAt(i);
|
||||||
|
if (ch === "<") {
|
||||||
|
var end = html.indexOf(">", i);
|
||||||
|
if (end < 0)
|
||||||
|
break;
|
||||||
|
var tag = html.substring(i, end + 1);
|
||||||
|
out += tag;
|
||||||
|
var isClose = tag.charAt(1) === "/";
|
||||||
|
var match = tag.match(/^<\/?([a-zA-Z]+)/);
|
||||||
|
var name = match ? match[1] : "";
|
||||||
|
if (isClose) {
|
||||||
|
if (openTags.length > 0 && openTags[openTags.length - 1] === name)
|
||||||
|
openTags.pop();
|
||||||
|
} else if (!tag.endsWith("/>") && name) {
|
||||||
|
openTags.push(name);
|
||||||
|
}
|
||||||
|
i = end + 1;
|
||||||
|
} else if (ch === "&") {
|
||||||
|
var eend = html.indexOf(";", i);
|
||||||
|
if (eend < 0 || eend - i > 6) {
|
||||||
|
out += "&";
|
||||||
|
visible++;
|
||||||
|
i++;
|
||||||
|
} else {
|
||||||
|
out += html.substring(i, eend + 1);
|
||||||
|
visible++;
|
||||||
|
i = eend + 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
out += ch;
|
||||||
|
visible++;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (i < len && html.charAt(i) === "<") {
|
||||||
|
var tend = html.indexOf(">", i);
|
||||||
|
if (tend < 0)
|
||||||
|
break;
|
||||||
|
var ttag = html.substring(i, tend + 1);
|
||||||
|
out += ttag;
|
||||||
|
var tisClose = ttag.charAt(1) === "/";
|
||||||
|
var tmatch = ttag.match(/^<\/?([a-zA-Z]+)/);
|
||||||
|
var tname = tmatch ? tmatch[1] : "";
|
||||||
|
if (tisClose) {
|
||||||
|
if (openTags.length > 0 && openTags[openTags.length - 1] === tname)
|
||||||
|
openTags.pop();
|
||||||
|
} else if (!ttag.endsWith("/>") && tname) {
|
||||||
|
openTags.push(tname);
|
||||||
|
}
|
||||||
|
i = tend + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i < len) {
|
||||||
|
out = out.replace(/\s+$/, "");
|
||||||
|
while (openTags.length > 0)
|
||||||
|
out += "</" + openTags.pop() + ">";
|
||||||
|
out += "…";
|
||||||
|
} else {
|
||||||
|
while (openTags.length > 0)
|
||||||
|
out += "</" + openTags.pop() + ">";
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ pragma ComponentBehavior: Bound
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
|
import "../../Common/htmlElide.js" as HtmlElide
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: root
|
id: root
|
||||||
@@ -72,15 +73,12 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.leftMargin: Theme.spacingM
|
|
||||||
anchors.rightMargin: Theme.spacingM
|
|
||||||
spacing: Theme.spacingM
|
|
||||||
|
|
||||||
AppIconRenderer {
|
AppIconRenderer {
|
||||||
|
id: iconRenderer
|
||||||
width: 36
|
width: 36
|
||||||
height: 36
|
height: 36
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: Theme.spacingM
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
iconValue: root.iconValue
|
iconValue: root.iconValue
|
||||||
iconSize: 36
|
iconSize: 36
|
||||||
@@ -88,32 +86,62 @@ Rectangle {
|
|||||||
materialIconSizeAdjustment: 12
|
materialIconSizeAdjustment: 12
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Item {
|
||||||
|
id: textColumn
|
||||||
|
anchors.left: iconRenderer.right
|
||||||
|
anchors.leftMargin: Theme.spacingM
|
||||||
|
anchors.right: rightContent.left
|
||||||
|
anchors.rightMargin: rightContent.width > 0 ? Theme.spacingM : 0
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
width: parent.width - 36 - Theme.spacingM * 3 - rightContent.width
|
height: nameText.implicitHeight + (subText.visible ? subText.height + 2 : 0)
|
||||||
spacing: 2
|
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
width: parent.width
|
id: nameText
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.top: parent.top
|
||||||
text: root.item?._hName ?? root.item?.name ?? ""
|
text: root.item?._hName ?? root.item?.name ?? ""
|
||||||
textFormat: root.item?._hRich ? Text.RichText : Text.PlainText
|
textFormat: root.item?._hRich ? Text.RichText : Text.PlainText
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
font.family: Theme.fontFamily
|
font.family: Theme.fontFamily
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
maximumLineCount: 1
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
horizontalAlignment: Text.AlignLeft
|
horizontalAlignment: Text.AlignLeft
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TextMetrics {
|
||||||
|
id: subProbe
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
font.family: Theme.fontFamily
|
||||||
|
elide: Qt.ElideRight
|
||||||
|
elideWidth: textColumn.width
|
||||||
|
text: root.item?._hRich ? HtmlElide.stripHtmlTags(root.item?._hSub ?? "") : ""
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property int _richBudget: {
|
||||||
|
if (!subProbe.text)
|
||||||
|
return 0;
|
||||||
|
var e = subProbe.elidedText;
|
||||||
|
return e.endsWith("…") ? e.length - 1 : e.length;
|
||||||
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
width: parent.width
|
id: subText
|
||||||
text: root.item?._hSub ?? root.item?.subtitle ?? ""
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.top: nameText.bottom
|
||||||
|
anchors.topMargin: 2
|
||||||
|
text: root.item?._hRich ? HtmlElide.elideRichText(root.item._hSub ?? "", textColumn._richBudget) : (root.item?.subtitle ?? "")
|
||||||
textFormat: root.item?._hRich ? Text.RichText : Text.PlainText
|
textFormat: root.item?._hRich ? Text.RichText : Text.PlainText
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
font.family: Theme.fontFamily
|
font.family: Theme.fontFamily
|
||||||
color: Theme.surfaceVariantText
|
color: Theme.surfaceVariantText
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
maximumLineCount: 1
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
clip: true
|
|
||||||
visible: (root.item?.subtitle ?? "").length > 0
|
visible: (root.item?.subtitle ?? "").length > 0
|
||||||
horizontalAlignment: Text.AlignLeft
|
horizontalAlignment: Text.AlignLeft
|
||||||
}
|
}
|
||||||
@@ -121,6 +149,8 @@ Rectangle {
|
|||||||
|
|
||||||
Row {
|
Row {
|
||||||
id: rightContent
|
id: rightContent
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: Theme.spacingM
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
@@ -198,5 +228,4 @@ Rectangle {
|
|||||||
glyphSize: 14
|
glyphSize: 14
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user