utilities/record: select mode
This commit is contained in:
parent
9a92ad2d62
commit
4f54763e02
11 changed files with 392 additions and 18 deletions
|
|
@ -8,6 +8,7 @@ MouseArea {
|
|||
property bool disabled
|
||||
property color color: Colours.palette.m3onSurface
|
||||
property real radius: parent?.radius ?? 0
|
||||
property alias rect: hoverLayer
|
||||
|
||||
function onClicked(): void {
|
||||
}
|
||||
|
|
|
|||
86
components/controls/IconTextButton.qml
Normal file
86
components/controls/IconTextButton.qml
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
import ".."
|
||||
import qs.services
|
||||
import qs.config
|
||||
import QtQuick
|
||||
|
||||
StyledRect {
|
||||
id: root
|
||||
|
||||
enum Type {
|
||||
Filled,
|
||||
Tonal,
|
||||
Text
|
||||
}
|
||||
|
||||
property alias icon: iconLabel.text
|
||||
property alias text: label.text
|
||||
property bool checked
|
||||
property bool toggle
|
||||
property real horizontalPadding: Appearance.padding.normal
|
||||
property real verticalPadding: Appearance.padding.smaller
|
||||
property alias font: label.font
|
||||
property int type: IconTextButton.Filled
|
||||
|
||||
property alias stateLayer: stateLayer
|
||||
property alias iconLabel: iconLabel
|
||||
property alias label: label
|
||||
|
||||
property bool internalChecked
|
||||
property color activeColour: type == IconTextButton.Filled ? Colours.palette.m3primary : Colours.palette.m3secondary
|
||||
property color inactiveColour: type == IconTextButton.Filled ? Colours.palette.m3surfaceContainer : Colours.palette.m3secondaryContainer
|
||||
property color activeOnColour: type == IconTextButton.Filled ? Colours.palette.m3onPrimary : Colours.palette.m3onSecondary
|
||||
property color inactiveOnColour: type == IconTextButton.Filled ? Colours.palette.m3onSurface : Colours.palette.m3onSecondaryContainer
|
||||
|
||||
function onClicked(): void {
|
||||
}
|
||||
|
||||
onCheckedChanged: internalChecked = checked
|
||||
|
||||
radius: internalChecked ? Appearance.rounding.small : implicitHeight / 2
|
||||
color: type == IconTextButton.Text ? "transparent" : internalChecked ? activeColour : inactiveColour
|
||||
|
||||
implicitWidth: row.implicitWidth + horizontalPadding * 2
|
||||
implicitHeight: row.implicitHeight + verticalPadding * 2
|
||||
|
||||
StateLayer {
|
||||
id: stateLayer
|
||||
|
||||
color: root.internalChecked ? root.activeOnColour : root.inactiveOnColour
|
||||
|
||||
function onClicked(): void {
|
||||
if (root.toggle)
|
||||
root.internalChecked = !root.internalChecked;
|
||||
root.onClicked();
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: row
|
||||
|
||||
anchors.centerIn: parent
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
MaterialIcon {
|
||||
id: iconLabel
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: root.internalChecked ? root.activeOnColour : root.inactiveOnColour
|
||||
fill: root.internalChecked ? 1 : 0
|
||||
|
||||
Behavior on fill {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: label
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: root.internalChecked ? root.activeOnColour : root.inactiveOnColour
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on radius {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
111
components/controls/Menu.qml
Normal file
111
components/controls/Menu.qml
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
|
||||
import ".."
|
||||
import "../effects"
|
||||
import qs.services
|
||||
import qs.config
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
Elevation {
|
||||
id: root
|
||||
|
||||
property list<MenuItem> items
|
||||
property MenuItem active: items[0] ?? null
|
||||
property bool expanded
|
||||
|
||||
signal itemSelected(item: MenuItem)
|
||||
|
||||
radius: Appearance.rounding.small / 2
|
||||
level: 2
|
||||
|
||||
implicitWidth: column.implicitWidth
|
||||
implicitHeight: root.expanded ? column.implicitHeight : 0
|
||||
opacity: root.expanded ? 1 : 0
|
||||
|
||||
StyledClippingRect {
|
||||
anchors.fill: parent
|
||||
radius: parent.radius
|
||||
color: Colours.palette.m3surfaceContainer
|
||||
|
||||
ColumnLayout {
|
||||
id: column
|
||||
|
||||
spacing: 0
|
||||
|
||||
Repeater {
|
||||
model: root.items
|
||||
|
||||
StyledRect {
|
||||
id: item
|
||||
|
||||
required property int index
|
||||
required property MenuItem modelData
|
||||
readonly property bool active: modelData === root.active
|
||||
|
||||
Layout.fillWidth: true
|
||||
implicitWidth: menuOptionRow.implicitWidth + Appearance.padding.normal * 2
|
||||
implicitHeight: menuOptionRow.implicitHeight + Appearance.padding.normal * 2
|
||||
|
||||
color: Qt.alpha(Colours.palette.m3secondaryContainer, active ? 1 : 0)
|
||||
|
||||
StateLayer {
|
||||
color: item.active ? Colours.palette.m3onSecondaryContainer : Colours.palette.m3onSurface
|
||||
disabled: !root.expanded
|
||||
|
||||
function onClicked(): void {
|
||||
root.itemSelected(item.modelData);
|
||||
root.active = item.modelData;
|
||||
root.expanded = false;
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: menuOptionRow
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.normal
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
MaterialIcon {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
text: item.modelData.icon
|
||||
color: item.active ? Colours.palette.m3onSecondaryContainer : Colours.palette.m3onSurfaceVariant
|
||||
}
|
||||
|
||||
StyledText {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.fillWidth: true
|
||||
text: item.modelData.text
|
||||
color: item.active ? Colours.palette.m3onSecondaryContainer : Colours.palette.m3onSurface
|
||||
}
|
||||
|
||||
Loader {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
active: item.modelData.trailingIcon.length > 0
|
||||
visible: active
|
||||
|
||||
sourceComponent: MaterialIcon {
|
||||
text: item.modelData.trailingIcon
|
||||
color: item.active ? Colours.palette.m3onSecondaryContainer : Colours.palette.m3onSurface
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on implicitHeight {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
}
|
||||
}
|
||||
}
|
||||
11
components/controls/MenuItem.qml
Normal file
11
components/controls/MenuItem.qml
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import QtQuick
|
||||
|
||||
QtObject {
|
||||
required property string text
|
||||
property string icon
|
||||
property string trailingIcon
|
||||
property string activeIcon: icon
|
||||
property string activeText: text
|
||||
|
||||
signal clicked
|
||||
}
|
||||
141
components/controls/SplitButton.qml
Normal file
141
components/controls/SplitButton.qml
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
import ".."
|
||||
import qs.services
|
||||
import qs.config
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
Row {
|
||||
id: root
|
||||
|
||||
enum Type {
|
||||
Filled,
|
||||
Tonal
|
||||
}
|
||||
|
||||
property real horizontalPadding: Appearance.padding.normal
|
||||
property real verticalPadding: Appearance.padding.smaller
|
||||
property int type: SplitButton.Filled
|
||||
property alias menuItems: menu.items
|
||||
property alias active: menu.active
|
||||
property alias expanded: menu.expanded
|
||||
property alias menu: menu
|
||||
|
||||
property color colour: type == SplitButton.Filled ? Colours.palette.m3primary : Colours.palette.m3secondaryContainer
|
||||
property color textColour: type == SplitButton.Filled ? Colours.palette.m3onPrimary : Colours.palette.m3onSecondaryContainer
|
||||
|
||||
spacing: Math.floor(Appearance.spacing.small / 2)
|
||||
|
||||
StyledRect {
|
||||
radius: implicitHeight / 2
|
||||
topRightRadius: Appearance.rounding.small / 2
|
||||
bottomRightRadius: Appearance.rounding.small / 2
|
||||
color: root.colour
|
||||
|
||||
implicitWidth: textRow.implicitWidth + root.horizontalPadding * 2
|
||||
implicitHeight: expandBtn.implicitHeight
|
||||
|
||||
StateLayer {
|
||||
id: stateLayer
|
||||
|
||||
rect.topRightRadius: parent.topRightRadius
|
||||
rect.bottomRightRadius: parent.bottomRightRadius
|
||||
color: root.textColour
|
||||
|
||||
function onClicked(): void {
|
||||
root.active?.clicked();
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: textRow
|
||||
|
||||
anchors.centerIn: parent
|
||||
anchors.horizontalCenterOffset: Math.floor(root.verticalPadding / 4)
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
MaterialIcon {
|
||||
id: iconLabel
|
||||
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
animate: true
|
||||
text: root.active?.activeIcon ?? ""
|
||||
color: root.textColour
|
||||
fill: 1
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: label
|
||||
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.preferredWidth: implicitWidth
|
||||
animate: true
|
||||
text: root.active?.activeText ?? ""
|
||||
color: root.textColour
|
||||
clip: true
|
||||
|
||||
Behavior on Layout.preferredWidth {
|
||||
Anim {
|
||||
easing.bezierCurve: Appearance.anim.curves.emphasized
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledRect {
|
||||
id: expandBtn
|
||||
|
||||
property real rad: root.expanded ? implicitHeight / 2 : Appearance.rounding.small / 2
|
||||
|
||||
radius: implicitHeight / 2
|
||||
topLeftRadius: rad
|
||||
bottomLeftRadius: rad
|
||||
color: root.colour
|
||||
|
||||
implicitWidth: implicitHeight
|
||||
implicitHeight: expandIcon.implicitHeight + root.verticalPadding * 2
|
||||
|
||||
StateLayer {
|
||||
id: expandStateLayer
|
||||
|
||||
rect.topRightRadius: parent.topRightRadius
|
||||
rect.bottomRightRadius: parent.bottomRightRadius
|
||||
color: root.textColour
|
||||
|
||||
function onClicked(): void {
|
||||
root.expanded = !root.expanded;
|
||||
}
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
id: expandIcon
|
||||
|
||||
anchors.centerIn: parent
|
||||
anchors.horizontalCenterOffset: root.expanded ? 0 : -Math.floor(root.verticalPadding / 4)
|
||||
|
||||
text: "expand_more"
|
||||
color: root.textColour
|
||||
rotation: root.expanded ? 180 : 0
|
||||
|
||||
Behavior on anchors.horizontalCenterOffset {
|
||||
Anim {}
|
||||
}
|
||||
|
||||
Behavior on rotation {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on rad {
|
||||
Anim {}
|
||||
}
|
||||
|
||||
Menu {
|
||||
id: menu
|
||||
|
||||
anchors.top: parent.bottom
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: Appearance.spacing.small
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -18,16 +18,16 @@ StyledRect {
|
|||
property real horizontalPadding: Appearance.padding.normal
|
||||
property real verticalPadding: Appearance.padding.smaller
|
||||
property alias font: label.font
|
||||
property int type: IconButton.Filled
|
||||
property int type: TextButton.Filled
|
||||
|
||||
property alias stateLayer: stateLayer
|
||||
property alias label: label
|
||||
|
||||
property bool internalChecked
|
||||
property color activeColour: type == IconButton.Filled ? Colours.palette.m3primary : Colours.palette.m3secondary
|
||||
property color inactiveColour: type == IconButton.Filled ? Colours.palette.m3surfaceContainer : Colours.palette.m3secondaryContainer
|
||||
property color activeOnColour: type == IconButton.Filled ? Colours.palette.m3onPrimary : Colours.palette.m3onSecondary
|
||||
property color inactiveOnColour: type == IconButton.Filled ? Colours.palette.m3onSurface : Colours.palette.m3onSecondaryContainer
|
||||
property color activeColour: type == TextButton.Filled ? Colours.palette.m3primary : Colours.palette.m3secondary
|
||||
property color inactiveColour: type == TextButton.Filled ? Colours.palette.m3surfaceContainer : Colours.palette.m3secondaryContainer
|
||||
property color activeOnColour: type == TextButton.Filled ? Colours.palette.m3onPrimary : Colours.palette.m3onSecondary
|
||||
property color inactiveOnColour: type == TextButton.Filled ? Colours.palette.m3onSurface : Colours.palette.m3onSecondaryContainer
|
||||
|
||||
function onClicked(): void {
|
||||
}
|
||||
|
|
@ -35,7 +35,7 @@ StyledRect {
|
|||
onCheckedChanged: internalChecked = checked
|
||||
|
||||
radius: internalChecked ? Appearance.rounding.small : implicitHeight / 2
|
||||
color: type == IconButton.Text ? "transparent" : internalChecked ? activeColour : inactiveColour
|
||||
color: type == TextButton.Text ? "transparent" : internalChecked ? activeColour : inactiveColour
|
||||
|
||||
implicitWidth: label.implicitWidth + horizontalPadding * 2
|
||||
implicitHeight: label.implicitHeight + verticalPadding * 2
|
||||
|
|
|
|||
|
|
@ -6,6 +6,6 @@ JsonObject {
|
|||
property Sizes sizes: Sizes {}
|
||||
|
||||
component Sizes: JsonObject {
|
||||
property int width: 400
|
||||
property int width: 430
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ Item {
|
|||
Record {
|
||||
props: root.props
|
||||
visibilities: root.visibilities
|
||||
z: 1
|
||||
}
|
||||
|
||||
Toggles {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ Item {
|
|||
readonly property PersistentProperties props: PersistentProperties {
|
||||
property bool recordingListExpanded: false
|
||||
property string recordingConfirmDelete
|
||||
property string recordingMode
|
||||
|
||||
reloadableId: "utilities"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ StyledRect {
|
|||
|
||||
required property var props
|
||||
required property var visibilities
|
||||
property bool modePickerExpanded
|
||||
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: layout.implicitHeight + layout.anchors.margins * 2
|
||||
|
|
@ -34,6 +35,7 @@ StyledRect {
|
|||
|
||||
RowLayout {
|
||||
spacing: Appearance.spacing.normal
|
||||
z: 1
|
||||
|
||||
StyledRect {
|
||||
implicitWidth: implicitHeight
|
||||
|
|
@ -77,16 +79,36 @@ StyledRect {
|
|||
}
|
||||
}
|
||||
|
||||
IconButton {
|
||||
icon: "not_started"
|
||||
checked: Recorder.running
|
||||
type: IconButton.Filled
|
||||
inactiveColour: Colours.palette.m3surfaceContainerHighest
|
||||
toggle: true
|
||||
SplitButton {
|
||||
active: menuItems.find(m => root.props.recordingMode === m.icon + m.text) ?? menuItems[0]
|
||||
menu.onItemSelected: item => root.props.recordingMode = item.icon + item.text
|
||||
|
||||
function onClicked(): void {
|
||||
Recorder.toggle();
|
||||
}
|
||||
menuItems: [
|
||||
MenuItem {
|
||||
icon: "fullscreen"
|
||||
text: qsTr("Record fullscreen")
|
||||
activeText: qsTr("Fullscreen")
|
||||
onClicked: Recorder.toggle()
|
||||
},
|
||||
MenuItem {
|
||||
icon: "screenshot_region"
|
||||
text: qsTr("Record region")
|
||||
activeText: qsTr("Region")
|
||||
onClicked: Recorder.toggle(["-r"])
|
||||
},
|
||||
MenuItem {
|
||||
icon: "select_to_speak"
|
||||
text: qsTr("Record fullscreen with sound")
|
||||
activeText: qsTr("Fullscreen")
|
||||
onClicked: Recorder.toggle(["-s"])
|
||||
},
|
||||
MenuItem {
|
||||
icon: "volume_up"
|
||||
text: qsTr("Record region with sound")
|
||||
activeText: qsTr("Region")
|
||||
onClicked: Recorder.toggle(["-sr"])
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ Singleton {
|
|||
readonly property alias running: props.running
|
||||
readonly property alias paused: props.paused
|
||||
|
||||
function toggle(): void {
|
||||
Quickshell.execDetached(["caelestia", "record"]);
|
||||
function toggle(extraArgs: list<string>): void {
|
||||
Quickshell.execDetached(["caelestia", "record", ...extraArgs]);
|
||||
props.running = !props.running;
|
||||
if (!props.running)
|
||||
props.paused = false;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue