theme: add fixed corner radius mode and rescale radius strength

This commit is contained in:
bbedward committed 2026-09-19 17:07:37 -04:00
1 parent aea35afdc7
commit a5087735b5
16 files changed
+150 -48

No files matched your search

+3 -1
View File
@@ -185,7 +185,9 @@ Singleton {
property string controlCenterTileColorMode: Spec.SPEC.controlCenterTileColorMode.def
property string buttonColorMode: Spec.SPEC.buttonColorMode.def
property int radiusStrength: Spec.SPEC.radiusStrength.def
readonly property real cornerRadius: Shape.radius("m", Shape.scaleForStrength(radiusStrength))
property string radiusMode: Spec.SPEC.radiusMode.def
property int fixedRadius: Spec.SPEC.fixedRadius.def
readonly property real cornerRadius: Shape.radius("m", Shape.scaleForStrength(radiusStrength), radiusMode === "fixed" ? fixedRadius : -1)
property int niriLayoutGapsOverride: Spec.SPEC.niriLayoutGapsOverride.def
property int niriLayoutRadiusOverride: Spec.SPEC.niriLayoutRadiusOverride.def
property int niriLayoutBorderSize: Spec.SPEC.niriLayoutBorderSize.def
+15 -14
View File
@@ -1142,18 +1142,19 @@ Singleton {
}
readonly property real radiusStrength: typeof SettingsData !== "undefined" ? SettingsData.radiusStrength : 50
readonly property real shapeScale: Shape.scaleForStrength(radiusStrength)
readonly property real fixedRadius: typeof SettingsData !== "undefined" && SettingsData.radiusMode === "fixed" ? SettingsData.fixedRadius : -1
readonly property real shapeScale: fixedRadius >= 0 ? fixedRadius / Shape.corners.m : Shape.scaleForStrength(radiusStrength)
readonly property real cornerRadius: cornerRadiusM
readonly property real cornerRadiusXXS: Shape.radius("xxs", shapeScale)
readonly property real cornerRadiusXS: Shape.radius("xs", shapeScale)
readonly property real cornerRadiusS: Shape.radius("s", shapeScale)
readonly property real cornerRadiusM: Shape.radius("m", shapeScale)
readonly property real cornerRadiusL: Shape.radius("l", shapeScale)
readonly property real cornerRadiusLIncreased: Shape.radius("lIncreased", shapeScale)
readonly property real cornerRadiusXL: Shape.radius("xl", shapeScale)
readonly property real cornerRadiusXLIncreased: Shape.radius("xlIncreased", shapeScale)
readonly property real cornerRadiusXXL: Shape.radius("xxl", shapeScale)
readonly property real cornerRadiusFull: shapeScale > 0 ? 9999 : 0
readonly property real cornerRadiusXXS: Shape.radius("xxs", shapeScale, fixedRadius)
readonly property real cornerRadiusXS: Shape.radius("xs", shapeScale, fixedRadius)
readonly property real cornerRadiusS: Shape.radius("s", shapeScale, fixedRadius)
readonly property real cornerRadiusM: Shape.radius("m", shapeScale, fixedRadius)
readonly property real cornerRadiusL: Shape.radius("l", shapeScale, fixedRadius)
readonly property real cornerRadiusLIncreased: Shape.radius("lIncreased", shapeScale, fixedRadius)
readonly property real cornerRadiusXL: Shape.radius("xl", shapeScale, fixedRadius)
readonly property real cornerRadiusXLIncreased: Shape.radius("xlIncreased", shapeScale, fixedRadius)
readonly property real cornerRadiusXXL: Shape.radius("xxl", shapeScale, fixedRadius)
readonly property real cornerRadiusFull: fixedRadius >= 0 ? fixedRadius : (shapeScale > 0 ? 9999 : 0)
readonly property real cornerRadiusSmall: cornerRadiusS
readonly property real cornerRadiusLarge: cornerRadiusL
readonly property int compositorRadiusOverride: {
@@ -1167,15 +1168,15 @@ Singleton {
readonly property real windowRadius: compositorRadiusOverride >= 0 ? compositorRadiusOverride : cornerRadiusL
function scaledRadius(radius, limit) {
return Shape.scaledRadius(radius, limit, shapeScale);
return Shape.scaledRadius(radius, limit, shapeScale, fixedRadius);
}
function fullRadius(width, height) {
return Shape.fullRadius(width, height, shapeScale);
return Shape.fullRadius(width, height, shapeScale, fixedRadius);
}
function buttonRadius(width, height, sizeHeight, pressed, round) {
return Shape.buttonRadius(width, height, sizeHeight, pressed, round, shapeScale);
return Shape.buttonRadius(width, height, sizeHeight, pressed, round, shapeScale, fixedRadius);
}
readonly property real groupedListGap: spacingXXS
@@ -66,7 +66,7 @@ DankPopout {
popupWidth: DashMetrics.widthFor(SettingsData.showWeekNumber, screen?.width, DashMetrics.panelColumnsFor(activeTabId))
minimumSurfaceWidth: DashMetrics.widthFor(SettingsData.showWeekNumber, screen?.width, DashRegistry.widestPanelColumns)
popupHeight: contentLoader.item?.implicitHeight ?? (DashMetrics.tabMinHeight + DashMetrics.tabBarBlockHeight + DashMetrics.contentGap + DashMetrics.contentPadding * 2)
popupHeight: contentLoader.item?.implicitHeight ?? (DashMetrics.tabDefaultHeight + DashMetrics.tabBarBlockHeight + DashMetrics.contentGap + DashMetrics.contentPadding * 2)
triggerWidth: DashMetrics.triggerWidth
screen: triggerScreen
@@ -247,7 +247,7 @@ DankPopout {
readonly property int panelColumns: DashMetrics.panelColumnsFor(root.activeTabId)
readonly property int contentRows: DashMetrics.rowsForHeight(pages.currentHostImplicitHeight)
readonly property int panelRows: Math.max(DashMetrics.panelFloorRowsFor(root.activeTabId), contentRows)
readonly property bool panelAtDefault: panelColumns === DashMetrics.defaultGridColumns && DashMetrics.panelFloorRowsFor(root.activeTabId) <= contentRows
readonly property bool panelAtDefault: panelColumns === DashMetrics.defaultGridColumns && panelRows === DashMetrics.defaultPanelRows(root.activeTabId, contentRows)
readonly property DankPanelResizer panelResizer: DankPanelResizer {
popout: root
stepWidth: DashMetrics.preferredColumnWidth + DashMetrics.gridGap
@@ -269,7 +269,7 @@ DankPopout {
if (columnsChanged)
values.panelColumns = columns;
if (rowsChanged)
values.panelRows = rows > mainContainer.contentRows ? rows : DashMetrics.minimumTabRows;
values.panelRows = DashMetrics.panelRowsToStore(root.activeTabId, rows, mainContainer.contentRows);
DashRegistry.setOptions(root.activeTabId, values);
DashMetrics.panelPreview = null;
}
@@ -691,7 +691,7 @@ DankPopout {
id: pages
property var currentHost: null
property real settledHeight: DashMetrics.tabMinHeight
property real settledHeight: DashMetrics.tabDefaultHeight
readonly property var currentItem: currentHost?.item ?? null
readonly property Item focusTarget: currentHost?.focusTarget ?? null
readonly property bool currentSettled: !!currentHost && (!!currentHost.item || currentHost.failed)
+22 -2
View File
@@ -48,6 +48,7 @@ Singleton {
readonly property int maximumGridRows: 8
readonly property int maximumCardRows: 64
readonly property int minimumTabRows: 4
readonly property int defaultTabRows: 5
property var panelPreview: null
function clampColumns(value) {
@@ -77,13 +78,31 @@ Singleton {
function panelFloorRowsFor(id) {
if (panelPreview?.id === id)
return panelPreview.rows;
return storedPanelRows(id) || minimumTabRows;
return storedPanelRows(id) || defaultRowsFor(id);
}
function defaultRowsForTab(tab) {
return tab?.sizeToContent === true ? minimumTabRows : defaultTabRows;
}
function defaultRowsFor(id) {
return defaultRowsForTab(DashRegistry.entry(id)?.tab);
}
function panelRowsFor(id, contentHeight = 0) {
return Math.max(panelFloorRowsFor(id), rowsForHeight(contentHeight));
}
function defaultPanelRows(id, contentRows) {
return Math.max(defaultRowsFor(id), contentRows);
}
function panelRowsToStore(id, rows, contentRows) {
if (rows < defaultRowsFor(id) || rows > defaultPanelRows(id, contentRows))
return rows;
return defaultRowsFor(id);
}
function panelHeightFor(id, contentHeight = 0) {
return heightForRows(panelRowsFor(id, contentHeight));
}
@@ -111,7 +130,8 @@ Singleton {
readonly property int gridColumns: panelColumnsFor(overviewId)
readonly property real gridRowUnit: 96
readonly property real gridGap: Theme.spacingS
readonly property real tabMinHeight: minimumTabRows * gridRowUnit + (minimumTabRows - 1) * gridGap
readonly property real tabMinHeight: heightForRows(minimumTabRows)
readonly property real tabDefaultHeight: heightForRows(defaultTabRows)
readonly property real cardRadius: Theme.cornerRadius
readonly property real surfaceRadius: Theme.cornerRadiusXL
readonly property real mutedAlpha: 0.72
+8 -7
View File
@@ -69,10 +69,10 @@ Singleton {
return choice("tone", I18n.tr("Tone", "noun, dashboard widget color tone option label"), "", toneChoices);
}
function panelOptions() {
function panelOptions(tab) {
return [Object.assign(number("panelColumns", I18n.tr("Panel width (columns)"), DashMetrics.defaultGridColumns, DashMetrics.minimumGridColumns, DashMetrics.maximumGridColumns), {
"settingsOnly": true
}), Object.assign(number("panelRows", I18n.tr("Panel height (rows)"), DashMetrics.minimumTabRows, DashMetrics.minimumTabRows, DashMetrics.maximumGridRows), {
}), Object.assign(number("panelRows", I18n.tr("Panel height (rows)"), DashMetrics.defaultRowsForTab(tab), DashMetrics.minimumTabRows, DashMetrics.maximumGridRows), {
"settingsOnly": true
})];
}
@@ -95,7 +95,8 @@ Singleton {
"description": I18n.tr("Now playing and media controls"),
"tab": {
"component": mediaTab,
"async": true
"async": true,
"sizeToContent": true
},
"card": {
"component": mediaCard,
@@ -143,7 +144,8 @@ Singleton {
"available": SettingsData.weatherEnabled,
"tab": {
"component": weatherTab,
"async": true
"async": true,
"sizeToContent": true
},
"card": {
"component": weatherCard,
@@ -372,7 +374,6 @@ Singleton {
}
readonly property var entries: {
const panel = panelOptions();
return builtins.map(e => e.card ? Object.assign({}, e, {
"card": Object.assign({
"text": e.text,
@@ -383,7 +384,7 @@ Singleton {
"maxH": DashMetrics.maximumCardRows
}, e.card)
}) : e).concat(pluginEntries).map(e => e.tab ? Object.assign({}, e, {
"options": (e.options ?? []).concat(panel)
"options": (e.options ?? []).concat(panelOptions(e.tab))
}) : e);
}
@@ -548,7 +549,7 @@ Singleton {
const stored = storedOptions(id);
if (!("panelColumns" in stored) && !("panelRows" in stored))
return;
setPanelSize(id, DashMetrics.defaultGridColumns, DashMetrics.minimumTabRows);
setPanelSize(id, DashMetrics.defaultGridColumns, DashMetrics.defaultRowsFor(id));
}
function resetOptions(id) {
@@ -38,7 +38,7 @@ SettingsRow {
leading: Rectangle {
width: Theme.iconButtonSize
height: width
radius: Theme.cornerRadiusFull
radius: Theme.fullRadius(width, height)
color: root.selected ? Theme.onPrimary : Theme.chipSurface
DankIcon {
@@ -81,7 +81,7 @@ FocusScope {
viewport.contentY = Math.max(0, Math.min(point.y, bottom));
}
implicitHeight: NotificationMetrics.centerMaxHeight
implicitHeight: DashMetrics.tabMinHeight
enabled: interactive
clip: true
LayoutMirroring.enabled: I18n.isRtl
@@ -569,7 +569,7 @@ Card {
width: parent ? parent.width : 0
height: Math.max(DashMetrics.eventRowMinHeight, eventContent.implicitHeight + Theme.spacingS * 2)
radius: Theme.cornerRadiusM
radius: Theme.cornerRadiusS
color: isDragging ? Theme.withAlpha(Theme.primary, Theme.stateLayerDrag) : Theme.foregroundColor(Theme.chipSurface, Theme.isFloatingWindow(root))
z: isDragging ? 100 : visualIndex
activeFocusOnTab: !!modelData && !isEditing && root.interactive
+1 -1
View File
@@ -16,7 +16,7 @@ Item {
LayoutMirroring.childrenInherit: true
implicitWidth: DashMetrics.contentWidthFor(SettingsData.showWeekNumber, DashMetrics.panelColumnsFor(entryId))
implicitHeight: DashMetrics.tabMinHeight + DashMetrics.wallpaperFooterHeight
implicitHeight: DashMetrics.tabMinHeight
property string wallpaperDir: ""
readonly property string searchQuery: wallpaperSearchField.text
@@ -58,7 +58,7 @@ FocusScope {
if (columnsChanged)
values.panelColumns = columns;
if (rowsChanged)
values.panelRows = rows > root.contentRows ? rows : DashMetrics.minimumTabRows;
values.panelRows = DashMetrics.panelRowsToStore(root.entryId, rows, root.contentRows);
DashRegistry.setOptions(root.entryId, values);
DashMetrics.panelPreview = null;
}
@@ -210,7 +210,7 @@ FocusScope {
buttonSize: PopoutMetrics.chromeButtonSize
iconSize: PopoutMetrics.chromeIconSize
resizing: root.panelResizer.resizing
atDefault: root.panelColumns === DashMetrics.defaultGridColumns && DashMetrics.panelFloorRowsFor(root.entryId) <= root.contentRows
atDefault: root.panelColumns === DashMetrics.defaultGridColumns && root.panelRows === DashMetrics.defaultPanelRows(root.entryId, root.contentRows)
sizeText: root.panelColumns + "×" + root.panelRows
onResizeStarted: (px, py, signX) => root.panelResizer.begin(px, py, signX)
onResizeMoved: (px, py) => root.panelResizer.move(px, py)
@@ -78,7 +78,7 @@ QtObject {
readonly property real dashboardMaxWidth: Math.min(dashboardAvailableWidth, DashMetrics.widthFor(SettingsData.showWeekNumber, undefined, editingActivity !== "" ? dashboardColumnCap : DashRegistry.widestPanelColumns) + PopoutMetrics.editOverflow * 2)
property var dashboardContentHeights: ({})
readonly property real dashboardChromeHeight: Theme.buttonHeightXS + Theme.spacingXS * 2 + DashMetrics.contentPadding
readonly property real dashboardHeight: Math.min(dashboardAvailableHeight, Math.max(DashMetrics.tabMinHeight + dashboardChromeHeight, ...Object.values(dashboardContentHeights)))
readonly property real dashboardHeight: Math.min(dashboardAvailableHeight, Math.max(DashMetrics.tabDefaultHeight + dashboardChromeHeight, ...Object.values(dashboardContentHeights)))
readonly property int dashboardRowBudget: Math.max(DashMetrics.minimumTabRows, Math.floor((dashboardAvailableHeight - dashboardChromeHeight + DashMetrics.gridGap) / (DashMetrics.gridRowUnit + DashMetrics.gridGap)))
readonly property real mediaCompactMaxLength: 360
property real notificationContentLength: 0
@@ -134,10 +134,24 @@ Column {
onClicked: root.parentModal?.navigateTo("compositor_layout")
}
SettingsButtonGroupRow {
tab: "theme"
tags: ["corner", "radius", "rounded", "square", "fixed", "material", "shape"]
settingKey: "radiusMode"
text: I18n.tr("Corner style")
model: [I18n.tr("Material scale", "corner style: Material shape scale with a strength slider"), I18n.tr("Fixed", "corner style: one radius for every corner")]
currentIndex: SettingsData.radiusMode === "fixed" ? 1 : 0
onSelectionChanged: (index, selected) => {
if (selected)
SettingsData.set("radiusMode", index === 1 ? "fixed" : "scale");
}
}
SettingsSliderRow {
tab: "theme"
tags: ["corner", "radius", "rounded", "square"]
tags: ["corner", "radius", "rounded", "square", "strength"]
settingKey: "radiusStrength"
visible: SettingsData.radiusMode !== "fixed"
text: I18n.tr("Radius strength", "global component corner rounding")
description: I18n.tr("50 uses Material shapes. Lower values reduce rounding; higher values increase it.", "radius strength slider description")
minimumLabel: I18n.tr("Square")
@@ -148,6 +162,20 @@ Column {
onSliderValueChanged: newValue => SettingsData.set("radiusStrength", newValue)
}
SettingsSliderRow {
tab: "theme"
tags: ["corner", "radius", "rounded", "square", "fixed"]
settingKey: "fixedRadius"
visible: SettingsData.radiusMode === "fixed"
text: I18n.tr("Corner radius")
minimumLabel: I18n.tr("Square")
value: SettingsData.fixedRadius
minimum: 0
maximum: 32
unit: "px"
onSliderValueChanged: newValue => SettingsData.set("fixedRadius", newValue)
}
SettingsSliderRow {
tab: "theme"
tags: ["window", "corner", "radius", "rounded", "popout", "menu", "modal", "compositor", "niri", "hyprland", "mango"]
+22 -10
View File
@@ -172,6 +172,13 @@ ShellRoot {
beginDrag(root.popout, c);
c.panelResizer.end();
check(!DashMetrics.panelPreview && SettingsData.dashOptions?.media === undefined, "release without movement stores nothing");
check(c.panelRows === DashMetrics.minimumTabRows && c.contentRows === DashMetrics.minimumTabRows && c.panelAtDefault, "media sizes to its content: " + JSON.stringify([c.panelRows, c.contentRows]));
beginDrag(root.popout, c);
dragTo(root.popout, c, 0, root.rowStep);
c.panelResizer.end();
check(SettingsData.dashOptions?.media?.panelRows === DashMetrics.minimumTabRows + 1 && c.panelRows === DashMetrics.minimumTabRows + 1 && !c.panelAtDefault, "one row above the content is stored for a content-sized tab: " + JSON.stringify([SettingsData.dashOptions?.media, c.panelRows]));
DashRegistry.resetPanelSize("media");
check(SettingsData.dashOptions?.media === undefined && c.panelRows === DashMetrics.minimumTabRows, "reset returns media to its content rows: " + c.panelRows);
root.popout.editMode = false;
DashRegistry.setPanelSize("overview", DashMetrics.defaultGridColumns, root.baseRows + 1);
root.popout.requestTab("overview");
@@ -270,40 +277,45 @@ ShellRoot {
break;
case 5:
root.wallHeight = root.popout.popupHeight;
check(c.contentRows === DashMetrics.rowsForHeight(DashMetrics.tabMinHeight + DashMetrics.wallpaperFooterHeight) && c.panelRows === c.contentRows, "wallpaper rows cover its content: " + JSON.stringify([c.contentRows, c.panelRows]));
check(DashMetrics.heightForRows(c.contentRows) > DashMetrics.tabMinHeight + DashMetrics.wallpaperFooterHeight, "wallpaper content sits between rows");
check(c.panelResizer.maxRows > c.contentRows, "screen leaves room for one more row: " + JSON.stringify([c.panelResizer.maxRows, c.contentRows]));
check(c.contentRows === DashMetrics.minimumTabRows && c.panelRows === DashMetrics.defaultTabRows, "wallpaper rests on the shared default rows: " + JSON.stringify([c.contentRows, c.panelRows]));
check(c.panelResizer.maxRows > c.panelRows, "screen leaves room for one more row: " + JSON.stringify([c.panelResizer.maxRows, c.panelRows]));
beginDrag(root.popout, c);
dragTo(root.popout, c, 0, root.rowStep);
check(DashMetrics.panelPreview?.rows === c.contentRows + 1, "wallpaper preview adds a row: " + JSON.stringify(DashMetrics.panelPreview));
check(DashMetrics.panelPreview?.rows === DashMetrics.defaultTabRows + 1, "wallpaper preview adds a row: " + JSON.stringify(DashMetrics.panelPreview));
break;
case 6:
check(root.popout.popupHeight === root.wallHeight + root.rowStep, "one row of drag adds one row of height: " + JSON.stringify([root.popout.popupHeight, root.wallHeight]));
dragTo(root.popout, c, 0, 0);
check(DashMetrics.panelPreview?.rows === c.contentRows, "dragging back lands on the content rows");
check(DashMetrics.panelPreview?.rows === DashMetrics.defaultTabRows, "dragging back lands on the default rows");
break;
case 7:
check(root.popout.popupHeight === root.wallHeight, "the content-row preview matches the resting height: " + JSON.stringify([root.popout.popupHeight, root.wallHeight]));
check(root.popout.popupHeight === root.wallHeight, "the default-row preview matches the resting height: " + JSON.stringify([root.popout.popupHeight, root.wallHeight]));
c.panelResizer.end();
check(!DashMetrics.panelPreview && SettingsData.dashOptions?.wallpaper === undefined, "release on the start rows stores nothing");
beginDrag(root.popout, c);
dragTo(root.popout, c, 0, root.rowStep);
c.panelResizer.end();
check(SettingsData.dashOptions?.wallpaper?.panelRows === c.contentRows + 1, "wallpaper rows stored: " + JSON.stringify(SettingsData.dashOptions?.wallpaper));
check(SettingsData.dashOptions?.wallpaper?.panelRows === DashMetrics.defaultTabRows + 1, "wallpaper rows stored: " + JSON.stringify(SettingsData.dashOptions?.wallpaper));
break;
case 8:
check(root.popout.popupHeight === root.wallHeight + root.rowStep, "committed rows keep the preview height: " + JSON.stringify([root.popout.popupHeight, root.wallHeight]));
beginDrag(root.popout, c);
dragTo(root.popout, c, 0, -root.rowStep);
c.panelResizer.end();
check(SettingsData.dashOptions?.wallpaper === undefined, "dragging back to the content clears the stored rows");
check(SettingsData.dashOptions?.wallpaper === undefined, "dragging back to the default clears the stored rows");
beginDrag(root.popout, c);
dragTo(root.popout, c, 0, -root.rowStep);
c.panelResizer.end();
check(SettingsData.dashOptions?.wallpaper?.panelRows === DashMetrics.minimumTabRows && c.panelRows === DashMetrics.minimumTabRows, "wallpaper shrinks below the default down to its content: " + JSON.stringify([SettingsData.dashOptions?.wallpaper, c.panelRows]));
break;
case 9:
check(root.popout.popupHeight === root.wallHeight, "cleared rows keep the row-aligned height: " + JSON.stringify([root.popout.popupHeight, root.wallHeight]));
check(root.popout.popupHeight === root.wallHeight - root.rowStep, "stored rows below the default shrink the panel: " + JSON.stringify([root.popout.popupHeight, root.wallHeight]));
DashRegistry.resetPanelSize("wallpaper");
check(SettingsData.dashOptions?.wallpaper === undefined && c.panelRows === DashMetrics.defaultTabRows, "reset returns wallpaper to the default rows: " + c.panelRows);
root.popout.editMode = false;
break;
case 10:
check(root.popout.popupHeight === root.wallHeightOut, "leaving edit mode keeps the height: " + JSON.stringify([root.popout.popupHeight, root.wallHeightOut]));
check(root.popout.popupHeight === root.wallHeightOut, "reset restores the default height outside edit mode: " + JSON.stringify([root.popout.popupHeight, root.wallHeightOut]));
SettingsData.weatherEnabled = true;
root.popout.requestTab("weather");
break;
+1 -1
View File
@@ -7,7 +7,7 @@ const spec = store.SpecModule;
test("legacy migration preserves explicit radii and defaults sparse settings to 50", () => {
for (const version of [0, 13, 14, 18, 22]) {
for (const [radius, strength] of [[0, 0], [8, 25], [12, 38], [16, 50], [32, 80], [undefined, 50]]) {
for (const [radius, strength] of [[0, 0], [8, 25], [12, 38], [16, 50], [32, 100], [undefined, 50]]) {
const original = { configVersion: version, cornerRadius: radius, niriLayoutRadiusOverride: 17 };
const migrated = store.migrateToVersion(original, 26);
assert.equal(migrated.radiusStrength, strength);
@@ -549,6 +549,44 @@
],
"page": "theme_surfaces"
},
{
"section": "fixedRadius",
"label": "Corner radius",
"tabIndex": -1,
"category": "Interface style",
"keywords": [
"corner",
"corners",
"fixed",
"interface",
"radius",
"round",
"rounded",
"square",
"style"
],
"page": "theme_surfaces"
},
{
"section": "radiusMode",
"label": "Corner style",
"tabIndex": -1,
"category": "Interface style",
"keywords": [
"corner",
"corners",
"fixed",
"interface",
"material",
"radius",
"round",
"rounded",
"shape",
"square",
"style"
],
"page": "theme_surfaces"
},
{
"section": "notificationHistorySaveCritical",
"label": "Critical",