[calendar] Refactor the Calendar grid computation a bit

Makes the code a bit simpler & lighter and fixes the sometimes missing
bottom line in calendar.

The grid is now equally padded from both sides (including the month
name), so basically it's now always aligned to the center and this also
fixes the cases where there was a bigger padding on one side than on the
other.

The grid is now also anchored to the bottom so that the bottom margin
can stay moreless consistent with the side margins in different sizes.

Change-Id: I2f2173d11e473d6e93db2bdca002269e4239f516
REVIEW: 124072
CHANGELOG: Improve hidpi support in the Calendar grid component
This commit is contained in:
Martin Klapetek 2015-06-15 16:35:23 +02:00
parent f9d4f3d836
commit 23add5d6e4
2 changed files with 153 additions and 154 deletions

View File

@ -25,21 +25,14 @@ Item {
readonly property int gridColumns: root.showWeekNumbers ? calendarGrid.columns + 1 : calendarGrid.columns
// This is to ensure that the inner grid.width is always aligned to be divisible by 7,
// fixes wrong side margins because of the rounding of cell size
// (consider the parent.width to be 404, the cell width would be 56,
// but 56*7 + 6 (the inner spacing) is 398, so we split the remaining 6 to avoid
// wrong alignment)
anchors {
leftMargin: Math.floor(((parent.width - (gridColumns + 1) * borderWidth) % gridColumns) / 2)
rightMargin: anchors.leftMargin
bottomMargin: anchors.leftMargin
}
// Paints the inner grid and the outer frame
Canvas {
id: canvas
anchors.fill: parent
width: (root.cellWidth + root.borderWidth) * gridColumns + root.borderWidth
height: (root.cellHeight + root.borderWidth) * calendarGrid.rows + root.borderWidth
anchors.bottom: parent.bottom
opacity: root.borderOpacity
antialiasing: false
clip: false
@ -56,28 +49,37 @@ Item {
ctx.beginPath();
// This isn't the real width/height, but rather the X coord where the line will stop
// and as the coord system starts from (0,0), we need to do "-1" to not get off-by-1 errors
var rectWidth = (root.cellWidth + root.borderWidth) * gridColumns + root.borderWidth - 1
var rectHeight = (root.cellHeight + root.borderWidth) * calendarGrid.rows + root.borderWidth - 1
// the outer frame
ctx.strokeRect(0, 0, rectWidth, rectHeight);
// When line is more wide than 1px, it is painted with 1px line at the actual coords
// and then 1px lines are added first to the left of the middle then right (then left again)
// So all the lines need to be offset a bit to have their middle point in the center
// of the grid spacing rather than on the left most pixel, otherwise they will be painted
// over the days grid which will be visible on eg. mouse hover
var lineBasePoint = Math.floor(root.borderWidth / 2)
// horizontal lines
for (var i = 0; i < calendarGrid.rows - 1; i++) {
var lineY = (rectHeight / calendarGrid.rows) * (i + 1);
for (var i = 0; i < calendarGrid.rows + 1; i++) {
var lineY = lineBasePoint + (root.cellHeight + root.borderWidth) * (i);
ctx.moveTo(root.showWeekNumbers ? root.cellWidth + root.borderWidth : root.borderWidth, lineY);
ctx.lineTo(rectWidth, lineY);
if (i == 0 || i == calendarGrid.rows) {
ctx.moveTo(0, lineY);
} else {
ctx.moveTo(root.showWeekNumbers ? root.cellWidth + root.borderWidth : root.borderWidth, lineY);
}
ctx.lineTo(width, lineY);
}
// vertical lines
for (var i = 0; i < gridColumns - 1; i++) {
var lineX = (rectWidth / gridColumns) * (i + 1);
for (var i = 0; i < gridColumns + 1; i++) {
var lineX = lineBasePoint + (root.cellWidth + root.borderWidth) * (i);
ctx.moveTo(lineX, root.borderWidth + root.cellHeight);
ctx.lineTo(lineX, rectHeight);
// Draw the outer vertical lines in full height so that it closes
// the outer rectangle
if (i == 0 || i == gridColumns) {
ctx.moveTo(lineX, 0);
} else {
ctx.moveTo(lineX, root.borderWidth + root.cellHeight);
}
ctx.lineTo(lineX, height);
}
ctx.closePath();
@ -129,10 +131,15 @@ Item {
// because there's one more cell to count with and therefore also
// another border to add
x: root.showWeekNumbers ? 2 * root.borderWidth + root.cellWidth: root.borderWidth
y: root.borderWidth
anchors {
bottom: parent.bottom
bottomMargin: root.borderWidth
}
columns: calendarBackend.days
rows: calendarBackend.weeks + 1
spacing: 1
spacing: root.borderWidth
property Item selectedItem
property bool containsEventItems: false // FIXME
property bool containsTodoItems: false // FIXME
@ -145,6 +152,8 @@ Item {
}
}
Repeater {
id: days
model: calendarBackend.days
@ -158,7 +167,7 @@ Item {
verticalAlignment: Text.AlignBottom
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
anchors.bottomMargin: borderWidth * 2
anchors.bottomMargin: units.smallSpacing
}
}
}

View File

@ -16,6 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.0
import QtQuick.Layouts 1.1
import org.kde.plasma.calendar 2.0
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.components 2.0 as PlasmaComponents
@ -23,8 +24,8 @@ import org.kde.plasma.extras 2.0 as PlasmaExtras
Item {
id: root
width: parent.width
height: parent.height
anchors.fill: parent
property QtObject date
property date showDate: new Date()
@ -32,44 +33,24 @@ Item {
property alias selectedMonth: calendarBackend.monthName
property alias selectedYear: calendarBackend.year
property int mWidth: theme.mSize(theme.defaultFont).width
property int mHeight: theme.mSize(theme.defaultFont).height
property int borderWidth: 1
property real borderOpacity: 0.4
property int columns: calendarBackend.days
property int rows: calendarBackend.weeks
property int cellWidth: prefCellWidth()
property int cellHeight: prefCellHeight()
// Take the calendar width, subtract the inner and outer spacings and divide by number of columns (==days in week)
property int cellWidth: Math.floor((calendar.width - (root.columns + 1) * borderWidth) / (root.columns + (root.showWeekNumbers ? 1 : 0))) //prefCellWidth()
// Take the calendar height, subtract the inner spacings and divide by number of rows (root.weeks + one row for day names)
property int cellHeight: Math.floor((calendar.height - (root.rows + 1) * borderWidth) / (root.rows + 1)) //prefCellHeight()
property Item selectedItem
property int week;
property int firstDay: new Date(showDate.getFullYear(), showDate.getMonth(), 1).getDay()
property date today
property bool showWeekNumbers: true
property bool showWeekNumbers: false
function prefCellWidth() {
return Math.min(
Math.max(
mWidth * 3,
// Take the calendar width, subtract the inner and outer spacings and divide by number of columns (==days in week)
Math.floor((calendar.width - (root.columns + 1) * borderWidth) / (root.columns + (root.showWeekNumbers ? 1 : 0)))
),
mWidth * 100
)
}
function prefCellHeight() {
return Math.min(
Math.max(
mHeight * 1.5,
// Take the calendar height, subtract the inner spacings and divide by number of rows (root.weeks + one row for day names)
Math.floor((calendar.height - (root.rows + 1) * borderWidth) / (root.rows + 1))
),
mHeight * 40
)
}
function isToday(date) {
if (date.toDateString() == new Date().toDateString()) {
@ -88,67 +69,6 @@ Item {
calendarBackend.resetToToday();
}
PlasmaExtras.Heading {
id: monthHeading
anchors {
top: parent.top
left: parent.left
right: parent.right
}
level: 1
text: calendarBackend.displayedDate.getFullYear() == new Date().getFullYear() ? root.selectedMonth : root.selectedMonth + ", " + root.selectedYear
elide: Text.ElideRight
font.capitalization: Font.Capitalize
Loader {
id: menuLoader
property QtObject calendarBackend: calendarBackend
}
MouseArea {
id: monthMouse
property int previousPixelDelta
width: monthHeading.paintedWidth
anchors {
left: parent.left
top: parent.top
bottom: parent.bottom
}
onClicked: {
if (menuLoader.source == "") {
menuLoader.source = "MonthMenu.qml"
}
menuLoader.item.year = selectedYear
menuLoader.item.open(0, height);
}
onExited: previousPixelDelta = 0
onWheel: {
var delta = wheel.angleDelta.y || wheel.angleDelta.x
var pixelDelta = wheel.pixelDelta.y || wheel.pixelDelta.x
// For high-precision touchpad scrolling, we get a wheel event for basically every slightest
// finger movement. To prevent the view from suddenly ending up in the next century, we
// cumulate all the pixel deltas until they're larger than the label and then only change
// the month. Standard mouse wheel scrolling is unaffected since it's fine.
if (pixelDelta) {
if (Math.abs(previousPixelDelta) < monthMouse.height) {
previousPixelDelta += pixelDelta
return
}
}
if (delta >= 15) {
calendarBackend.previousMonth()
} else if (delta <= -15) {
calendarBackend.nextMonth()
}
previousPixelDelta = 0
}
}
}
Calendar {
id: calendarBackend
@ -158,56 +78,126 @@ Item {
today: root.today
}
DaysCalendar {
id: calendar
ColumnLayout {
// This is to ensure that the inner grid.width is always aligned to be divisible by 7,
// fixes wrong side margins because of the rounding of cell size
// (consider the parent.width to be 404, the cell width would be 56,
// but 56*7 + 6 (the inner spacing) is 398, so we split the remaining 6 to avoid
// wrong alignment)
anchors {
top: monthHeading.bottom
left: parent.left
right: parent.right
bottom: parent.bottom
fill: parent
leftMargin: Math.floor(((parent.width - (calendar.gridColumns + 1) * borderWidth) % calendar.gridColumns) / 2)
rightMargin: anchors.leftMargin
bottomMargin: anchors.leftMargin
}
PlasmaComponents.Label {
text: "◀"
opacity: leftmouse.containsMouse ? 1 : 0.4
Behavior on opacity { NumberAnimation {} }
anchors {
top: parent.top
left: parent.left
leftMargin: Math.floor(units.largeSpacing / 2)
PlasmaExtras.Heading {
id: monthHeading
level: 1
text: calendarBackend.displayedDate.getFullYear() == new Date().getFullYear() ? root.selectedMonth : root.selectedMonth + ", " + root.selectedYear
elide: Text.ElideRight
font.capitalization: Font.Capitalize
Loader {
id: menuLoader
property QtObject calendarBackend: calendarBackend
}
MouseArea {
id: leftmouse
anchors.fill: parent
anchors.margins: -units.largeSpacing / 3
hoverEnabled: true
id: monthMouse
property int previousPixelDelta
width: monthHeading.paintedWidth
anchors {
left: parent.left
top: parent.top
bottom: parent.bottom
}
onClicked: {
calendarBackend.previousMonth()
if (menuLoader.source == "") {
menuLoader.source = "MonthMenu.qml"
}
menuLoader.item.year = selectedYear
menuLoader.item.open(0, height);
}
onExited: previousPixelDelta = 0
onWheel: {
var delta = wheel.angleDelta.y || wheel.angleDelta.x
var pixelDelta = wheel.pixelDelta.y || wheel.pixelDelta.x
// For high-precision touchpad scrolling, we get a wheel event for basically every slightest
// finger movement. To prevent the view from suddenly ending up in the next century, we
// cumulate all the pixel deltas until they're larger than the label and then only change
// the month. Standard mouse wheel scrolling is unaffected since it's fine.
if (pixelDelta) {
if (Math.abs(previousPixelDelta) < monthMouse.height) {
previousPixelDelta += pixelDelta
return
}
}
if (delta >= 15) {
calendarBackend.previousMonth()
} else if (delta <= -15) {
calendarBackend.nextMonth()
}
previousPixelDelta = 0
}
}
}
PlasmaComponents.Label {
text: "▶"
opacity: rightmouse.containsMouse ? 1 : 0.4
Behavior on opacity { NumberAnimation {} }
anchors {
top: parent.top
right: parent.right
rightMargin: Math.floor(units.largeSpacing / 2)
DaysCalendar {
id: calendar
Layout.fillWidth: true
Layout.fillHeight: true
PlasmaComponents.Label {
text: "◀"
opacity: leftmouse.containsMouse ? 1 : 0.4
Behavior on opacity { NumberAnimation {} }
font.pixelSize: Math.max(theme.smallestFont.pixelSize, Math.floor(root.cellHeight / 3))
anchors {
top: parent.top
left: parent.left
leftMargin: Math.floor(units.largeSpacing / 2) + root.borderWidth
topMargin: anchors.leftMargin
}
MouseArea {
id: leftmouse
anchors.fill: parent
anchors.margins: -units.largeSpacing / 3
hoverEnabled: true
onClicked: {
calendarBackend.previousMonth()
}
}
}
MouseArea {
id: rightmouse
anchors.fill: parent
anchors.margins: -units.largeSpacing / 3
hoverEnabled: true
onClicked: {
calendarBackend.nextMonth()
PlasmaComponents.Label {
text: "▶"
opacity: rightmouse.containsMouse ? 1 : 0.4
Behavior on opacity { NumberAnimation {} }
font.pixelSize: Math.max(theme.smallestFont.pixelSize, Math.floor(root.cellHeight / 3))
anchors {
top: parent.top
right: parent.right
rightMargin: Math.floor(units.largeSpacing / 2) + root.borderWidth
topMargin: anchors.rightMargin
}
MouseArea {
id: rightmouse
anchors.fill: parent
anchors.margins: -units.largeSpacing / 3
hoverEnabled: true
onClicked: {
calendarBackend.nextMonth()
}
}
}
}
}
/*
Item {
id: calendarToolbar