[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-usability
Subject:    KDatePicker improvements
From:       Jakob Petsovits <jpetso () gmx ! at>
Date:       2007-11-22 10:05:53
Message-ID: 200711221105.54140.jpetso () gmx ! at
[Download RAW message or body]

Hi list,

originally, I just wanted to get rid of the arrow-{left,right}[-double] icons 
in KDatePicker, but as it's hard to take stuff away from something that's 
already good, I could not only remove icons but needed to provide a 
replacement that works better than the current solution.

So, the following KDatePicker improvements are the result of a bunch of
UI peer reviews on the usability list, and should thus hopefully make for a 
better user experience.

Screenshots:
Current: http://jakob.petsovits.at/files-jakob/image/kdatepicker-current.png
Modified: http://jakob.petsovits.at/files-jakob/image/kdatepicker-new.png

The main change is that the next/previous arrows are directly positioned next 
to each of the year and month displays, as the discoverability of the two 
different kinds of arrows is not quite ideal. So the year and month are now 
positioned vertically (the horizontal direction is needed for the arrows), 
which makes some other layout changes necessary, or better, possible :)

The whole year/month/week/go-to-today navigation is now concentrated in one 
place (at the top of the widget) instead of being split up between top and 
bottom. The direct date line has been identified by Aaron as source to copy 
from, and lacking that much space I made the "copy to clipboard" 
functionality into a button (which invokes a context menu with short or long 
date format to be copied).

While working on this, I also came up with some other fixes:

* Fix a grave bug where the week selector selects other weeks than it should,
  for example for 2007 (try selecting "week 46"). After the treatment, it not
  only works now but even remembers the current day of week, which it did not
  ever do before.

* Embed the year line edit into the date picker instead of putting it into a
  popup. It's much more natural now, you can edit the years directly in place.

* Fix the calculation of the minimum size for the month toolbutton.
  On my system, it missed out on about 12 pixels of additional margin
  which caused the next/previous month icons to hop around.

* Fix focus issues. That makes it possible for KFind's KDateCombo not to
  install an event filter that catches Enter key presses; instead, KFind can
  now rely on dateSelected() being emitted in any case.

* Replace calendar()->setYMD() by calendar()->setDate(), as the former
  is deprecated.

* ...and as you would have suspected, I fixed the icon names properly.


Now this patch is binary compatible, but it comes with three slight drawbacks:

- In order to keep BC, I abused lineEnterPressed() and changed its
  behaviour - which wasn't documented before, either. Before, it handled
  the full-date line edit (which is now gone), whereas now it handles the
  year line edit which has moved out of the popup and into the widget.
  lineEnterPressed() is protected, but not actively used in all of the
  SVN repository, so I hope I can get away with that =)
  Alternatively, I could break BC and call it yearLineChanged() instead.

- As the full-date line edit is gone, the dateEntered() signal is not emitted
  anymore. This is more logical since dateSelected() covers its purpose
  anyways (which is signaling the final selection of the desired date),
  and should not be harmful as the apidox did recommend to connect to
  both dateSelected() and dateEntered(). I guess we could just mark
  dateEntered as deprecated. (How is this done with signals?)

- There's two new i18n strings, "Copy date to clipboard" (as tooltip)
  and "Copy to clipboard" (as context menu title). If it is decided that
  I must not introduce those, I can fall back to "Copy" for the tooltip
  and leave out the context menu title, but I'd like to keep them
  (and ask the translators list once the patch has gotten through k-c-d).


So...
* is the patch ok from a technical point of view?
* ok to apply? if yes, instantly or on Monday?
* if no, what do I need to leave out?

Thanks for your patience,
  Jakob

["kdatepicker-usability-attempt-try3.diff" (text/x-diff)]

Index: kdatepicker.h
===================================================================
--- kdatepicker.h	(revision 739813)
+++ kdatepicker.h	(working copy)
@@ -172,6 +172,7 @@
     void selectMonthClicked();
     void selectYearClicked();
     void lineEnterPressed();
+    void copyDateButtonClicked();
     void todayButtonClicked();
     void weekSelected( int );
 
