From kde-usability Thu Nov 22 10:05:53 2007 From: Jakob Petsovits Date: Thu, 22 Nov 2007 10:05:53 +0000 To: kde-usability Subject: KDatePicker improvements Message-Id: <200711221105.54140.jpetso () gmx ! at> X-MARC-Message: https://marc.info/?l=kde-usability&m=119572601811622 MIME-Version: 1 Content-Type: multipart/mixed; boundary="--Boundary-00=_CSVRH2yi1yO66dE" --Boundary-00=_CSVRH2yi1yO66dE Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Content-Disposition: inline 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 --Boundary-00=_CSVRH2yi1yO66dE Content-Type: text/x-diff; charset="utf-8"; name="kdatepicker-usability-attempt-try3.diff" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="kdatepicker-usability-attempt-try3.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 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 -#include #include #include +#include /** Year selection widget. * @internal @@ -38,23 +39,21 @@ Q_OBJECT public: - KDatePickerPrivateYearSelector( const KCalendarSystem *calendar, const QDate ¤tDate, 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 + (C) 2007 Jakob Petsovits 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 #include #include +#include #include #include +#include #include #include -#include #include #include #include #include -#include #include #include +#include #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 ¤tDate, 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 { --Boundary-00=_CSVRH2yi1yO66dE Content-Type: text/x-diff; charset="utf-8"; name="kfind-fix-datecombo.diff" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="kfind-fix-datecombo.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; --Boundary-00=_CSVRH2yi1yO66dE Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: inline _______________________________________________ kde-usability mailing list kde-usability@kde.org https://mail.kde.org/mailman/listinfo/kde-usability --Boundary-00=_CSVRH2yi1yO66dE--