import QtQuick import Weave.Controls import Weave.Templates as T import "internal" T.IconButton { id: root opacity: root.enabled ? 1.0 : Theme.component.iconbutton.opacity.disabled implicitWidth: Math.max(implicitContentWidth + leftPadding + rightPadding, implicitBackgroundWidth + leftInset + rightInset) implicitHeight: Math.max(implicitContentHeight + topPadding + bottomPadding, implicitBackgroundHeight + topInset + bottomInset) // padding includes the indicator item as well as the normal padding. rightPadding: mirrored ? Theme.component.iconbutton.icon.paddingLeft : (Theme.component.iconbutton.icon.paddingRight + ((popupIndicatorVisible && popupIndicatorDisplay === T.IconButton.PopupIndicatorBesideIcon) ? implicitIndicatorWidth + Theme.component.iconbutton.caret.icon.paddingLeft + Theme.component.iconbutton.caret.icon.paddingRight : 0)) leftPadding: mirrored ? (Theme.component.iconbutton.icon.paddingRight + ((popupIndicatorVisible && popupIndicatorDisplay === T.IconButton.PopupIndicatorBesideIcon) ? implicitIndicatorWidth + Theme.component.iconbutton.caret.icon.paddingLeft + Theme.component.iconbutton.caret.icon.paddingRight : 0)) : Theme.component.iconbutton.icon.paddingLeft bottomPadding: Theme.component.iconbutton.icon.paddingBottom // For vertical case, use paddingLeft as paddingBottom, because there is no vertical-orientation-specific paddingBottom token. + ((popupIndicatorVisible && popupIndicatorDisplay === T.IconButton.PopupIndicatorBelowIcon) ? implicitIndicatorHeight + Theme.component.iconbutton.caret.icon.paddingLeft : 0) topPadding: Theme.component.iconbutton.icon.paddingTop dynamicColor: false swellOnPress: true hoverEnabled: root.enabled icon.width: Theme.component.iconbutton.icon.width icon.height: Theme.component.iconbutton.icon.height icon.color: Theme.component.iconbutton.icon.fill.default surfaceLevel: T.Surface.level // Instead of setting checkable to true, and reacting to checkedChanged() // (e.g. to trigger some behavior or set some state of another component), // developers should instead bind `checked` to the state of that other // component (i.e. that component becomes the "authoritative source" of // the button's checked-state), and trigger the other component in reaction // to clicked() (or pressed()) signals. // This allows the UI to accurately reflect the state of the system, rather // than attempting to have the system follow the state of the UI. checkable: false popupIndicatorVisible: popup != null popupOpened: popup ? popup.opened : false popupPosition: Qt.point( !popup || popup.horizontalAlignment === undefined || popup.horizontalAlignment === Menu.AlignLeft ? 0 : (popup.horizontalAlignment == Menu.AlignRight ? width - popup.width : (width - popup.width) / 2), height + (popup ? popup.topMargin + popup.verticalPadding : 0)) onPopupChanged: if (popup) popup.background.implicitWidth = Qt.binding(function() { return root.width }) contentItem: Item { implicitWidth: dynamicIcon.implicitWidth implicitHeight: dynamicIcon.implicitHeight T.IconImage { id: dynamicIcon // the icon might be larger the contentItem, because the tokens... x: -(width - parent.width)/2 y: -(height - parent.height)/2 url: root.icon.source name: root.icon.name sourceSize: Qt.size(root.icon.width, root.icon.height) color: { if (!root.dynamicColor) { return root.icon.color } else { var state = (root.pressed || root.popupOpened) ? "pressed" : (root.enabled && root.hovered) ? "hover" : root.visualFocus ? "focus" : "default" return root.checked ? Theme.component.iconbutton.icon.fill.selected[state] : Theme.component.iconbutton.icon.fill[state] } } } } indicator: T.IconImage { x: (root.popupIndicatorVisible && (root.popupIndicatorDisplay === T.IconButton.PopupIndicatorBesideIcon)) ? (root.mirrored ? Theme.component.iconbutton.icon.paddingRight : (root.width - Theme.component.iconbutton.dropdown.caret.marginLeft - width)) : ((root.width - width)/2) y: (root.popupIndicatorVisible && root.popupIndicatorDisplay === T.IconButton.PopupIndicatorBesideIcon) ? ((root.height - height)/2) : (root.height - Theme.component.iconbutton.dropdown.paddingBottom - height) visible: root.popupIndicatorVisible // We cannot use these tokens, because if the actual dimensions of the icons // (provided by the ui-controls-icons repository) don't match the expected // dimensions (defined by the tokens) then there will be distortion due to scaling. //sourceSize: Qt.size(Theme.component.iconbutton.caret.icon.width, // Theme.component.iconbutton.caret.icon.height) name: Theme.icon.medium("caret-down") color: root.icon.color } background: Item { implicitWidth: root.popupIndicatorVisible ? Theme.component.iconbutton.dropdown.width : Theme.component.iconbutton.width.default implicitHeight: Theme.component.iconbutton.height Rectangle { id: bgRect anchors.fill: parent anchors.margins: -swell radius: Theme.component.iconbutton.borderRadius color: { var state = (root.pressed || root.popupOpened) ? "pressed" : (root.enabled && root.hovered) ? "hover" : root.visualFocus ? "focus" : "default" if (root.dynamicColor && state != "pressed") { // default background color only exists for static + selected case return "transparent" } else if (root.checked) { return Theme.component.iconbutton.backgroundColor.selected[state] } else if ((root.enabled && root.hovered) || root.pressed || root.popupOpened) { return root.surfaceLevel < 300 ? Theme.component.iconbutton.surfaceHigh.backgroundColor[state] : Theme.component.iconbutton.surfaceLow.backgroundColor[state] } else { return Theme.component.iconbutton.backgroundColor.default } } property real swell: root.swellOnPress && root.pressed ? Theme.component.iconbutton.swell.outlineWidth.pressed : 0 Behavior on swell { enabled: swellAnim.duration > 0 NumberAnimation { id: swellAnim duration: Theme.component.iconbutton.swell.transitionDuration } } } // the "swell" effect needs to go "under" the outline border // so, define the border as a separate rectangle. Rectangle { id: borderRect visible: !root.dynamicColor && (root.checked || root.visualFocus) anchors.fill: parent radius: Theme.component.iconbutton.borderRadius color: "transparent" border.width: Theme.component.iconbutton.borderWidth border.color: { if (root.dynamicColor) { return "transparent" } else if (root.pressed || root.popupOpened) { return root.checked ? Theme.component.iconbutton.borderColor.selected.pressed : Theme.component.iconbutton.borderColor.default } else if (root.checked) { return (root.visualFocus && !root.hovered) ? Theme.component.iconbutton.borderColor.selected.focus : Theme.component.iconbutton.borderColor.selected.default } else { return "transparent" } } } BoxShadow { width: parent.width height: parent.height radius: Theme.component.iconbutton.borderRadius offsetX: Theme.component.iconbutton.boxShadowX.focus offsetY: Theme.component.iconbutton.boxShadowY.focus blurRadius: Theme.component.iconbutton.boxShadowBlur.focus spreadRadius: (root.visualFocus && !root.pressed && !root.hovered) ? Theme.component.iconbutton.boxShadowSpread.focus : 0 color: (root.visualFocus && !root.pressed && !root.hovered) ? Theme.component.iconbutton.boxShadowColor.focus : "transparent" Behavior on color { enabled: colorAnim.duration > 0 ColorAnimation { id: colorAnim duration: Theme.component.iconbutton.transitionDuration easing.type: Theme.component.iconbutton.transitionTimingFunction } } Behavior on spreadRadius { enabled: spreadAnim.duration > 0 NumberAnimation { id: spreadAnim duration: Theme.component.iconbutton.transitionDuration easing.type: Theme.component.iconbutton.transitionTimingFunction } } } } }