Index: kdatetable.cpp
===================================================================
--- kdatetable.cpp	(revision 739813)
+++ kdatetable.cpp	(working copy)
@@ -817,28 +817,29 @@
 void KPopupFrame::popup( const QPoint &pos )
 {
     // Make sure the whole popup is visible.
-    QRect d = KGlobalSettings::desktopGeometry( pos );
+    QRect desktopGeometry = KGlobalSettings::desktopGeometry( pos );
 
     int x = pos.x();
     int y = pos.y();
     int w = width();
     int h = height();
-    if ( x + w > d.x() + d.width() ) {
-        x = d.width() - w;
+    if ( x + w > desktopGeometry.x() + desktopGeometry.width() ) {
+        x = desktopGeometry.width() - w;
     }
-    if ( y + h > d.y() + d.height() ) {
-        y = d.height() - h;
+    if ( y + h > desktopGeometry.y() + desktopGeometry.height() ) {
+        y = desktopGeometry.height() - h;
     }
-    if ( x < d.x() ) {
+    if ( x < desktopGeometry.x() ) {
         x = 0;
     }
-    if ( y < d.y() ) {
+    if ( y < desktopGeometry.y() ) {
         y = 0;
     }
 
     // Pop the thingy up.
     move( x, y );
     show();
+    d->main->setFocus();
 }
 
 int KPopupFrame::exec( const QPoint &pos )
Index: kdatepicker_p.h
===================================================================
--- kdatepicker_p.h	(revision 739813)
+++ kdatepicker_p.h	(working copy)
@@ -3,6 +3,7 @@
     Copyright (C) 1997 Tim D. Gilman (tdgilman@best.org)
               (C) 1998-2001 Mirko Boehm (mirko@kde.org)
               (C) 1998-2001 Mirko Boehm (john@layt.net)
+              (C) 2007 Jakob Petsovits <jpetso@gmx.at>
     This library is free software; you can redistribute it and/or
     modify it under the terms of the GNU Library General Public
     License as published by the Free Software Foundation; either
@@ -24,9 +25,9 @@
 
 #include <kcalendarsystem.h>
 
-#include <QtCore/QDate>
 #include <QtGui/QLineEdit>
 #include <QtGui/QValidator>
+#include <QtGui/QToolButton>
 
 /** Year selection widget.
 * @internal
@@ -38,23 +39,21 @@
     Q_OBJECT
 
 public:
-    KDatePickerPrivateYearSelector( const KCalendarSystem *calendar, const QDate \
                &currentDate, QWidget *parent = 0 );
-    int year();
-    void setYear( int year );
+    KDatePickerPrivateYearSelector( QToolButton *selectYear, QWidget *parent = 0 );
+    void setCalendar( const KCalendarSystem *calendar );
+    virtual QSize sizeHint() const;
 
-public Q_SLOTS:
-    void yearEnteredSlot();
+protected:
+    virtual void focusOutEvent( QFocusEvent *e );
 
 Q_SIGNALS:
-    void closeMe( int );
+    void yearEntered();
 
 protected:
     QIntValidator *val;
-    int result;
 
 private:
-    const KCalendarSystem *calendar;
-    QDate oldDate;
+    QToolButton *selectYear;
 
     Q_DISABLE_COPY( KDatePickerPrivateYearSelector )
 };
Index: kdatepicker.cpp
===================================================================
--- kdatepicker.cpp	(revision 739813)
+++ kdatepicker.cpp	(working copy)
@@ -3,6 +3,7 @@
     Copyright (C) 1997 Tim D. Gilman (tdgilman@best.org)
               (C) 1998-2001 Mirko Boehm (mirko@kde.org)
               (C) 2007 John Layt <john@layt.net>
+              (C) 2007 Jakob Petsovits <jpetso@gmx.at>
     This library is free software; you can redistribute it and/or
     modify it under the terms of the GNU Library General Public
     License as published by the Free Software Foundation; either
@@ -30,19 +31,20 @@
 #include <QtGui/QMenu>
 #include <QtGui/QPainter>
 #include <QtGui/QStyle>
+#include <QtGui/QLineEdit>
 #include <QtGui/QToolButton>
 #include <QtGui/QDoubleValidator>
+#include <QtGui/QClipboard>
 
 #include <kcalendarsystem.h>
 #include <kcombobox.h>
-#include <kdebug.h>
 #include <kdialog.h>
 #include <kglobal.h>
 #include <kicon.h>
 #include <kiconloader.h>
-#include <klineedit.h>
 #include <klocale.h>
 #include <knotification.h>
+#include <kmenu.h>
 
 #include "kdatepicker.moc"
 #include "kdatepicker_p.moc"
@@ -50,75 +52,52 @@
 // Week numbers are defined by ISO 8601
 // See http://www.merlyn.demon.co.uk/weekinfo.htm for details
 
-KDatePickerPrivateYearSelector::KDatePickerPrivateYearSelector(
-                                const KCalendarSystem *cal, const QDate \
                &currentDate, QWidget* parent )
-                              : QLineEdit( parent ), val( new QIntValidator( this ) \
), result( 0 ) +KDatePickerPrivateYearSelector::KDatePickerPrivateYearSelector( \
QToolButton *selectYear, QWidget* parent ) +          : QLineEdit( parent ), val( new \
QIntValidator( this ) ), selectYear( selectYear )  {
-    calendar = cal;
-    oldDate = currentDate;
-
-    QFont font;
-    font = KGlobalSettings::generalFont();
-    setFont( font );
-
+    connect( this, SIGNAL( editingFinished() ), SIGNAL( yearEntered() ) );
     setFrame( false );
-
-    val->setRange( calendar->year( calendar->earliestValidDate() ),
-                   calendar->year( calendar->latestValidDate() ) );
-    setValidator( val );
-
-    connect( this, SIGNAL( returnPressed() ), SLOT( yearEnteredSlot() ) );
+    setAlignment( Qt::AlignHCenter );
 }
 
-void KDatePickerPrivateYearSelector::yearEnteredSlot()
+void KDatePickerPrivateYearSelector::focusOutEvent( QFocusEvent * )
 {
-    bool ok;
-    int newYear;
-    QDate newDate;
-
-    // check if entered value is a number
-    newYear = text().toInt( &ok );
-    if( !ok ) {
-        KNotification::beep();
-        return;
-    }
-
-    // check if new year will lead to a valid date
-    if ( calendar->setYMD( newDate, newYear, calendar->month( oldDate ), \
                calendar->day( oldDate ) ) ) {
-        result = newYear;
-        emit( closeMe( 1 ) );
-    } else {
-        KNotification::beep();
-    }
-
+    emit yearEntered();
 }
 
-int KDatePickerPrivateYearSelector::year()
+void KDatePickerPrivateYearSelector::setCalendar( const KCalendarSystem *calendar )
 {
-    return result;
+    val->setRange( calendar->year( calendar->earliestValidDate() ),
+                   calendar->year( calendar->latestValidDate() ) );
+    setValidator( val );
 }
 
-void KDatePickerPrivateYearSelector::setYear( int year )
+QSize KDatePickerPrivateYearSelector::sizeHint() const
 {
-    setText( QString::number( year ) );
+    return ( selectYear == 0 ) ? QLineEdit::sizeHint() : selectYear->size();
 }
 
 class KDatePicker::KDatePickerPrivate
 {
 public:
     KDatePickerPrivate( KDatePicker *q ) :
-             q( q ), closeButton( 0L ), selectWeek( 0L ), todayButton( 0 ), \
navigationLayout( 0 ) +             q( q ), closeButton( 0 ), selectWeek( 0 ), \
copyDateButton( 0 ), +             todayButton( 0 ), navigationLayout( 0 ), \
yearForward( 0 ), +             yearBackward( 0 ), monthForward( 0 ), monthBackward( \
0 ), +             selectMonth( 0 ), selectYear( 0 ), yearLine( 0 ), table( 0 )
     {
     }
 
     void fillWeeksCombo();
     QDate validDateInYearMonth( int year, int month );
 
+public:
     /// the date table
     KDatePicker *q;
 
     QToolButton *closeButton;
-    KComboBox *selectWeek;
+    QComboBox *selectWeek;
+    QToolButton *copyDateButton;
     QToolButton *todayButton;
     QBoxLayout *navigationLayout;
 
@@ -132,12 +111,10 @@
     QToolButton *monthBackward;
     /// the button for selecting the month directly
     QToolButton *selectMonth;
-    /// the button for selecting the year directly
+    /// the button for showing the year selection line edit
     QToolButton *selectYear;
-    /// the line edit to enter the date directly
-    QLineEdit *line;
-    /// the validator for the line edit:
-    KDateValidator *val;
+    /// the line edit to enter the year directly
+    KDatePickerPrivateYearSelector *yearLine;
     /// the date table
     KDateTable *table;
     /// the widest month string in pixels:
@@ -177,8 +154,8 @@
 
     QDate lastDayOfYear;
     if ( ! q->calendar()->isValid( dayInLastMonthOfYear ) ||
-         ! q->calendar()->setYMD( lastDayOfYear, thisYear, lastMonthThisYear,
-                                  q->calendar()->daysInMonth( dayInLastMonthOfYear ) \
) ) { +         ! q->calendar()->setDate( lastDayOfYear, thisYear, lastMonthThisYear,
+                                   q->calendar()->daysInMonth( dayInLastMonthOfYear \
) ) ) {  lastDayOfYear = q->calendar()->latestValidDate();
     }
 
@@ -191,7 +168,7 @@
     // In particular covers the case of Gregorian where 1/1/-4713 is not a valid \
QDate  
     QDate day;
-    if ( ! q->calendar()->setYMD( day, thisYear, 1, 1 ) ) {
+    if ( ! q->calendar()->setDate( day, thisYear, 1, 1 ) ) {
         day = q->calendar()->earliestValidDate();
     }
 
@@ -214,7 +191,12 @@
             weekString += '*';
         }
 
-        selectWeek->addItem( weekString );
+        // when the week is selected, go to the same weekday as the one
+        // that is currently selected in the date table
+        QDate targetDate = q->calendar()->addDays( day,
+            q->calendar()->dayOfWeek( q->date() ) - q->calendar()->dayOfWeek( day )
+        );
+        selectWeek->addItem( weekString, targetDate );
 
         // make sure that the week of the lastDayOfYear is always inserted: in \
Chinese calendar  // system, this is not always the case
@@ -233,9 +215,9 @@
     // Try to create a valid date in this year and month
     // First try the first of the month, then try last of month
     if ( q->calendar()->isValid( year, month, 1 ) ) {
-        q->calendar()->setYMD( newDate, year, month, 1 );
+        q->calendar()->setDate( newDate, year, month, 1 );
     } else if ( q->calendar()->isValid( year, month + 1, 1 ) ) {
-        q->calendar()->setYMD( newDate, year, month, 1 );
+        q->calendar()->setDate( newDate, year, month, 1 );
         q->calendar()->addDays( newDate, -1 );
     } else {
         newDate = QDate::fromJulianDay( 0 );
@@ -257,7 +239,6 @@
 
 void KDatePicker::init( const QDate &date_ )
 {
-
     QBoxLayout * topLayout = new QVBoxLayout( this );
     topLayout->setSpacing( 0 );
     topLayout->setMargin( 0 );
@@ -266,45 +247,78 @@
     d->navigationLayout->setSpacing( 0 );
     d->navigationLayout->setMargin( 0 );
     topLayout->addLayout( d->navigationLayout );
-    d->navigationLayout->addStretch();
+
+    QBoxLayout * leftLayout = new QVBoxLayout();
+    leftLayout->setSpacing( 0 );
+    leftLayout->setMargin( 0 );
+    d->navigationLayout->addLayout( leftLayout );
+
+    QBoxLayout * yearLayout = new QHBoxLayout();
+    yearLayout->setSpacing( 0 );
+    yearLayout->setMargin( 0 );
+    leftLayout->addLayout( yearLayout );
+
+    yearLayout->addStretch();
     d->yearBackward = new QToolButton( this );
     d->yearBackward->setAutoRaise( true );
-    d->navigationLayout->addWidget( d->yearBackward );
-    d->monthBackward = new QToolButton( this );
-    d->monthBackward ->setAutoRaise( true );
-    d->navigationLayout->addWidget( d->monthBackward );
-    d->navigationLayout->addSpacing( KDialog::spacingHint() );
-
-    d->selectMonth = new QToolButton( this );
-    d->selectMonth ->setAutoRaise( true );
-    d->navigationLayout->addWidget( d->selectMonth );
+    yearLayout->addWidget( d->yearBackward );
     d->selectYear = new QToolButton( this );
-    d->selectYear->setCheckable( true );
     d->selectYear->setAutoRaise( true );
-    d->navigationLayout->addWidget( d->selectYear );
-    d->navigationLayout->addSpacing( KDialog::spacingHint() );
+    yearLayout->addWidget( d->selectYear );
+    d->yearLine = new KDatePickerPrivateYearSelector( d->selectYear, this );
+    d->yearLine->installEventFilter( this );
+    d->yearLine->setVisible( false );
+    yearLayout->addWidget( d->yearLine );
+    d->yearForward = new QToolButton( this );
+    d->yearForward->setAutoRaise( true );
+    yearLayout->addWidget( d->yearForward );
+    yearLayout->addStretch();
 
+    QBoxLayout * monthLayout = new QHBoxLayout();
+    monthLayout->setSpacing( 0 );
+    monthLayout->setMargin( 0 );
+    leftLayout->addLayout( monthLayout );
+
+    monthLayout->addStretch();
+    d->monthBackward = new QToolButton( this );
+    d->monthBackward->setAutoRaise( true );
+    monthLayout->addWidget( d->monthBackward );
+    d->selectMonth = new QToolButton( this );
+    d->selectMonth->setAutoRaise( true );
+    monthLayout->addWidget( d->selectMonth );
     d->monthForward = new QToolButton( this );
-    d->monthForward ->setAutoRaise( true );
-    d->navigationLayout->addWidget( d->monthForward );
-    d->yearForward = new QToolButton( this );
-    d->yearForward ->setAutoRaise( true );
-    d->navigationLayout->addWidget( d->yearForward );
-    d->navigationLayout->addStretch();
+    d->monthForward->setAutoRaise( true );
+    monthLayout->addWidget( d->monthForward );
+    monthLayout->addStretch();
 
-    d->line = new KLineEdit( this );
-    d->val = new KDateValidator( this );
-    d->table = new KDateTable( this );
-    d->fontsize = KGlobalSettings::generalFont().pointSize();
-    if ( d->fontsize == -1 ) {
-        d->fontsize = QFontInfo( KGlobalSettings::generalFont() ).pointSize();
-    }
+    QBoxLayout * rightLayout = new QVBoxLayout();
+    rightLayout->setSpacing( 0 );
+    rightLayout->setMargin( 0 );
+    d->navigationLayout->addLayout( rightLayout );
 
-    d->fontsize++; // Make a little bigger
+    QBoxLayout * weekLayout = new QHBoxLayout();
+    weekLayout->setSpacing( 0 );
+    weekLayout->setMargin( 0 );
+    rightLayout->addLayout( weekLayout );
 
-    d->selectWeek = new KComboBox( this );  // read only week selection
+    d->selectWeek = new QComboBox( this );  // read only week selection
+    d->selectWeek->setFocusPolicy( Qt::NoFocus );
+    weekLayout->addWidget( d->selectWeek );
+
+    QBoxLayout * buttonLayout = new QHBoxLayout();
+    buttonLayout->setMargin( 0 );
+    buttonLayout->setSpacing( 0 );
+    rightLayout->addLayout( buttonLayout );
+
+    buttonLayout->addStretch();
+    buttonLayout->addSpacing( KDialog::spacingHint() );
+
+    d->copyDateButton = new QToolButton( this );
+    d->copyDateButton->setAutoRaise( true );
+    buttonLayout->addWidget( d->copyDateButton );
     d->todayButton = new QToolButton( this );
-    d->todayButton->setIcon( KIcon( "go-jump-today" ) );
+    d->todayButton->setAutoRaise( true );
+    buttonLayout->addWidget( d->todayButton );
 
     d->yearForward->setToolTip( i18n( "Next year" ) );
     d->yearBackward->setToolTip( i18n( "Previous year" ) );
@@ -313,24 +327,35 @@
     d->selectWeek->setToolTip( i18n( "Select a week" ) );
     d->selectMonth->setToolTip( i18n( "Select a month" ) );
     d->selectYear->setToolTip( i18n( "Select a year" ) );
+    d->copyDateButton->setToolTip( i18n( "Copy date to clipboard" ) );
     d->todayButton->setToolTip( i18n( "Select the current day" ) );
 
-    // -----
-    setFontSize( d->fontsize );
-    d->line->setValidator( d->val );
-    d->line->installEventFilter( this );
     if ( QApplication::isRightToLeft() ) {
-        d->yearForward->setIcon( KIcon( QLatin1String( "arrow-left-double" ) ) );
-        d->yearBackward->setIcon( KIcon( QLatin1String( "arrow-right-double" ) ) );
-        d->monthForward->setIcon( KIcon( QLatin1String( "arrow-left" ) ) );
-        d->monthBackward->setIcon( KIcon( QLatin1String( "arrow-right" ) ) );
+        d->yearForward->setIcon( KIcon( "go-next-rtl" ) );
+        d->yearBackward->setIcon( KIcon( "go-previous-rtl" ) );
+        d->monthForward->setIcon( KIcon( "go-next-rtl" ) );
+        d->monthBackward->setIcon( KIcon( "go-previous-rtl" ) );
     } else {
-        d->yearForward->setIcon( KIcon( QLatin1String( "arrow-right-double" ) ) );
-        d->yearBackward->setIcon( KIcon( QLatin1String( "arrow-left-double" ) ) );
-        d->monthForward->setIcon( KIcon( QLatin1String( "arrow-right" ) ) );
-        d->monthBackward->setIcon( KIcon( QLatin1String( "arrow-left" ) ) );
+        d->yearForward->setIcon( KIcon( "go-next" ) );
+        d->yearBackward->setIcon( KIcon( "go-previous" ) );
+        d->monthForward->setIcon( KIcon( "go-next" ) );
+        d->monthBackward->setIcon( KIcon( "go-previous" ) );
     }
+    d->copyDateButton->setIcon( KIcon( "edit-copy" ) );
+    d->todayButton->setIcon( KIcon( "go-jump-today" ) );
 
+    d->table = new KDateTable( this );
+    setFocusProxy( d->table );
+    topLayout->addWidget( d->table );
+
+    // -----
+    d->fontsize = KGlobalSettings::generalFont().pointSize();
+    if ( d->fontsize == -1 ) {
+        d->fontsize = QFontInfo( KGlobalSettings::generalFont() ).pointSize();
+    }
+    d->fontsize++; // Make a little bigger
+    setFontSize( d->fontsize );
+
     connect( d->table, SIGNAL( dateChanged( const QDate& ) ), SLOT( dateChangedSlot( \
                const QDate& ) ) );
     connect( d->table, SIGNAL( tableClicked() ), SLOT( tableClickedSlot() ) );
     connect( d->monthForward, SIGNAL( clicked() ), SLOT( monthForwardClicked() ) );
@@ -338,25 +363,14 @@
     connect( d->yearForward, SIGNAL( clicked() ), SLOT( yearForwardClicked() ) );
     connect( d->yearBackward, SIGNAL( clicked() ), SLOT( yearBackwardClicked() ) );
     connect( d->selectWeek, SIGNAL( activated( int ) ), SLOT( weekSelected( int ) ) \
); +    connect( d->copyDateButton, SIGNAL( clicked() ), SLOT( \
                copyDateButtonClicked() ) );
     connect( d->todayButton, SIGNAL( clicked() ), SLOT( todayButtonClicked() ) );
     connect( d->selectMonth, SIGNAL( clicked() ), SLOT( selectMonthClicked() ) );
-    connect( d->selectYear, SIGNAL( toggled( bool ) ), SLOT( selectYearClicked() ) \
                );
-    connect( d->line, SIGNAL( returnPressed() ), SLOT( lineEnterPressed() ) );
-    d->table->setFocus();
+    connect( d->selectYear, SIGNAL( clicked() ), SLOT( selectYearClicked() ) );
+    connect( d->yearLine, SIGNAL( yearEntered() ), SLOT( lineEnterPressed() ) );
 
-
-    topLayout->addWidget( d->table );
-
-    QBoxLayout * bottomLayout = new QHBoxLayout();
-    bottomLayout->setMargin( 0 );
-    bottomLayout->setSpacing( 0 );
-    topLayout->addLayout( bottomLayout );
-
-    bottomLayout->addWidget( d->todayButton );
-    bottomLayout->addWidget( d->line );
-    bottomLayout->addWidget( d->selectWeek );
-
     d->table->setDate( date_ );
+    d->yearLine->setCalendar( calendar() );
     dateChangedSlot( date_ );  // needed because table emits changed only when \
newDate != oldDate  }
 
@@ -389,7 +403,6 @@
 
 void KDatePicker::dateChangedSlot( const QDate &date_ )
 {
-    d->line->setText( calendar()->formatDate( date_, KLocale::ShortDate ) );
     d->selectMonth->setText( calendar()->monthName( date_, KCalendarSystem::LongName \
) );  d->fillWeeksCombo();
 
@@ -400,13 +413,16 @@
     // the earliestValidDate as the first day.
     // In particular covers the case of Gregorian where 1/1/-4713 is not a valid \
QDate  QDate day;
-    if ( ! calendar()->setYMD( firstDay, calendar()->year( date_ ), 1, 1 ) ) {
+    if ( ! calendar()->setDate( firstDay, calendar()->year( date_ ), 1, 1 ) ) {
         firstDay = calendar()->earliestValidDate();
     }
     d->selectWeek->setCurrentIndex( ( calendar()->dayOfYear( date_ ) + \
calendar()->dayOfWeek( firstDay ) - 2 ) /  calendar()->daysInWeek( date_ ) );
-    d->selectYear->setText( calendar()->yearString( date_, \
KCalendarSystem::LongFormat ) );  
+    QString year = calendar()->yearString( date_, KCalendarSystem::LongFormat );
+    d->selectYear->setText( year );
+    d->yearLine->setText( year );
+
     emit( dateChanged( date_ ) );
 }
 
@@ -448,6 +464,7 @@
     if ( ! setDate( calendar()->addMonths( date(), 1 ) ) ) {
         KNotification::beep();
     }
+    d->table->setFocus();
 }
 
 void KDatePicker::monthBackwardClicked()
@@ -455,6 +472,7 @@
     if ( ! setDate( calendar()->addMonths( date(), -1 ) ) ) {
         KNotification::beep();
     }
+    d->table->setFocus();
 }
 
 void KDatePicker::yearForwardClicked()
@@ -462,6 +480,7 @@
     if ( ! setDate( calendar()->addYears( d->table->date(), 1 ) ) ) {
         KNotification::beep();
     }
+    d->table->setFocus();
 }
 
 void KDatePicker::yearBackwardClicked()
@@ -469,32 +488,23 @@
     if ( ! setDate( calendar()->addYears( d->table->date(), -1 ) ) ) {
         KNotification::beep();
     }
+    d->table->setFocus();
 }
 
-void KDatePicker::weekSelected( int week )
+void KDatePicker::weekSelected( int index )
 {
-    QDate newDate;
+    QDate targetDay = d->selectWeek->itemData( index ).toDateTime().date();
 
-    // First obtain a date that has weekday number of 1, either in this week or next \
                week, whichevers valid
-    newDate = calendar()->addDays( date(), 1 - calendar()->dayOfWeek( date() ) );
-    if ( ! calendar()->isValid( newDate ) ) {
-        newDate = calendar()->addDays( date(),
-                  1 - calendar()->dayOfWeek( date() ) + calendar()->daysInWeek( \
                date() ) );
-    }
-
-    // If we have a valid date, then add/subtract the number of days difference \
                between the week numbers
-    if ( calendar()->isValid( newDate ) ) {
-        newDate = calendar()->addDays( newDate,
-                 ( week - calendar()->weekNumber( newDate ) ) * \
                calendar()->daysInWeek( date() ) );
-    }
-
-    if ( ! setDate( newDate ) ) {
+    if ( ! setDate( targetDay ) ) {
         KNotification::beep();
     }
+    d->table->setFocus();
 }
 
 void KDatePicker::selectMonthClicked()
 {
+    d->table->setFocus();
+
     QMenu popup( d->selectMonth );
 
     // Populate the pick list with all the month names, this may change by year
@@ -521,10 +531,10 @@
     // If we have succeeded in creating a date in the new month, then try to create \
the new date,  // checking we don't set a day after the last day of the month
     if ( calendar()->isValid( newDate ) ) {
-        calendar()->setYMD( newDate,
-                            calendar()->year( date() ),
-                            item->data().toInt(),
-                            qMin( calendar()->day( date() ), \
calendar()->daysInMonth( newDate ) ) ); +        calendar()->setDate( newDate,
+            calendar()->year( date() ), item->data().toInt(),
+            qMin( calendar()->day( date() ), calendar()->daysInMonth( newDate ) )
+        );
     }
 
     // Set the date, if it's invalid in any way then alert user and don't update
@@ -535,43 +545,12 @@
 
 void KDatePicker::selectYearClicked()
 {
-    QDate newDate;
+    d->yearLine->adjustSize();
+    d->yearLine->selectAll();
 
-    if ( !d->selectYear->isChecked() ) {
-        return;
-    }
-
-    KPopupFrame *popup = new KPopupFrame( this );
-    KDatePickerPrivateYearSelector *picker = new KDatePickerPrivateYearSelector( \
                calendar(), date(), popup );
-    picker->resize( picker->sizeHint() );
-    picker->setYear( calendar()->year( date() ) );
-    picker->selectAll();
-    popup->setMainWidget( picker );
-    connect( picker, SIGNAL( closeMe( int ) ), popup, SLOT( close( int ) ) );
-    picker->setFocus();
-
-    if( popup->exec( d->selectYear->mapToGlobal( QPoint( 0, d->selectMonth->height() \
                ) ) ) ) {
-        // We need to create a valid date in the year/month selected so we can find \
                out how many
-        // days are in the month.
-        newDate = d->validDateInYearMonth( picker->year(), calendar()->month( date() \
                ) );
-
-        // If we have succeeded in creating a date in the new month, then try to \
                create the new
-        // date, checking we don't set a day after the last day of the month
-        if ( calendar()->isValid( newDate ) ) {
-            calendar()->setYMD( newDate,
-                                picker->year(),
-                                calendar()->month( date() ),
-                                qMin( calendar()->day( date() ), \
                calendar()->daysInMonth( newDate ) ) );
-        }
-    }
-
-    // Set the date, if it's invalid in any way then alert user and don't update
-    if ( ! setDate( newDate ) ) {
-        KNotification::beep();
-    }
-
-    d->selectYear->setChecked( false );
-    delete popup;
+    d->selectYear->setVisible( false );
+    d->yearLine->setVisible( true );
+    d->yearLine->setFocus();
 }
 
 
@@ -580,9 +559,10 @@
 void KDatePicker::setEnabled( bool enable )
 {
     QWidget * widgets[] = {
-                              d->yearForward, d->yearBackward, d->monthForward, \
                d->monthBackward,
-                              d->selectMonth, d->selectYear,
-                              d->line, d->table, d->selectWeek, d->todayButton
+                              d->yearForward, d->yearBackward, d->monthForward,
+                              d->monthBackward, d->selectMonth, d->selectYear,
+                              d->yearLine, d->selectWeek, d->copyDateButton,
+                              d->todayButton, d->table
                           };
     const int Size = sizeof( widgets ) / sizeof( widgets[0] );
     int count;
@@ -590,6 +570,7 @@
     for( count = 0; count < Size; ++count ) {
         widgets[count]->setEnabled( enable );
     }
+    d->table->setFocus();
 }
 
 KDateTable *KDatePicker::dateTable() const
@@ -599,19 +580,71 @@
 
 void KDatePicker::lineEnterPressed()
 {
-    QDate newDate = calendar()->readDate( d->line->text() );
+    bool ok;
+    int newYear;
+    QDate newDate;
 
+    // check if entered value is a number
+    newYear = d->yearLine->text().toInt( &ok );
+    if( !ok ) {
+        KNotification::beep();
+        return;
+    }
+
+    // We need to create a valid date in the year/month selected so we can find out \
how many +    // days are in the month.
+    newDate = d->validDateInYearMonth( newYear, calendar()->month( date() ) );
+
+    // If we have succeeded in creating a date in the new month, then try to create \
the new +    // date, checking we don't set a day after the last day of the month
     if ( calendar()->isValid( newDate ) ) {
-        emit( dateEntered( newDate ) );
-        setDate( newDate );
+        calendar()->setDate( newDate,
+            newYear, calendar()->month( date() ),
+            qMin( calendar()->day( date() ), calendar()->daysInMonth( newDate ) )
+        );
+    }
+
+    // Set the date, if it's invalid in any way then alert user and don't update
+    if ( setDate( newDate ) ) {
+        d->yearLine->setVisible( false );
+        d->selectYear->setVisible( true );
+        d->table->setFocus();
     } else {
         KNotification::beep();
     }
 }
 
+void KDatePicker::copyDateButtonClicked()
+{
+    d->table->setFocus();
+
+    QString items[] = {
+        calendar()->formatDate( date(), KLocale::ShortDate ),
+        calendar()->formatDate( date(), KLocale::LongDate ),
+    };
+    const int NoOfItems = sizeof( items ) / sizeof( items[0] );
+
+    KMenu popup( d->copyDateButton );
+    popup.addTitle( i18n( "Copy to clipboard" ) );
+
+    for ( int i = 0; i < NoOfItems; i++ ) {
+        popup.addAction( items[i] )->setData(i);
+    }
+
+    QAction *item;
+    QPoint openPos = QPoint( 0, d->copyDateButton->size().height() );
+    if ( ( item = popup.exec( d->copyDateButton->mapToGlobal( openPos ) ) ) == 0 ) {
+        return; // cancelled
+    }
+
+    QString data = items[item->data().toInt()];
+    QApplication::clipboard()->setText( data );
+}
+
 void KDatePicker::todayButtonClicked()
 {
     setDate( QDate::currentDate() );
+    d->table->setFocus();
 }
 
 QSize KDatePicker::sizeHint() const
@@ -621,22 +654,26 @@
 
 void KDatePicker::setFontSize( int s )
 {
-    QWidget * buttons[] = {
+    QWidget * widgets[] = {
                               d->selectMonth,
                               d->selectYear,
+                              d->yearLine
                           };
-    const int NoOfButtons = sizeof( buttons ) / sizeof( buttons[0] );
+    const int NoOfButtons = sizeof( widgets ) / sizeof( widgets[0] );
     int count;
     QFont font;
     QRect r;
     // -----
     d->fontsize = s;
     for( count = 0; count < NoOfButtons; ++count ) {
-        font = buttons[count]->font();
+        font = widgets[count]->font();
         font.setPointSize( s );
-        buttons[count]->setFont( font );
+        widgets[count]->setFont( font );
     }
+    d->table->setFontSize( s );
+
     QFontMetrics metrics( d->selectMonth->fontMetrics() );
+    QString longestMonth;
 
     for ( int i = 1; ; ++i ) {
         QString str = calendar()->monthName( i, calendar()->year( date() ), \
KCalendarSystem::LongName ); @@ -644,26 +681,32 @@
             break;
         }
         r = metrics.boundingRect( str );
-        d->maxMonthRect.setWidth( qMax( r.width(), d->maxMonthRect.width() ) );
-        d->maxMonthRect.setHeight( qMax( r.height(),  d->maxMonthRect.height() ) );
+
+        if ( r.width() > d->maxMonthRect.width() ) {
+            d->maxMonthRect.setWidth( r.width() );
+            longestMonth = str;
+        }
+        if ( r.height() > d->maxMonthRect.height() ) {
+            d->maxMonthRect.setHeight( r.height() );
+        }
     }
 
     QStyleOptionToolButton opt;
+    opt.initFrom( d->selectMonth );
+    opt.text      = longestMonth;
 
-    // stolen from KToolBarButton
-    opt.init( this );
-    opt.font      = d->selectMonth->font();
-    opt.icon      = d->selectMonth->icon();
-    opt.text      = d->selectMonth->text();
-    opt.features  = d->selectMonth->menu() ? QStyleOptionToolButton::Menu : \
                QStyleOptionToolButton::None; //### FIXME: delay?
-    opt.subControls       = QStyle::SC_All;
-    opt.activeSubControls = 0; //### FIXME: !!
+    // stolen from QToolButton
+    QSize textSize = metrics.size( Qt::TextShowMnemonic, longestMonth );
+    textSize.setWidth( textSize.width() + metrics.width( QLatin1Char(' ') ) * 2 );
+    int w = textSize.width();
+    int h = textSize.height();
+    opt.rect.setHeight( h ); // PM_MenuButtonIndicator depends on the height
 
-    QSize metricBound = style()->sizeFromContents( QStyle::CT_ToolButton, &opt,
-                                                   d->maxMonthRect, d->selectMonth \
); +    QSize metricBound = style()->sizeFromContents(
+        QStyle::CT_ToolButton, &opt, QSize( w, h ), d->selectMonth
+    ).expandedTo( QApplication::globalStrut() );
+
     d->selectMonth->setMinimumSize( metricBound );
-
-    d->table->setFontSize( s );
 }
 
 int KDatePicker::fontSize() const
@@ -684,7 +727,7 @@
         d->navigationLayout->addSpacing( KDialog::spacingHint() );
         d->navigationLayout->addWidget( d->closeButton );
         d->closeButton->setToolTip( i18nc( "@action:button", "Close" ) );
-        d->closeButton->setIcon( SmallIcon( "list-remove" ) );
+        d->closeButton->setIcon( SmallIcon( "window-close" ) );
         connect( d->closeButton, SIGNAL( clicked() ),
                  topLevelWidget(), SLOT( close() ) );
     } else {


["kfind-fix-datecombo.diff" (text/x-diff)]

Index: kdatecombo.cpp
===================================================================
--- kdatecombo.cpp	(revision 739815)
+++ kdatecombo.cpp	(working copy)
@@ -99,7 +99,6 @@
       getDate(& tempDate);
       datePicker->setDate(tempDate);
       popupFrame->popup(mapToGlobal(QPoint(0, height())));
-      //datePicker->setFocus();
     }
   }
 }
@@ -119,19 +118,14 @@
   else if ( e->type() == QEvent::KeyRelease )
   {
       QKeyEvent *k = (QKeyEvent *)e;
-      //Press return == pick selected date and close the combo
-      if((k->key()==Qt::Key_Return)||(k->key()==Qt::Key_Enter))
-      {
-        dateEnteredEvent(datePicker->date());
-        return true;
-      }
-      else if (k->key()==Qt::Key_Escape)
-      {
+
+      if (k->key()==Qt::Key_Escape) {
         popupFrame->hide();
         return true;
       }
-      else
+      else {
         return false;
+      }
   }
 
   return false;


_______________________________________________
kde-usability mailing list
kde-usability@kde.org
https://mail.kde.org/mailman/listinfo/kde-usability


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic