mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-09-11 11:15:12 -04:00
Toast improvement & UI cleanup
This commit is contained in:
+73
-70
@@ -55,7 +55,7 @@ catalog-filters select-button input {
|
||||
font-size: var(--font-size-sm);
|
||||
}
|
||||
|
||||
catalog-filters button.catalog-library-link {
|
||||
.catalog-library-link {
|
||||
padding: var(--space-sm) 10dp;
|
||||
border-radius: var(--radius-panel);
|
||||
text-align: left;
|
||||
@@ -78,7 +78,7 @@ catalog-results header {
|
||||
padding-right: var(--space-2xl);
|
||||
}
|
||||
|
||||
catalog-results header catalog-results-summary {
|
||||
catalog-results > header small {
|
||||
display: block;
|
||||
margin-top: var(--space-2xs);
|
||||
font-size: var(--font-size-xs);
|
||||
@@ -101,7 +101,7 @@ catalog-grid {
|
||||
padding: var(--space-2xs);
|
||||
}
|
||||
|
||||
button.catalog-card {
|
||||
.catalog-card {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
flex: 0 0 48%;
|
||||
@@ -134,20 +134,14 @@ catalog-card-art-shadow {
|
||||
|
||||
catalog-card-body {
|
||||
display: flex;
|
||||
position: relative;
|
||||
flex-flow: column;
|
||||
flex: 1 1 auto;
|
||||
min-height: 0;
|
||||
padding: var(--space-xl) var(--space-lg) var(--space-md) var(--space-lg);
|
||||
padding: var(--space-xl) var(--space-lg) var(--space-lg);
|
||||
gap: 7dp;
|
||||
}
|
||||
|
||||
catalog-card-kicker {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6dp;
|
||||
font-size: var(--font-size-sm);
|
||||
}
|
||||
|
||||
catalog-official-badge {
|
||||
padding: var(--space-2xs) 5dp;
|
||||
border-radius: var(--radius-small);
|
||||
@@ -157,7 +151,8 @@ catalog-official-badge {
|
||||
font-size: 9dp;
|
||||
}
|
||||
|
||||
catalog-card-kicker > catalog-card-category {
|
||||
catalog-card-body > section > b {
|
||||
display: block;
|
||||
font-family: var(--font-family-heading);
|
||||
font-size: var(--font-size-sm);
|
||||
font-weight: bold;
|
||||
@@ -165,18 +160,19 @@ catalog-card-kicker > catalog-card-category {
|
||||
color: var(--color-accent);
|
||||
}
|
||||
|
||||
catalog-card-updated {
|
||||
margin-left: auto;
|
||||
catalog-card-body > small {
|
||||
position: absolute;
|
||||
top: var(--space-md);
|
||||
right: var(--space-lg);
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: var(--font-size-sm);
|
||||
font-weight: normal;
|
||||
text-transform: none;
|
||||
opacity: 0.45;
|
||||
}
|
||||
|
||||
catalog-card-identity {
|
||||
display: block;
|
||||
}
|
||||
|
||||
catalog-card-icon {
|
||||
.catalog-card mod-icon {
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 14dp;
|
||||
@@ -190,14 +186,22 @@ catalog-card-icon {
|
||||
box-shadow: rgba(var(--color-black-rgb), 60%) 0 6dp 16dp;
|
||||
}
|
||||
|
||||
catalog-card-icon-image {
|
||||
mod-icon-image {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: var(--radius-panel);
|
||||
}
|
||||
|
||||
catalog-card-title {
|
||||
catalog-card-body > section {
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
catalog-card-body > section h2 {
|
||||
display: block;
|
||||
margin: 0;
|
||||
font-family: var(--font-family-body);
|
||||
font-size: var(--font-size-3xl);
|
||||
font-weight: bold;
|
||||
@@ -205,22 +209,23 @@ catalog-card-title {
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
catalog-card-author {
|
||||
catalog-card-body > section small {
|
||||
display: block;
|
||||
font-size: var(--font-size-3xs);
|
||||
opacity: 0.55;
|
||||
}
|
||||
|
||||
catalog-card-summary {
|
||||
catalog-card-body > p {
|
||||
flex: 1 1 auto;
|
||||
min-height: 0;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
font-size: var(--font-size-base);
|
||||
line-height: 1.35;
|
||||
color: rgba(var(--color-text-rgb), 68%);
|
||||
}
|
||||
|
||||
catalog-card-meta {
|
||||
catalog-card-body > footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10dp;
|
||||
@@ -228,24 +233,24 @@ catalog-card-meta {
|
||||
opacity: 0.52;
|
||||
}
|
||||
|
||||
catalog-card-stat {
|
||||
catalog-card-body > footer stat {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 3dp;
|
||||
}
|
||||
|
||||
catalog-card-meta icon {
|
||||
catalog-card-body > footer icon {
|
||||
flex: 0 0 13dp;
|
||||
font-size: var(--font-size-sm);
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
catalog-card-size {
|
||||
catalog-card-body > footer .size {
|
||||
margin-left: auto;
|
||||
font-size: var(--font-size-sm);
|
||||
}
|
||||
|
||||
catalog-card-size.installed {
|
||||
catalog-card-body > footer .size.installed {
|
||||
color: var(--color-success);
|
||||
}
|
||||
|
||||
@@ -280,15 +285,19 @@ catalog-detail-status {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
catalog-status-title {
|
||||
catalog-results-status h2,
|
||||
catalog-detail-status h2 {
|
||||
display: block;
|
||||
margin: 0;
|
||||
font-family: var(--font-family-heading);
|
||||
font-size: 25dp;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
catalog-status-message {
|
||||
catalog-results-status p,
|
||||
catalog-detail-status p {
|
||||
display: block;
|
||||
margin: 0;
|
||||
opacity: 0.58;
|
||||
}
|
||||
|
||||
@@ -364,7 +373,7 @@ catalog-source-actions button {
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
button.catalog-install-action {
|
||||
.catalog-install-action {
|
||||
display: flex;
|
||||
position: relative;
|
||||
align-items: center;
|
||||
@@ -383,33 +392,33 @@ button.catalog-install-action {
|
||||
box-shadow: var(--color-accent) 0 0 0 2dp;
|
||||
}
|
||||
|
||||
button.catalog-install-action icon {
|
||||
.catalog-install-action icon {
|
||||
flex: 0 0 22dp;
|
||||
font-size: 22dp;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
button.catalog-install-action catalog-action-label {
|
||||
.catalog-install-action > span {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
button.catalog-install-action.idle {
|
||||
.catalog-install-action.idle {
|
||||
--button-background: rgba(var(--color-interactive-rgb), 40%);
|
||||
--button-background-hover: rgba(var(--color-interactive-rgb), 55%);
|
||||
--button-background-selected: rgba(var(--color-interactive-rgb), 55%);
|
||||
--button-background-active: rgba(var(--color-interactive-rgb), 55%);
|
||||
}
|
||||
|
||||
button.catalog-install-action.paused {
|
||||
.catalog-install-action.paused {
|
||||
box-shadow: rgba(var(--color-border-rgb), 25%) 0 0 0 1dp;
|
||||
}
|
||||
|
||||
button.catalog-install-action.paused:not(:disabled):hover,
|
||||
button.catalog-install-action.paused:not(:disabled):focus-visible {
|
||||
.catalog-install-action.paused:not(:disabled):hover,
|
||||
.catalog-install-action.paused:not(:disabled):focus-visible {
|
||||
box-shadow: var(--color-accent) 0 0 0 2dp;
|
||||
}
|
||||
|
||||
button.catalog-install-action progress {
|
||||
.catalog-install-action progress {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
@@ -422,25 +431,25 @@ button.catalog-install-action progress {
|
||||
background-color: rgba(var(--color-white-rgb), 10%);
|
||||
}
|
||||
|
||||
button.catalog-install-action progress fill {
|
||||
.catalog-install-action progress fill {
|
||||
border-radius: 0;
|
||||
background-color: rgba(var(--color-accent-rgb), 80%);
|
||||
}
|
||||
|
||||
button.catalog-install-action.paused progress fill {
|
||||
.catalog-install-action.paused progress fill {
|
||||
background-color: rgba(var(--color-text-rgb), 35%);
|
||||
}
|
||||
|
||||
button.catalog-install-action.retrying {
|
||||
.catalog-install-action.retrying {
|
||||
color: var(--color-warning);
|
||||
box-shadow: rgba(var(--color-warning-rgb), 60%) 0 0 0 2dp;
|
||||
}
|
||||
|
||||
button.catalog-install-action.retrying progress fill {
|
||||
.catalog-install-action.retrying progress fill {
|
||||
background-color: rgba(var(--color-warning-rgb), 60%);
|
||||
}
|
||||
|
||||
button.catalog-install-action.failed {
|
||||
.catalog-install-action.failed {
|
||||
color: var(--color-white);
|
||||
--button-background: rgba(var(--color-error-rgb), 20%);
|
||||
--button-background-hover: rgba(var(--color-error-rgb), 35%);
|
||||
@@ -449,20 +458,20 @@ button.catalog-install-action.failed {
|
||||
box-shadow: var(--color-error) 0 0 0 2dp;
|
||||
}
|
||||
|
||||
button.catalog-install-action.failed progress fill {
|
||||
.catalog-install-action.failed progress fill {
|
||||
background-color: rgba(var(--color-error-rgb), 70%);
|
||||
}
|
||||
|
||||
button.catalog-install-action.installed {
|
||||
.catalog-install-action.installed {
|
||||
color: var(--color-success);
|
||||
box-shadow: rgba(var(--color-success-rgb), 50%) 0 0 0 2dp;
|
||||
}
|
||||
|
||||
button.catalog-install-action.installed:disabled {
|
||||
.catalog-install-action.installed:disabled {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
button.catalog-install-action.installing progress fill {
|
||||
.catalog-install-action.installing progress fill {
|
||||
background-color: rgba(var(--color-info-rgb), 80%);
|
||||
}
|
||||
|
||||
@@ -497,7 +506,7 @@ catalog-detail-identity {
|
||||
gap: var(--space-lg);
|
||||
}
|
||||
|
||||
catalog-detail-icon {
|
||||
catalog-detail-identity mod-icon {
|
||||
display: block;
|
||||
flex: 0 0 70dp;
|
||||
width: 70dp;
|
||||
@@ -508,19 +517,13 @@ catalog-detail-icon {
|
||||
box-shadow: rgba(var(--color-black-rgb), 65%) 0 8dp 20dp;
|
||||
}
|
||||
|
||||
catalog-detail-icon-image {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
catalog-detail-heading {
|
||||
catalog-detail-identity > header {
|
||||
display: block;
|
||||
flex: 1 1 auto;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
catalog-detail-category {
|
||||
catalog-detail-identity > header > b {
|
||||
display: block;
|
||||
font-family: var(--font-family-heading);
|
||||
font-size: var(--font-size-sm);
|
||||
@@ -567,7 +570,7 @@ catalog-detail-stats {
|
||||
color: rgba(var(--color-text-rgb), 58%);
|
||||
}
|
||||
|
||||
catalog-detail-stat {
|
||||
catalog-detail-stats > stat {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5dp;
|
||||
@@ -579,7 +582,7 @@ catalog-detail-stats icon {
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
catalog-stat-value {
|
||||
catalog-detail-stats > stat > b {
|
||||
color: var(--color-text);
|
||||
font-weight: bold;
|
||||
}
|
||||
@@ -701,7 +704,7 @@ catalog-gallery {
|
||||
gap: var(--space-sm);
|
||||
}
|
||||
|
||||
button.catalog-screenshot {
|
||||
.catalog-screenshot {
|
||||
flex: 1 1 0;
|
||||
height: 100%;
|
||||
min-width: 0;
|
||||
@@ -711,7 +714,7 @@ button.catalog-screenshot {
|
||||
font-size: var(--font-size-5xl);
|
||||
}
|
||||
|
||||
button.catalog-screenshot.primary {
|
||||
.catalog-screenshot.primary {
|
||||
flex: 2 1 0;
|
||||
}
|
||||
|
||||
@@ -825,7 +828,7 @@ catalog-screenshot-actions button {
|
||||
gap: 10dp;
|
||||
}
|
||||
|
||||
button.catalog-card {
|
||||
.catalog-card {
|
||||
min-width: 220dp;
|
||||
height: 254dp;
|
||||
}
|
||||
@@ -839,23 +842,23 @@ catalog-screenshot-actions button {
|
||||
padding: 18dp 10dp var(--space-sm) 10dp;
|
||||
}
|
||||
|
||||
catalog-card-icon {
|
||||
.catalog-card mod-icon {
|
||||
left: 10dp;
|
||||
bottom: -14dp;
|
||||
width: 40dp;
|
||||
height: 40dp;
|
||||
}
|
||||
|
||||
catalog-card-kicker,
|
||||
catalog-card-kicker > catalog-card-category {
|
||||
catalog-card-body > section > b,
|
||||
catalog-card-body > small {
|
||||
font-size: var(--font-size-3xs);
|
||||
}
|
||||
|
||||
catalog-card-title {
|
||||
catalog-card-body > section h2 {
|
||||
font-size: var(--font-size-md);
|
||||
}
|
||||
|
||||
catalog-card-summary {
|
||||
catalog-card-body > p {
|
||||
font-size: var(--font-size-3xs);
|
||||
}
|
||||
|
||||
@@ -865,7 +868,7 @@ catalog-screenshot-actions button {
|
||||
padding: var(--space-md) 18dp;
|
||||
}
|
||||
|
||||
catalog-detail-icon {
|
||||
catalog-detail-identity mod-icon {
|
||||
flex-basis: 56dp;
|
||||
width: 56dp;
|
||||
height: 56dp;
|
||||
@@ -875,11 +878,11 @@ catalog-screenshot-actions button {
|
||||
gap: var(--space-md);
|
||||
}
|
||||
|
||||
catalog-detail-category {
|
||||
catalog-detail-identity > header > b {
|
||||
font-size: var(--font-size-2xs);
|
||||
}
|
||||
|
||||
button.catalog-install-action {
|
||||
.catalog-install-action {
|
||||
gap: var(--space-sm);
|
||||
min-width: 112dp;
|
||||
padding: 7dp 18dp;
|
||||
@@ -887,7 +890,7 @@ catalog-screenshot-actions button {
|
||||
font-size: var(--font-size-md);
|
||||
}
|
||||
|
||||
button.catalog-install-action icon {
|
||||
.catalog-install-action icon {
|
||||
flex-basis: 18dp;
|
||||
font-size: var(--font-size-xl);
|
||||
}
|
||||
|
||||
+42
-36
@@ -15,33 +15,33 @@ window.mods content pane.mod-detail {
|
||||
gap: var(--space-md);
|
||||
}
|
||||
|
||||
mod-entry.mod-browser-entry,
|
||||
mod-entry.mod-installs-entry {
|
||||
mod-entry.browser,
|
||||
mod-entry.installs {
|
||||
min-height: 76dp;
|
||||
}
|
||||
|
||||
mod-entry icon.mod-icon.mod-browser-icon {
|
||||
mod-entry.browser mod-icon {
|
||||
color: var(--color-accent);
|
||||
decorator: text("" center center);
|
||||
}
|
||||
|
||||
mod-entry icon.mod-icon.mod-installs-icon {
|
||||
mod-entry.installs mod-icon {
|
||||
color: var(--color-accent);
|
||||
decorator: text("" center center);
|
||||
}
|
||||
|
||||
mod-entry.mod-installs-entry {
|
||||
mod-entry.installs {
|
||||
background-color: rgba(var(--color-control-rgb), 20%);
|
||||
box-shadow: rgba(var(--color-border-rgb), 25%) 0 0 0 1dp;
|
||||
}
|
||||
|
||||
mod-entry.mod-installs-entry:hover,
|
||||
mod-entry.mod-installs-entry:focus-visible {
|
||||
mod-entry.installs:hover,
|
||||
mod-entry.installs:focus-visible {
|
||||
background-color: rgba(var(--color-interactive-rgb), 12%);
|
||||
box-shadow: var(--color-accent) 0 0 0 2dp;
|
||||
}
|
||||
|
||||
mod-entry.mod-installs-entry progress {
|
||||
mod-entry.installs progress {
|
||||
height: 6dp;
|
||||
margin: var(--space-xs) 0 0 0;
|
||||
}
|
||||
@@ -60,14 +60,13 @@ mod-info-row {
|
||||
padding: var(--space-xs) 0;
|
||||
}
|
||||
|
||||
mod-info-label {
|
||||
mod-info-row > b {
|
||||
font-family: var(--font-family-heading);
|
||||
font-weight: bold;
|
||||
opacity: 0.55;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
mod-info-value {
|
||||
mod-info-row > span {
|
||||
flex: 1 1 0;
|
||||
}
|
||||
|
||||
@@ -102,21 +101,28 @@ mod-entry:selected {
|
||||
decorator: vertical-gradient(rgba(var(--color-accent-rgb), 16) rgba(var(--color-accent-rgb), 64));
|
||||
}
|
||||
|
||||
mod-entry .mod-icon {
|
||||
mod-icon {
|
||||
display: block;
|
||||
flex: 0 0 auto;
|
||||
width: 56dp;
|
||||
height: 56dp;
|
||||
border-radius: var(--radius-panel);
|
||||
}
|
||||
|
||||
mod-entry icon.mod-icon {
|
||||
font-family: var(--font-family-icons);
|
||||
font-size: 36dp;
|
||||
background-color: rgba(var(--color-control-rgb), 20%);
|
||||
color: rgba(var(--color-text-rgb), 45%);
|
||||
decorator: text("" center center);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
mod-entry mod-entry-info {
|
||||
mod-icon img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: var(--radius-panel);
|
||||
}
|
||||
|
||||
mod-info {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
flex: 1 1 0;
|
||||
@@ -124,36 +130,35 @@ mod-entry mod-entry-info {
|
||||
gap: var(--space-2xs);
|
||||
}
|
||||
|
||||
mod-entry mod-entry-name {
|
||||
mod-info header {
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
align-items: baseline;
|
||||
gap: 6dp;
|
||||
}
|
||||
|
||||
mod-entry mod-entry-name-text {
|
||||
mod-info header b {
|
||||
flex: 0 1 auto;
|
||||
min-width: 0;
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
mod-entry mod-entry-version {
|
||||
mod-info header small {
|
||||
flex: 0 0 auto;
|
||||
font-size: var(--font-size-xs);
|
||||
color: rgba(var(--color-text-rgb), 50%);
|
||||
}
|
||||
|
||||
mod-entry mod-entry-status.active {
|
||||
mod-status.active {
|
||||
color: var(--color-success);
|
||||
}
|
||||
|
||||
mod-entry mod-entry-status.failed {
|
||||
mod-status.failed {
|
||||
color: var(--color-error);
|
||||
}
|
||||
|
||||
mod-entry mod-entry-network {
|
||||
mod-network {
|
||||
margin-left: 6dp;
|
||||
padding: 1dp 5dp;
|
||||
border-radius: 5dp;
|
||||
@@ -161,7 +166,8 @@ mod-entry mod-entry-network {
|
||||
color: var(--color-info);
|
||||
}
|
||||
|
||||
mod-entry mod-entry-desc {
|
||||
mod-info > p {
|
||||
margin: 0;
|
||||
font-size: var(--font-size-sm);
|
||||
line-height: 1.3;
|
||||
color: rgba(var(--color-text-rgb), 65%);
|
||||
@@ -170,16 +176,16 @@ mod-entry mod-entry-desc {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
mod-entry mod-entry-sub {
|
||||
mod-info > small {
|
||||
font-size: var(--font-size-xs);
|
||||
color: rgba(var(--color-text-rgb), 50%);
|
||||
}
|
||||
|
||||
mod-entry.inactive .mod-icon {
|
||||
mod-entry.inactive mod-icon {
|
||||
filter: grayscale(1);
|
||||
}
|
||||
|
||||
mod-entry.inactive mod-entry-info {
|
||||
mod-entry.inactive mod-info {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
@@ -193,7 +199,7 @@ mod-header.has-banner {
|
||||
margin: -24dp -24dp 0dp -24dp;
|
||||
}
|
||||
|
||||
mod-header mod-actions {
|
||||
mod-actions {
|
||||
position: absolute;
|
||||
top: 24dp;
|
||||
left: 24dp;
|
||||
@@ -202,7 +208,7 @@ mod-header mod-actions {
|
||||
gap: var(--space-sm);
|
||||
}
|
||||
|
||||
mod-header mod-actions button {
|
||||
mod-actions button {
|
||||
font-size: var(--font-size-md);
|
||||
padding: 6dp 14dp;
|
||||
background-color: rgba(var(--color-surface-rgb), 80%);
|
||||
@@ -219,31 +225,31 @@ mod-header.no-banner mod-actions {
|
||||
position: static;
|
||||
}
|
||||
|
||||
window.mods mod-title {
|
||||
mod-title {
|
||||
display: block;
|
||||
font-size: var(--font-size-5xl);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
window.mods mod-title mod-title-version {
|
||||
mod-title small {
|
||||
font-weight: normal;
|
||||
font-size: var(--font-size-md);
|
||||
color: rgba(var(--color-text-rgb), 55%);
|
||||
}
|
||||
|
||||
window.mods mod-author {
|
||||
mod-author {
|
||||
display: block;
|
||||
font-size: var(--font-size-base);
|
||||
color: rgba(var(--color-text-rgb), 55%);
|
||||
}
|
||||
|
||||
window.mods mod-restart-note {
|
||||
mod-restart-note {
|
||||
font-size: var(--font-size-base);
|
||||
color: var(--color-warning);
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
window.mods mod-description {
|
||||
mod-description {
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
@@ -253,13 +259,13 @@ status-badge {
|
||||
}
|
||||
|
||||
status-badge.active,
|
||||
mod-info-label.active {
|
||||
mod-info-row > b.active {
|
||||
color: var(--color-success);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
status-badge.failed,
|
||||
mod-info-label.failed {
|
||||
mod-info-row > b.failed {
|
||||
color: var(--color-error);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@@ -105,6 +105,50 @@ toast message row.muted {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
toast.mod-installed row {
|
||||
align-items: center;
|
||||
gap: var(--space-md);
|
||||
}
|
||||
|
||||
mod-icon {
|
||||
flex: 0 0 42dp;
|
||||
width: 42dp;
|
||||
height: 42dp;
|
||||
overflow: hidden;
|
||||
border-radius: var(--radius-panel);
|
||||
background-color: rgba(var(--color-control-rgb), 45%);
|
||||
color: rgba(var(--color-text-rgb), 45%);
|
||||
font-family: var(--font-family-icons);
|
||||
font-size: var(--font-size-4xl);
|
||||
decorator: text("" center center);
|
||||
}
|
||||
|
||||
mod-icon img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: var(--radius-panel);
|
||||
}
|
||||
|
||||
mod-info {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
min-width: 0;
|
||||
gap: var(--space-xs);
|
||||
}
|
||||
|
||||
mod-name {
|
||||
color: var(--color-white);
|
||||
}
|
||||
|
||||
toast.mod-installed small {
|
||||
font-size: var(--font-size-sm);
|
||||
color: rgba(var(--color-text-rgb), 55%);
|
||||
}
|
||||
|
||||
toast.mod-installed small.version {
|
||||
margin-left: var(--space-sm);
|
||||
}
|
||||
|
||||
progress {
|
||||
height: 4dp;
|
||||
position: absolute;
|
||||
@@ -239,6 +283,13 @@ icon.warning {
|
||||
decorator: text("" center center);
|
||||
}
|
||||
|
||||
icon.download-done {
|
||||
width: 1.2em;
|
||||
height: 1.2em;
|
||||
font-size: 1.2em;
|
||||
decorator: text("" center center);
|
||||
}
|
||||
|
||||
fps {
|
||||
display: none;
|
||||
z-index: 99;
|
||||
|
||||
+42
-29
@@ -668,7 +668,11 @@ package-row {
|
||||
border-bottom-color: rgba(var(--color-border-rgb), 30%);
|
||||
}
|
||||
|
||||
package-row-icon {
|
||||
package-row:last-child {
|
||||
border-bottom-width: 0;
|
||||
}
|
||||
|
||||
package-row > mod-icon {
|
||||
display: none;
|
||||
flex: 0 0 36dp;
|
||||
width: 36dp;
|
||||
@@ -683,46 +687,51 @@ package-row-icon {
|
||||
decorator: text("" center center);
|
||||
}
|
||||
|
||||
package-row-icon.visible {
|
||||
package-row > mod-icon.visible {
|
||||
display: block;
|
||||
}
|
||||
|
||||
package-row-icon.has-image {
|
||||
package-row > mod-icon.has-image {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
package-row.paused package-row-icon,
|
||||
package-row.retrying package-row-icon,
|
||||
package-row.failed package-row-icon {
|
||||
package-row.paused > mod-icon,
|
||||
package-row.retrying > mod-icon,
|
||||
package-row.failed > mod-icon {
|
||||
filter: grayscale(1);
|
||||
opacity: 0.55;
|
||||
}
|
||||
|
||||
package-row-info {
|
||||
package-row > section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1 1 0;
|
||||
min-width: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
package-row-heading {
|
||||
package-row > section > header {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
gap: var(--space-sm);
|
||||
align-items: center;
|
||||
padding-bottom: 6dp;
|
||||
margin: 0;
|
||||
padding: 0 0 6dp 0;
|
||||
}
|
||||
|
||||
package-row-identity {
|
||||
package-row h3 {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
flex: 1 1 auto;
|
||||
min-width: 0;
|
||||
gap: var(--space-xs);
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
package-row-name {
|
||||
package-row h3 > span {
|
||||
display: block;
|
||||
flex: 0 1 auto;
|
||||
min-width: 0;
|
||||
@@ -733,7 +742,7 @@ package-row-name {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
package-row-version {
|
||||
package-row h3 > small {
|
||||
display: block;
|
||||
flex: 0 0 auto;
|
||||
font-size: var(--font-size-xs);
|
||||
@@ -741,28 +750,28 @@ package-row-version {
|
||||
color: rgba(var(--color-text-rgb), 50%);
|
||||
}
|
||||
|
||||
package-row-state {
|
||||
package-row header > small {
|
||||
display: block;
|
||||
flex: 0 0 auto;
|
||||
font-size: var(--font-size-sm);
|
||||
color: rgba(var(--color-text-rgb), 60%);
|
||||
}
|
||||
|
||||
package-row.downloading package-row-state {
|
||||
package-row.downloading header > small {
|
||||
color: var(--color-accent);
|
||||
}
|
||||
|
||||
package-row.retrying package-row-state {
|
||||
package-row.retrying header > small {
|
||||
color: var(--color-warning);
|
||||
}
|
||||
|
||||
package-row.failed package-row-state,
|
||||
package-row.failed package-row-detail {
|
||||
package-row.failed header > small,
|
||||
package-row.failed footer > small {
|
||||
color: var(--color-error);
|
||||
}
|
||||
|
||||
package-row.installed package-row-state,
|
||||
package-row.installed package-row-detail {
|
||||
package-row.installed header > small,
|
||||
package-row.installed footer > small {
|
||||
color: var(--color-success);
|
||||
}
|
||||
|
||||
@@ -789,15 +798,17 @@ package-row.installed progress fill {
|
||||
background-color: var(--color-success);
|
||||
}
|
||||
|
||||
package-row-footer {
|
||||
package-row footer {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
gap: var(--space-sm);
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
package-row-detail {
|
||||
package-row footer > small {
|
||||
display: block;
|
||||
flex: 1 1 0;
|
||||
min-width: 0;
|
||||
@@ -809,13 +820,15 @@ package-row-detail {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
package-row-actions {
|
||||
package-row nav {
|
||||
display: flex;
|
||||
flex: 0 0 auto;
|
||||
gap: 6dp;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
package-row-actions button.package-row-icon-action {
|
||||
package-row nav > button.icon-action {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex: 0 0 30dp;
|
||||
@@ -827,7 +840,7 @@ package-row-actions button.package-row-icon-action {
|
||||
font-size: var(--font-size-md);
|
||||
}
|
||||
|
||||
package-row-actions button.package-row-icon-action icon {
|
||||
package-row nav > button.icon-action icon {
|
||||
flex: 0 0 var(--font-size-md);
|
||||
width: var(--font-size-md);
|
||||
height: var(--font-size-md);
|
||||
@@ -839,25 +852,25 @@ package-row.installed {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
package-row.installed package-row-icon {
|
||||
package-row.installed > mod-icon {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
package-row.installed package-row-info {
|
||||
package-row.installed > section {
|
||||
padding-right: 38dp;
|
||||
}
|
||||
|
||||
package-row.installed package-row-state {
|
||||
package-row.installed header > small {
|
||||
display: none;
|
||||
}
|
||||
|
||||
package-row.installed package-row-actions {
|
||||
package-row.installed nav {
|
||||
position: absolute;
|
||||
top: 17dp;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
package-row.installed package-row-actions button.package-row-icon-action {
|
||||
package-row.installed nav > button.icon-action {
|
||||
opacity: 0.45;
|
||||
}
|
||||
|
||||
|
||||
@@ -354,7 +354,7 @@ private:
|
||||
// bundle from disk, then reactivates whatever the current cvar/provider state allows.
|
||||
void apply_lifecycle_change(LoadedMod& target, bool reload);
|
||||
// `target` plus transitive active/suspended dependents, in m_mods (init) order.
|
||||
std::vector<LoadedMod*> collect_lifecycle_set(LoadedMod& target);
|
||||
std::vector<LoadedMod*> collect_lifecycle_set(LoadedMod& target) const;
|
||||
void resume_lifecycle_set(const std::vector<LoadedMod*>& mods);
|
||||
bool reload_bundle(LoadedMod& mod);
|
||||
bool ensure_native_loaded(LoadedMod& mod);
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "dusk/mods/svc/config.hpp"
|
||||
#include "dusk/mods/svc/hook.hpp"
|
||||
#include "dusk/mods/svc/registry.hpp"
|
||||
#include "dusk/ui/mod_texture_provider.hpp"
|
||||
#include "dusk/ui/mods_window.hpp"
|
||||
#include "dusk/ui/ui.hpp"
|
||||
#include "miniz.h"
|
||||
@@ -1271,7 +1272,7 @@ void ModLoader::flush_toasts() {
|
||||
ui::push_toast(std::move(toast));
|
||||
}
|
||||
|
||||
std::vector<LoadedMod*> ModLoader::collect_lifecycle_set(LoadedMod& target) {
|
||||
std::vector<LoadedMod*> ModLoader::collect_lifecycle_set(LoadedMod& target) const {
|
||||
std::vector included{&target};
|
||||
std::vector pending{&target};
|
||||
while (!pending.empty()) {
|
||||
@@ -1730,10 +1731,19 @@ void ModLoader::apply_pending_requests() {
|
||||
if (const auto* install = std::get_if<InstallRequest>(&request)) {
|
||||
auto result = install_staged(install->stagedPath);
|
||||
if (result.success && result.mod != nullptr) {
|
||||
const auto& metadata = result.mod->metadata;
|
||||
const std::string iconRml =
|
||||
metadata.iconPath.empty() ?
|
||||
std::string{} :
|
||||
fmt::format(R"(<img src="{}" />)",
|
||||
ui::escape(ui::mod_image_source(*result.mod, metadata.iconPath)));
|
||||
ui::push_toast({
|
||||
.type = "mod-installed",
|
||||
.title = "Mod installed",
|
||||
.content = fmt::format(
|
||||
"{} {}", result.mod->metadata.name, result.mod->metadata.version),
|
||||
R"(<row><mod-icon>{}</mod-icon><mod-info><mod-name><b>{}</b><small class="version">v{}</small></mod-name><small>{}</small></mod-info></row>)",
|
||||
iconRml, ui::escape(metadata.name), ui::escape(metadata.version),
|
||||
ui::escape(metadata.author)),
|
||||
.duration = std::chrono::seconds{4},
|
||||
});
|
||||
} else if (!result.success && result.mod == nullptr) {
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <charconv>
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
@@ -14,6 +16,68 @@ struct ByteFormat {
|
||||
int mibFractionDigits = 1;
|
||||
};
|
||||
|
||||
inline std::string format_count(uint64_t value) {
|
||||
if (value >= 1'000'000) {
|
||||
return fmt::format("{:.1f}m", static_cast<double>(value) / 1'000'000.0);
|
||||
}
|
||||
if (value >= 1'000) {
|
||||
return fmt::format("{:.1f}k", static_cast<double>(value) / 1'000.0);
|
||||
}
|
||||
return fmt::format("{}", value);
|
||||
}
|
||||
|
||||
inline std::string display_date(std::string_view timestamp) {
|
||||
return std::string{timestamp.substr(0, 10)};
|
||||
}
|
||||
|
||||
inline std::string relative_date(std::string_view timestamp) {
|
||||
if (timestamp.size() < 10 || timestamp[4] != '-' || timestamp[7] != '-') {
|
||||
return display_date(timestamp);
|
||||
}
|
||||
int yearValue = 0;
|
||||
unsigned monthValue = 0;
|
||||
unsigned dayValue = 0;
|
||||
const auto parse = [](std::string_view text, auto& value) {
|
||||
const auto [end, error] = std::from_chars(text.data(), text.data() + text.size(), value);
|
||||
return error == std::errc{} && end == text.data() + text.size();
|
||||
};
|
||||
if (!parse(timestamp.substr(0, 4), yearValue) || !parse(timestamp.substr(5, 2), monthValue) ||
|
||||
!parse(timestamp.substr(8, 2), dayValue))
|
||||
{
|
||||
return display_date(timestamp);
|
||||
}
|
||||
|
||||
const std::chrono::year_month_day date{
|
||||
std::chrono::year{yearValue}, std::chrono::month{monthValue}, std::chrono::day{dayValue}};
|
||||
if (!date.ok()) {
|
||||
return display_date(timestamp);
|
||||
}
|
||||
const auto today = std::chrono::floor<std::chrono::days>(std::chrono::system_clock::now());
|
||||
const auto age = (today - std::chrono::sys_days{date}).count();
|
||||
if (age < 0) {
|
||||
return display_date(timestamp);
|
||||
}
|
||||
if (age == 0) {
|
||||
return "today";
|
||||
}
|
||||
if (age == 1) {
|
||||
return "yesterday";
|
||||
}
|
||||
if (age < 7) {
|
||||
return fmt::format("{} days ago", age);
|
||||
}
|
||||
if (age < 30) {
|
||||
const auto weekCount = age / 7;
|
||||
return fmt::format("{} week{} ago", weekCount, weekCount == 1 ? "" : "s");
|
||||
}
|
||||
if (age < 365) {
|
||||
const auto monthCount = age / 30;
|
||||
return fmt::format("{} month{} ago", monthCount, monthCount == 1 ? "" : "s");
|
||||
}
|
||||
const auto yearCount = age / 365;
|
||||
return fmt::format("{} year{} ago", yearCount, yearCount == 1 ? "" : "s");
|
||||
}
|
||||
|
||||
inline std::string format_bytes(uint64_t bytes, ByteFormat options = {}) {
|
||||
constexpr double kiB = 1024.0;
|
||||
constexpr double miB = kiB * 1024.0;
|
||||
|
||||
+28
-94
@@ -18,7 +18,6 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <charconv>
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <exception>
|
||||
@@ -47,65 +46,6 @@ std::string_view sort_label(mods::catalog::Sort sort) noexcept {
|
||||
return iter != sortOptions.end() ? iter->label : sortOptions.front().label;
|
||||
}
|
||||
|
||||
std::string format_count(uint64_t value) {
|
||||
if (value >= 1'000'000) {
|
||||
return fmt::format("{:.1f}m", static_cast<double>(value) / 1'000'000.0);
|
||||
}
|
||||
if (value >= 1'000) {
|
||||
return fmt::format("{:.1f}k", static_cast<double>(value) / 1'000.0);
|
||||
}
|
||||
return fmt::format("{}", value);
|
||||
}
|
||||
|
||||
std::string display_date(std::string_view timestamp) {
|
||||
return std::string{timestamp.substr(0, std::min<size_t>(timestamp.size(), 10))};
|
||||
}
|
||||
|
||||
std::string relative_date(std::string_view timestamp) {
|
||||
if (timestamp.size() < 10) {
|
||||
return display_date(timestamp);
|
||||
}
|
||||
int yearValue = 0;
|
||||
unsigned monthValue = 0;
|
||||
unsigned dayValue = 0;
|
||||
const auto parse = [](std::string_view text, auto& value) {
|
||||
const auto [end, error] = std::from_chars(text.data(), text.data() + text.size(), value);
|
||||
return error == std::errc{} && end == text.data() + text.size();
|
||||
};
|
||||
if (!parse(timestamp.substr(0, 4), yearValue) || !parse(timestamp.substr(5, 2), monthValue) ||
|
||||
!parse(timestamp.substr(8, 2), dayValue))
|
||||
{
|
||||
return display_date(timestamp);
|
||||
}
|
||||
|
||||
const std::chrono::year_month_day date{
|
||||
std::chrono::year{yearValue}, std::chrono::month{monthValue}, std::chrono::day{dayValue}};
|
||||
if (!date.ok()) {
|
||||
return display_date(timestamp);
|
||||
}
|
||||
const auto today = std::chrono::floor<std::chrono::days>(std::chrono::system_clock::now());
|
||||
const auto age = (today - std::chrono::sys_days{date}).count();
|
||||
if (age < 0) {
|
||||
return display_date(timestamp);
|
||||
}
|
||||
if (age == 0) {
|
||||
return "today";
|
||||
}
|
||||
if (age == 1) {
|
||||
return "yesterday";
|
||||
}
|
||||
if (age < 7) {
|
||||
return fmt::format("{} days ago", age);
|
||||
}
|
||||
if (age < 30) {
|
||||
return fmt::format("{} weeks ago", age / 7);
|
||||
}
|
||||
if (age < 365) {
|
||||
return fmt::format("{} months ago", age / 30);
|
||||
}
|
||||
return fmt::format("{} years ago", age / 365);
|
||||
}
|
||||
|
||||
void add_list_markers(Rml::Element* fragment) {
|
||||
Rml::ElementList lists;
|
||||
fragment->QuerySelectorAll(lists, "ul, ol");
|
||||
@@ -179,12 +119,8 @@ std::string_view activation_failure(const mods::LoadedMod& mod) {
|
||||
return mod.suspendedByProvider ? "A required provider is unavailable" : "Activation failed";
|
||||
}
|
||||
|
||||
bool safe_web_url(std::string_view url) {
|
||||
return url.starts_with("https://") || url.starts_with("http://");
|
||||
}
|
||||
|
||||
void open_web_url(const std::string& url) {
|
||||
if (safe_web_url(url)) {
|
||||
if (url.starts_with("https://")) {
|
||||
SDL_OpenURL(url.c_str());
|
||||
}
|
||||
}
|
||||
@@ -192,21 +128,21 @@ void open_web_url(const std::string& url) {
|
||||
void set_icon_button_content(Button& button, const Rml::String& glyph, const Rml::String& label) {
|
||||
clear_children(button.root());
|
||||
append_text(append(button.root(), "icon"), glyph);
|
||||
append_text_element(button.root(), "catalog-action-label", label);
|
||||
append_text_element(button.root(), "span", label);
|
||||
}
|
||||
|
||||
void append_status(Rml::Element* parent, const Rml::String& title, const Rml::String& message) {
|
||||
append_text_element(parent, "catalog-status-title", title);
|
||||
append_text_element(parent, "catalog-status-message", message);
|
||||
append_text_element(parent, "h2", title);
|
||||
append_text_element(parent, "p", message);
|
||||
}
|
||||
|
||||
void append_stat(Rml::Element* parent, const Rml::String& glyph, const Rml::String& value,
|
||||
const Rml::String& suffix) {
|
||||
auto* stat = append(parent, "catalog-detail-stat");
|
||||
auto* stat = append(parent, "stat");
|
||||
if (!glyph.empty()) {
|
||||
append_text(append(stat, "icon"), glyph);
|
||||
}
|
||||
append_text_element(stat, "catalog-stat-value", value);
|
||||
append_text_element(stat, "b", value);
|
||||
append_text(stat, suffix);
|
||||
}
|
||||
|
||||
@@ -228,27 +164,25 @@ public:
|
||||
|
||||
auto* art = append(mRoot, "catalog-card-art");
|
||||
append(art, "catalog-card-art-shadow");
|
||||
auto* icon = append(art, "catalog-card-icon");
|
||||
auto* iconImage = append(icon, "catalog-card-icon-image");
|
||||
auto* icon = append(art, "mod-icon");
|
||||
auto* iconImage = append(icon, "mod-icon-image");
|
||||
|
||||
auto* body = append(mRoot, "catalog-card-body");
|
||||
auto* kicker = append(body, "catalog-card-kicker");
|
||||
append_text_element(kicker, "catalog-card-category", category);
|
||||
append_text_element(kicker, "catalog-card-updated", relative_date(mod.updatedAt));
|
||||
|
||||
auto* identity = append(body, "catalog-card-identity");
|
||||
append_text_element(identity, "catalog-card-title", mod.name);
|
||||
append_text_element(identity, "catalog-card-author", fmt::format("by {}", mod.author.name));
|
||||
|
||||
append_text_element(body, "catalog-card-summary", snippet(mod.summary, 126));
|
||||
auto* meta = append(body, "catalog-card-meta");
|
||||
auto* downloads = append(meta, "catalog-card-stat");
|
||||
append_text_element(body, "small", relative_date(mod.updatedAt));
|
||||
auto* identity = append(body, "section");
|
||||
append_text_element(identity, "b", category);
|
||||
append_text_element(identity, "h2", mod.name);
|
||||
append_text_element(identity, "small", fmt::format("by {}", mod.author.name));
|
||||
append_text_element(body, "p", snippet(mod.summary, 126));
|
||||
auto* meta = append(body, "footer");
|
||||
auto* downloads = append(meta, "stat");
|
||||
append_text(append(downloads, "icon"), "\uF090");
|
||||
append_text(downloads, format_count(mod.downloads));
|
||||
auto* endorsements = append(meta, "catalog-card-stat");
|
||||
auto* endorsements = append(meta, "stat");
|
||||
append_text(append(endorsements, "icon"), "\uE87D");
|
||||
append_text(endorsements, format_count(mod.endorsements));
|
||||
auto* size = append_text_element(meta, "catalog-card-size", installedLabel);
|
||||
auto* size = append_text_element(meta, "small", installedLabel);
|
||||
size->SetClass("size", true);
|
||||
if (isInstalled) {
|
||||
size->SetClass("installed", true);
|
||||
}
|
||||
@@ -566,7 +500,7 @@ public:
|
||||
if (mLabel != label || mGlyph != glyph) {
|
||||
ui::clear_children(mRoot);
|
||||
append_text(append(mRoot, "icon"), glyph);
|
||||
append_text_element(mRoot, "catalog-action-label", label);
|
||||
append_text_element(mRoot, "span", label);
|
||||
mProgress = append(mRoot, "progress");
|
||||
mLabel = std::move(label);
|
||||
mGlyph = std::move(glyph);
|
||||
@@ -664,11 +598,11 @@ DetailContent::DetailContent(
|
||||
open.on_pressed([url = detail.siteUrl] { open_web_url(url); });
|
||||
|
||||
auto* identity = append(hero, "catalog-detail-identity");
|
||||
auto* detailIcon = append(identity, "catalog-detail-icon");
|
||||
auto* detailIconImage = append(detailIcon, "catalog-detail-icon-image");
|
||||
auto* detailHeading = append(identity, "catalog-detail-heading");
|
||||
append_text_element(detailHeading, "catalog-detail-category",
|
||||
detail.mod.category ? detail.mod.category->name : "Uncategorized");
|
||||
auto* detailIcon = append(identity, "mod-icon");
|
||||
auto* detailIconImage = append(detailIcon, "mod-icon-image");
|
||||
auto* detailHeading = append(identity, "header");
|
||||
append_text_element(
|
||||
detailHeading, "b", detail.mod.category ? detail.mod.category->name : "Uncategorized");
|
||||
auto* title = append(detailHeading, "h1");
|
||||
append_text(title, detail.mod.name);
|
||||
append_text_element(title, "small", fmt::format("v{}", detail.mod.version));
|
||||
@@ -803,7 +737,7 @@ DetailContent::DetailContent(
|
||||
append_detail_field(detailList, "License", detail.license.value_or("Not specified"));
|
||||
append_detail_field(
|
||||
detailList, "Mod ABI", detail.modAbi ? fmt::format("{}", *detail.modAbi) : "Assets only");
|
||||
if (detail.sourceUrl && safe_web_url(*detail.sourceUrl)) {
|
||||
if (detail.sourceUrl && detail.sourceUrl->starts_with("https://")) {
|
||||
auto* sourceActions = append(sidebar, "catalog-source-actions");
|
||||
auto& sourceGroup =
|
||||
add_existing_item<NavGroup>(sourceActions, Props{
|
||||
@@ -908,8 +842,8 @@ void ModBrowser::build_content(Rml::Element* content) {
|
||||
}();
|
||||
const uint64_t total = mPage ? mPage->pagination.total : 0;
|
||||
append_text_element(heading, "h1", categoryName);
|
||||
append_text_element(heading, "catalog-results-summary",
|
||||
fmt::format("{} mods · sorted by {}", total, sort_label(mQuery.sort)));
|
||||
append_text_element(
|
||||
heading, "small", fmt::format("{} mods · sorted by {}", total, sort_label(mQuery.sort)));
|
||||
|
||||
Component* resultFocus = nullptr;
|
||||
Component* retryFocus = nullptr;
|
||||
|
||||
+29
-38
@@ -59,32 +59,28 @@ class ModListEntry : public FluentComponent<ModListEntry> {
|
||||
public:
|
||||
ModListEntry(Rml::Element* parent, const mods::LoadedMod& mod)
|
||||
: FluentComponent{append(parent, "mod-entry")} {
|
||||
Rml::Element* icon = nullptr;
|
||||
auto* icon = append(mRoot, "mod-icon");
|
||||
if (!mod.metadata.iconPath.empty()) {
|
||||
icon = append(mRoot, "img");
|
||||
icon->SetAttribute("src", mod_image_source(mod, mod.metadata.iconPath));
|
||||
} else {
|
||||
icon = append(mRoot, "icon");
|
||||
icon->SetClass("placeholder", true);
|
||||
auto* image = append(icon, "img");
|
||||
image->SetAttribute("src", mod_image_source(mod, mod.metadata.iconPath));
|
||||
}
|
||||
icon->SetClass("mod-icon", true);
|
||||
|
||||
const auto status = mod_status(mod);
|
||||
auto* info = append(mRoot, "mod-entry-info");
|
||||
auto* name = append(info, "mod-entry-name");
|
||||
append_text(append(name, "mod-entry-name-text"), mod.metadata.name);
|
||||
append_text(append(name, "mod-entry-version"), fmt::format("v{}", mod.metadata.version));
|
||||
auto* sub = append(info, "mod-entry-sub");
|
||||
auto* info = append(mRoot, "mod-info");
|
||||
auto* heading = append(info, "header");
|
||||
append_text(append(heading, "b"), mod.metadata.name);
|
||||
append_text(append(heading, "small"), fmt::format("v{}", mod.metadata.version));
|
||||
auto* sub = append(info, "small");
|
||||
append_text(sub, fmt::format("{} - ", mod.metadata.author));
|
||||
auto* statusElement = append(sub, "mod-entry-status");
|
||||
auto* statusElement = append(sub, "mod-status");
|
||||
if (status.badgeClass[0] != '\0') {
|
||||
statusElement->SetClass(status.badgeClass, true);
|
||||
}
|
||||
append_text(statusElement, status.text);
|
||||
if (mod_uses_network(mod)) {
|
||||
append_text(append(sub, "mod-entry-network"), "Network");
|
||||
append_text(append(sub, "mod-network"), "Network");
|
||||
}
|
||||
append_text(append(info, "mod-entry-desc"), snippet(mod.metadata.description, 90));
|
||||
append_text(append(info, "p"), snippet(mod.metadata.description, 90));
|
||||
mRoot->SetClass("inactive", !mod.active);
|
||||
mRoot->SetClass("failed", mod.loadFailed);
|
||||
|
||||
@@ -102,16 +98,13 @@ class BrowseModsEntry final : public FluentComponent<BrowseModsEntry> {
|
||||
public:
|
||||
BrowseModsEntry(Rml::Element* parent, std::function<void()> onOpen)
|
||||
: FluentComponent{append(parent, "mod-entry")} {
|
||||
mRoot->SetClass("mod-browser-entry", true);
|
||||
auto* icon = append(mRoot, "icon");
|
||||
icon->SetClass("mod-icon", true);
|
||||
icon->SetClass("mod-browser-icon", true);
|
||||
mRoot->SetClass("browser", true);
|
||||
append(mRoot, "mod-icon");
|
||||
|
||||
auto* info = append(mRoot, "mod-entry-info");
|
||||
auto* name = append(info, "mod-entry-name");
|
||||
append_text(append(name, "mod-entry-name-text"), "Browse online mods");
|
||||
append_text(
|
||||
append(info, "mod-entry-desc"), "Discover and install mods from the community.");
|
||||
auto* info = append(mRoot, "mod-info");
|
||||
auto* heading = append(info, "header");
|
||||
append_text(append(heading, "b"), "Browse online mods");
|
||||
append_text(append(info, "p"), "Discover and install mods from the community.");
|
||||
|
||||
on_nav_command([callback = std::move(onOpen)](Rml::Event&, NavCommand cmd) {
|
||||
if (cmd != NavCommand::Confirm) {
|
||||
@@ -127,15 +120,13 @@ class InstallQueueEntry final : public FluentComponent<InstallQueueEntry> {
|
||||
public:
|
||||
InstallQueueEntry(Rml::Element* parent, std::function<void()> onOpen)
|
||||
: FluentComponent{append(parent, "mod-entry")} {
|
||||
mRoot->SetClass("mod-installs-entry", true);
|
||||
auto* icon = append(mRoot, "icon");
|
||||
icon->SetClass("mod-icon", true);
|
||||
icon->SetClass("mod-installs-icon", true);
|
||||
mRoot->SetClass("installs", true);
|
||||
append(mRoot, "mod-icon");
|
||||
|
||||
auto* info = append(mRoot, "mod-entry-info");
|
||||
auto* name = append(info, "mod-entry-name");
|
||||
append_text(append(name, "mod-entry-name-text"), "Installs");
|
||||
mSummary = append(info, "mod-entry-sub");
|
||||
auto* info = append(mRoot, "mod-info");
|
||||
auto* heading = append(info, "header");
|
||||
append_text(append(heading, "b"), "Installs");
|
||||
mSummary = append(info, "small");
|
||||
mProgress = append(info, "progress");
|
||||
|
||||
on_nav_command([callback = std::move(onOpen)](Rml::Event&, NavCommand cmd) {
|
||||
@@ -183,7 +174,7 @@ public:
|
||||
const bool hasBanner = !mod.metadata.bannerPath.empty();
|
||||
mRoot->SetClass(hasBanner ? "has-banner" : "no-banner", true);
|
||||
if (hasBanner) {
|
||||
mRoot->SetProperty("decorator", fmt::format(R"(image("{}" cover center top))",
|
||||
mRoot->SetProperty("decorator", fmt::format(R"(image("{}" cover center center))",
|
||||
mod_image_source(mod, mod.metadata.bannerPath)));
|
||||
}
|
||||
|
||||
@@ -358,7 +349,7 @@ void ModsWindow::build_detail(Pane& pane, mods::LoadedMod& mod) {
|
||||
|
||||
auto* title = append(pane.root(), "mod-title");
|
||||
append_text(title, fmt::format("{} ", mod.metadata.name));
|
||||
append_text(append(title, "mod-title-version"), fmt::format("v{}", mod.metadata.version));
|
||||
append_text(append(title, "small"), fmt::format("v{}", mod.metadata.version));
|
||||
if (mod.loadFailed || mod.suspendedByProvider) {
|
||||
const auto status = mod_status(mod);
|
||||
append_text(title, "\u00a0");
|
||||
@@ -376,10 +367,10 @@ void ModsWindow::build_detail(Pane& pane, mods::LoadedMod& mod) {
|
||||
|
||||
if (mod.loadFailed && !mod.failureReason.empty()) {
|
||||
auto* row = append(pane.root(), "mod-info-row");
|
||||
auto* label = append(row, "mod-info-label");
|
||||
auto* label = append(row, "b");
|
||||
label->SetClass("failed", true);
|
||||
append_text(label, "Reason");
|
||||
append_text(append(row, "mod-info-value"), mod.failureReason);
|
||||
append_text(append(row, "span"), mod.failureReason);
|
||||
} else if (mod.suspendedByProvider) {
|
||||
std::vector<std::string_view> providers;
|
||||
for (const auto& edge : mod.dependencies) {
|
||||
@@ -388,8 +379,8 @@ void ModsWindow::build_detail(Pane& pane, mods::LoadedMod& mod) {
|
||||
}
|
||||
}
|
||||
auto* row = append(pane.root(), "mod-info-row");
|
||||
append_text(append(row, "mod-info-label"), "Waiting on");
|
||||
append_text(append(row, "mod-info-value"), fmt::format("{}", fmt::join(providers, ", ")));
|
||||
append_text(append(row, "b"), "Waiting on");
|
||||
append_text(append(row, "span"), fmt::format("{}", fmt::join(providers, ", ")));
|
||||
}
|
||||
|
||||
std::vector<std::string_view> activeDependents;
|
||||
|
||||
@@ -95,6 +95,9 @@ Rml::Element* create_toast(Rml::Element* parent, const Toast& toast) {
|
||||
} else if (toast.type == "warning") {
|
||||
auto* icon = append(heading, "icon");
|
||||
icon->SetClass("warning", true);
|
||||
} else if (toast.type == "mod-installed") {
|
||||
auto* icon = append(heading, "icon");
|
||||
icon->SetClass("download-done", true);
|
||||
}
|
||||
}
|
||||
{
|
||||
|
||||
+12
-12
@@ -67,23 +67,23 @@ std::string state_label(const mods::queue::Item& item) {
|
||||
}
|
||||
|
||||
PackageRow::PackageRow(Rml::Element* parent) : Component{create_row(parent)} {
|
||||
mIcon = append(mRoot, "package-row-icon");
|
||||
auto* info = append(mRoot, "package-row-info");
|
||||
auto* heading = append(info, "package-row-heading");
|
||||
auto* identity = append(heading, "package-row-identity");
|
||||
mName = append(identity, "package-row-name");
|
||||
mVersion = append(identity, "package-row-version");
|
||||
mState = append(heading, "package-row-state");
|
||||
mIcon = append(mRoot, "mod-icon");
|
||||
auto* info = append(mRoot, "section");
|
||||
auto* heading = append(info, "header");
|
||||
auto* identity = append(heading, "h3");
|
||||
mName = append(identity, "span");
|
||||
mVersion = append(identity, "small");
|
||||
mState = append(heading, "small");
|
||||
mProgress = append(info, "progress");
|
||||
mFooter = append(info, "package-row-footer");
|
||||
mDetail = append(mFooter, "package-row-detail");
|
||||
mFooter = append(info, "footer");
|
||||
mDetail = append(mFooter, "small");
|
||||
}
|
||||
|
||||
void PackageRow::set_package(std::string name, std::string version, std::string status,
|
||||
std::string detail, std::string stateClass, std::optional<float> progress) {
|
||||
mRoot->SetClassNames(fmt::format("package-row {}", stateClass));
|
||||
mRoot->SetClassNames(stateClass);
|
||||
set_text_content(mName, name);
|
||||
set_text_content(mVersion, version);
|
||||
set_text_content(mVersion, fmt::format("v{}", version));
|
||||
mVersion->SetProperty("display", version.empty() ? "none" : "block");
|
||||
set_text_content(mState, status);
|
||||
set_text_content(mDetail, detail);
|
||||
@@ -113,7 +113,7 @@ void PackageRow::set_icon(std::string source) {
|
||||
|
||||
Rml::Element* PackageRow::actions_root() {
|
||||
if (mActions == nullptr) {
|
||||
mActions = append(mFooter, "package-row-actions");
|
||||
mActions = append(mFooter, "nav");
|
||||
}
|
||||
return mActions;
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ public:
|
||||
auto* actions = actions_root();
|
||||
auto pause = std::make_unique<Button>(actions, "");
|
||||
mPause = pause.get();
|
||||
mPause->root()->SetClass("package-row-icon-action", true);
|
||||
mPause->root()->SetClass("icon-action", true);
|
||||
mPause->on_pressed([this] {
|
||||
const auto item = mods::queue::find(mId);
|
||||
if (!item) {
|
||||
@@ -63,7 +63,7 @@ public:
|
||||
|
||||
auto cancel = std::make_unique<Button>(actions, "");
|
||||
mCancel = cancel.get();
|
||||
mCancel->root()->SetClass("package-row-icon-action", true);
|
||||
mCancel->root()->SetClass("icon-action", true);
|
||||
mCancel->on_pressed([this] {
|
||||
const auto item = mods::queue::find(mId);
|
||||
if (!item) {
|
||||
@@ -196,6 +196,10 @@ QueueWindow::QueueWindow(std::string focusId)
|
||||
}
|
||||
|
||||
void QueueWindow::update() {
|
||||
if (!mItemIds.empty() && mods::queue::item_count() == 0) {
|
||||
pop();
|
||||
return;
|
||||
}
|
||||
refresh_queue();
|
||||
Modal::update();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user