/usr/lib/x86_64-linux-gnu/qt5/qml/Ubuntu/Components/Themes/Ambiance/TabBarStyle.qml is in ubuntu-ui-toolkit-theme 0.1.46+14.04.20140408.1-0ubuntu1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 | /*
* Copyright 2012 Canonical Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.0
import Ubuntu.Components 0.1
Item {
id: tabBarStyle
// used to detect when the user is interacting with the tab bar by pressing it
// or dragging the tab bar buttons.
readonly property bool pressed: mouseArea.interacting
// styling properties, public API
property color headerTextColor: Theme.palette.normal.backgroundText
property color headerTextSelectedColor: Theme.palette.selected.backgroundText
// Don't start transitions because of updates to selectionMode before styledItem is completed.
// This fixes bug #1246792: "Disable tabs scrolling animations at startup"
property bool animate: false
Binding {
target: tabBarStyle
property: "animate"
when: styledItem.width > 0
value: styledItem.animate
}
property int headerTextFadeDuration: animate ? 350 : 0
property url indicatorImageSource: "artwork/chevron.png"
property string headerFontSize: "x-large"
property int headerTextStyle: Text.Normal
property color headerTextStyleColor: Theme.palette.normal.backgroundText
property int headerFontWeight: Font.Light
property real headerTextLeftMargin: units.gu(2)
property real headerTextRightMargin: units.gu(2)
property real headerTextBottomMargin: units.gu(2)
property real buttonPositioningVelocity: animate ? 1.0 : -1
// The time of inactivity before leaving selection mode automatically
property int deactivateTime: 5000
/*
The function assures the visuals stay on the selected tab. This can be called
by the stack components holding the tabs (i.e. Tabs, ListView, etc) and only
when the changes happen on the list element values, which is not reported
automaytically through ListModel changes.
*/
function sync() {
buttonView.selectButton(styledItem.selectedIndex);
}
property var tabsModel : styledItem ? styledItem.model : null
Connections {
target: styledItem
onSelectionModeChanged: {
if (!styledItem.selectionMode) {
buttonView.selectButton(styledItem.selectedIndex);
}
}
}
Connections {
target: styledItem
onSelectedIndexChanged: buttonView.selectButton(styledItem.selectedIndex)
}
/*
Prevent events that are not accepted by tab buttons or mouseArea below
from passing through the TabBar.
*/
MouseArea {
anchors.fill: parent
onReleased: {
mouseArea.enteringSelectionMode = false;
}
}
Component {
id: tabButtonRow
Row {
id: theRow
anchors {
top: parent.top
bottom: parent.bottom
}
width: childrenRect.width
property int rowNumber: modelData
Component.onCompleted: {
if (rowNumber === 0) {
buttonView.buttonRow1 = theRow;
} else {
buttonView.buttonRow2 = theRow;
}
}
Repeater {
id: repeater
model: tabsModel
AbstractButton {
id: button
anchors {
top: parent.top
bottom: parent.bottom
}
width: text.paintedWidth + text.anchors.leftMargin + text.anchors.rightMargin
// When the tab bar is in selection mode, show both buttons corresponing to
// the tab index as selected, but when it is not in selection mode only one
// to avoid seeing fading animations of the unselected button when switching
// tabs from outside the tab bar.
property bool selected: (styledItem.selectionMode && buttonView.needsScrolling) ?
styledItem.selectedIndex === index :
buttonView.selectedButtonIndex === button.buttonIndex
property real offset: theRow.rowNumber + 1 - button.x / theRow.width;
property int buttonIndex: index + theRow.rowNumber*repeater.count
// Use opacity 0 to hide instead of setting visibility to false in order to
// make fading work well, and not to mess up width/offset computations
opacity: isVisible() ? 1.0 : 0.0
function isVisible() {
if (selected) return true;
if (!styledItem.selectionMode) return false;
if (buttonView.needsScrolling) return true;
// When we don't need scrolling, we want to avoid showing a button that is fading
// while sliding in from the right side when a new button was selected
var numTabs = tabsModel.count;
var minimum = buttonView.selectedButtonIndex;
var maximum = buttonView.selectedButtonIndex + numTabs - 1;
if (MathUtils.clamp(buttonIndex, minimum, maximum) === buttonIndex) return true;
// working modulus numTabs:
if (buttonIndex < buttonView.selectedButtonIndex - numTabs) return true;
return false;
}
Behavior on opacity {
NumberAnimation {
duration: headerTextFadeDuration
easing.type: Easing.InOutQuad
}
}
Image {
id: indicatorImage
source: indicatorImageSource
anchors {
bottom: parent.bottom
bottomMargin: headerTextBottomMargin
}
x: button.width - width
// FIXME: temporary hack for the chevron's height to match the font size
height: 0.82*sourceSize.height
// The indicator image must be visible after the selected tab button, when the
// tab bar is not in selection mode, or after the "last" button (starting with
// the selected one), when the tab bar is in selection mode.
property bool isLastAfterSelected: index === (styledItem.selectedIndex === 0 ?
repeater.count-1 :
styledItem.selectedIndex - 1)
opacity: (styledItem.selectionMode ? isLastAfterSelected : selected) ? 1 : 0
Behavior on opacity {
NumberAnimation {
duration: headerTextFadeDuration
easing.type: Easing.InOutQuad
}
}
}
Label {
id: text
color: selected ? headerTextSelectedColor : headerTextColor
Behavior on color {
ColorAnimation {
duration: headerTextFadeDuration
easing.type: Easing.InOutQuad
}
}
anchors {
left: parent.left
leftMargin: headerTextLeftMargin
rightMargin: headerTextRightMargin
baseline: parent.bottom
baselineOffset: -headerTextBottomMargin
}
text: (model.hasOwnProperty("tab") && tab.hasOwnProperty("title")) ? tab.title : title
fontSize: headerFontSize
font.weight: headerFontWeight
style: headerTextStyle
styleColor: headerTextStyleColor
}
onClicked: {
if (mouseArea.enteringSelectionMode) {
mouseArea.enteringSelectionMode = false;
} else if (opacity > 0.0) {
styledItem.selectedIndex = index;
if (!styledItem.alwaysSelectionMode) {
styledItem.selectionMode = false;
}
button.select();
}
}
onPressedChanged: {
// Catch release after a press with a delay that is too
// long to make it a click, but don't unset interacting when
// the user starts dragging. In that case it will be unset in
// buttonView.onDragEnded.
if (!pressed && !buttonView.dragging) {
// unset interacting which was set in mouseArea.onPressed
mouseArea.interacting = false;
}
}
// Select this button
function select() {
buttonView.selectedButtonIndex = button.buttonIndex;
buttonView.updateOffset(button.offset);
}
}
}
}
}
/*!
Used by autopilot tests to determine when an animation finishes moving.
\internal
*/
readonly property alias animating: offsetAnimation.running
PathView {
id: buttonView
anchors {
top: parent.top
bottom: parent.bottom
left: parent.left
}
width: needsScrolling ? parent.width : buttonRowWidth
// set to the width of one tabButtonRow in Component.onCompleted.
property real buttonRowWidth: buttonRow1 ? buttonRow1.width : 0
// set by the delegate when the components are completed.
property Row buttonRow1
property Row buttonRow2
// Track which button was last clicked
property int selectedButtonIndex: -1
delegate: tabButtonRow
model: 2 // The second buttonRow shows the buttons that disappear on the left
property bool needsScrolling: buttonRowWidth > parent.width
interactive: needsScrolling
clip: needsScrolling
highlightRangeMode: PathView.NoHighlightRange
offset: 0
path: Path {
startX: -buttonView.buttonRowWidth/2
PathLine {
x: buttonView.buttonRowWidth*1.5
}
}
// x - y (mod a), for (x - y) <= a
function cyclicDistance(x, y, a) {
var r = x - y;
return Math.min(Math.abs(r), Math.abs(r - a));
}
// Select the closest of the two buttons that represent the given tab index
function selectButton(tabIndex) {
if (!tabsModel || tabIndex < 0 || tabIndex >= tabsModel.count) return;
if (buttonView.buttonRow1 && buttonView.buttonRow2) {
var b1 = buttonView.buttonRow1.children[tabIndex];
var b2 = buttonView.buttonRow2.children[tabIndex];
// find the button with the nearest offset
var d1 = cyclicDistance(b1.offset, buttonView.offset, 2);
var d2 = cyclicDistance(b2.offset, buttonView.offset, 2);
if (d1 < d2) {
b1.select();
} else {
b2.select();
}
}
}
function updateOffset(newOffset) {
if (!newOffset) return; // do not update the offset when its value is NaN
if (offset - newOffset < -1) newOffset = newOffset - 2;
offset = newOffset;
}
Behavior on offset {
SmoothedAnimation {
id: offsetAnimation
velocity: buttonPositioningVelocity
easing.type: Easing.InOutQuad
}
}
onDragEnded: {
// unset interacting which was set in mouseArea.onPressed
mouseArea.interacting = false;
mouseArea.enteringSelectionMode = false;
}
Timer {
id: idleTimer
interval: tabBarStyle.deactivateTime
running: styledItem.selectionMode && !styledItem.alwaysSelectionMode
onTriggered: styledItem.selectionMode = false
function conditionalRestartOrStop() {
if (Qt.application.active &&
styledItem.selectionMode &&
!styledItem.alwaysSelectionMode &&
!mouseArea.interacting) {
idleTimer.restart();
} else {
idleTimer.stop();
}
}
}
// disable the timer when the application is not active and reset
// it when the application is resumed.
Connections {
target: Qt.application
onActiveChanged: idleTimer.conditionalRestartOrStop()
}
Connections {
target: styledItem
onSelectionModeChanged: idleTimer.conditionalRestartOrStop()
}
}
MouseArea {
// a tabBar not in selection mode can be put in selection mode by pressing
id: mouseArea
anchors.fill: parent
// set in onPressed, and unset in button.onPressedChanged or buttonView.onDragEnded
// because after not accepting the mouse, the released event will go to
// the buttonView or individual buttons.
property bool interacting: false
onInteractingChanged: idleTimer.conditionalRestartOrStop()
// When pressing to enter selection mode, a release should not be interpreted
// as a click on a button to select a new tab.
property bool enteringSelectionMode: false
// This MouseArea is always enabled, even when the tab bar is in selection mode,
// so that press events are detected and tabBarStyle.pressed is updated.
onPressed: {
mouseArea.interacting = true;
if (!styledItem.selectionMode) {
mouseArea.enteringSelectionMode = true;
}
styledItem.selectionMode = true;
mouse.accepted = false;
}
}
Component.onCompleted: {
tabBarStyle.sync();
}
}
|