diff --git a/conf/preferencesdialog.h b/conf/preferencesdialog.h --- a/conf/preferencesdialog.h +++ b/conf/preferencesdialog.h @@ -32,6 +32,7 @@ public: PreferencesDialog( QWidget * parent, KConfigSkeleton * config, Okular::EmbedMode embedMode ); + void switchToAccessibilityPage(); void switchToAnnotationsPage(); protected: @@ -52,6 +53,7 @@ DlgDebug * m_debug; #endif + KPageWidgetItem * m_accessibilityPage; KPageWidgetItem * m_annotationsPage; }; diff --git a/conf/preferencesdialog.cpp b/conf/preferencesdialog.cpp --- a/conf/preferencesdialog.cpp +++ b/conf/preferencesdialog.cpp @@ -29,6 +29,7 @@ m_general = new DlgGeneral( this, embedMode ); m_performance = new DlgPerformance( this ); m_accessibility = new DlgAccessibility( this ); + m_accessibilityPage = nullptr; m_presentation = nullptr; m_annotations = nullptr; m_annotationsPage = nullptr; @@ -38,7 +39,7 @@ #endif addPage( m_general, i18n("General"), QStringLiteral("okular"), i18n("General Options") ); - addPage( m_accessibility, i18n("Accessibility"), QStringLiteral("preferences-desktop-accessibility"), i18n("Accessibility Reading Aids") ); + m_accessibilityPage = addPage( m_accessibility, i18n("Accessibility"), QStringLiteral("preferences-desktop-accessibility"), i18n("Accessibility Reading Aids") ); addPage( m_performance, i18n("Performance"), QStringLiteral("preferences-system-performance"), i18n("Performance Tuning") ); if( embedMode == Okular::ViewerWidgetMode ) { @@ -61,6 +62,12 @@ setHelp(QStringLiteral("configure"), QStringLiteral("okular")); } +void PreferencesDialog::switchToAccessibilityPage() +{ + if ( m_accessibilityPage ) + setCurrentPage( m_accessibilityPage ); +} + void PreferencesDialog::switchToAnnotationsPage() { if ( m_annotationsPage ) diff --git a/part.h b/part.h --- a/part.h +++ b/part.h @@ -255,6 +255,8 @@ void moveSplitter( const int sideWidgetSize ); + void slotAccessibilityPreferences(); + private: bool aboutToShowContextMenu(QMenu *menu, QAction *action, QMenu *contextMenu); void showMenu(const Okular::Page *page, const QPoint &point, const QString &bookmarkTitle = QString(), const Okular::DocumentViewport &vp = DocumentViewport(), bool showTOCActions = false); diff --git a/part.cpp b/part.cpp --- a/part.cpp +++ b/part.cpp @@ -623,7 +623,7 @@ if ( m_embedMode != PrintPreviewMode ) { // now set up actions that are required for all remaining modes - m_pageView->setupViewerActions( actionCollection() ); + m_pageView->setupViewerActions( actionCollection(), this ); // and if we are not in viewer mode, we want the full GUI if ( m_embedMode != ViewerWidgetMode ) { @@ -2945,6 +2945,17 @@ m_pageView->slotSetChangeColors(active); } +void Part::slotAccessibilityPreferences() +{ + // Create dialog + PreferencesDialog * dialog = new PreferencesDialog( m_pageView, Okular::Settings::self(), m_embedMode ); + dialog->setAttribute( Qt::WA_DeleteOnClose ); + + // Show it + dialog->switchToAccessibilityPage(); + dialog->show(); +} + void Part::slotAnnotationPreferences() { // Create dialog diff --git a/ui/pageview.h b/ui/pageview.h --- a/ui/pageview.h +++ b/ui/pageview.h @@ -34,6 +34,7 @@ namespace Okular { class Action; +class Part; class Document; class DocumentViewport; class FormFieldSignature; @@ -71,7 +72,7 @@ // create actions that interact with this widget void setupBaseActions( KActionCollection * collection ); - void setupViewerActions( KActionCollection * collection ); + void setupViewerActions( KActionCollection * collection, Okular::Part * part ); void setupActions( KActionCollection * collection ); void updateActionState( bool docHasPages, bool docChanged, bool docHasFormWidgets ); @@ -129,6 +130,11 @@ void slotToggleChangeColors(); void slotSetChangeColors(bool active); + + /** + * Sets the color mode (render mode) to the one represented by @param action. + */ + void slotSetChangeColorsMode(QAction * action); void slotSelectPage(); diff --git a/ui/pageview.cpp b/ui/pageview.cpp --- a/ui/pageview.cpp +++ b/ui/pageview.cpp @@ -63,6 +63,7 @@ // local includes #include "debug_ui.h" +#include "part.h" #include "formwidgets.h" #include "pageviewutils.h" #include "pagepainter.h" @@ -120,6 +121,133 @@ { } +/** + * @brief A KActionMenu, with puts a checkable QToolButton into the toolbar, without being checkable itself. + * + * By default, the toolbar button will have the popupMode @c MenuButtonPopup, so it can be used as a checkable menu button. + * Clicking the middle of the button will trigger its default action. + * When the default action becomes checked, the button will be painted pressed. + * Clicking the arrow in the corner of the button, will open the menu. + * + * The purpose is the Color Mode menu, which shall have a checkable toolbar button, + * without being checkable itself when plugged as submenu into the menubar. + * + * setDefaultAction() is reimplemented, so the default action is passed to the toolbar button, + * whitout making this menu checkable. + * Clicking the toolbar button will trigger the default action, + * and setting the default action checked will just paint the toolbar button pressed. + * Instead, clicking the toolbar button will trigger the default action. + * + * @note + * Besides @c checkable and @c checked, any other parameters will also be disconnected. + * This means, they are not synchonized with any default action. + * Parameters, which are usually synchronized with the default action include: + * - @c enabled + * - @c font + * - @c icon + * - @c statusTip + * - @c text + * These parameters would have affected the toolbar button, so change the parameters of the default action instead: + * - @c popupMode + * - @c toolTip + * - @c whatsThis + * Because KActionMenu inherits QAction, all getters and setters for these parameters are still available. + */ +class CheckableActionMenu : public KActionMenu +{ +public: + explicit CheckableActionMenu( QWidget * parent ); + CheckableActionMenu( const QString &text, QWidget * parent ); + /** + * These are the usual constructors for KActionMenu. + * + * @note + * @p text and @p icon are used only if this menu is a submenu in another menu. + * To set the text and icon of the toolbar button, modify the default action. + * + * @param text as visible inside other menus. + * @param icon as visible inside other menus. + */ + CheckableActionMenu( const QIcon &icon, const QString &text, QWidget * parent ); + + QWidget * createWidget( QWidget * parent ) override; + + /** + * Returns the default action of the toolbar button. + * This menu does not have an own default action, the button handles that itself. + */ + QAction * defaultAction(); + + /** + * Sets the default action of the toolbar button. + * + * @param action will be triggered when the button is clicked, without triggering this menu. + * By purpose of this class, @p action should be checkable. + */ + void setDefaultAction( QAction * action ); + +private: + QToolButton * m_button; + QAction * m_defaultAction; +}; + +CheckableActionMenu::CheckableActionMenu( QWidget * parent ) + : KActionMenu( parent ), m_button( nullptr ), m_defaultAction( nullptr ) +{ +} + +CheckableActionMenu::CheckableActionMenu( const QString &text, QWidget * parent ) + : KActionMenu( text, parent ), m_button( nullptr ), m_defaultAction( nullptr ) +{ +} + +CheckableActionMenu::CheckableActionMenu( const QIcon &icon, const QString &text, QWidget * parent ) + : KActionMenu( icon, text, parent ), m_button( nullptr ), m_defaultAction( nullptr ) +{ +} + +QWidget * CheckableActionMenu::createWidget( QWidget * parent ) +{ + QToolButton * button = qobject_cast< QToolButton * >( KActionMenu::createWidget( parent ) ); + if ( !button ) { + // This function is used to add a button into the toolbar. + // KActionMenu will plug itself as QToolButton. + // So, if no QToolButton was returned, this was not called the intended way. + return button; + } + + m_button = button; + + m_button->setPopupMode( QToolButton::MenuButtonPopup ); + + if ( m_defaultAction ) { + setDefaultAction( m_defaultAction ); + } + + return button; +} + +void CheckableActionMenu::setDefaultAction( QAction * action ) +{ + if ( m_button ) { + m_button->setDefaultAction( action ); + m_button->removeAction( this ); + + // The button shall continue to use this menu as popup menu, even if the default action is changed. + m_button->setMenu( menu() ); + } + + m_defaultAction = action; + + // TODO The QToolButton will check whether the default action has a menu and will show that instead. + // Probably. See qtoolbutton.cpp:725 +} + +QAction * CheckableActionMenu::defaultAction() +{ + return m_defaultAction; +} + // structure used internally by PageView for data storage class PageViewPrivate { @@ -229,6 +357,8 @@ KToggleAction * aZoomAutoFit; KActionMenu * aViewMode; KToggleAction * aViewContinuous; + CheckableActionMenu * aColorModeMenu; + QAction * aEnableColorModes; QAction * aPrevAction; QAction * aToggleForms; QAction * aSpeakDoc; @@ -488,7 +618,7 @@ d->aZoomActual->setText(i18n("Zoom to 100%")); } -void PageView::setupViewerActions( KActionCollection * ac ) +void PageView::setupViewerActions( KActionCollection * ac, Okular::Part * part ) { d->actionCollection = ac; @@ -609,9 +739,65 @@ mz->setActionGroup( d->mouseModeActionGroup ); mz->setChecked( Okular::Settings::mouseMode() == Okular::Settings::EnumMouseMode::Zoom ); - QAction * aToggleChangeColors = new QAction(i18n("&Toggle Change Colors"), this); - ac->addAction(QStringLiteral("toggle_change_colors"), aToggleChangeColors ); - connect( aToggleChangeColors, &QAction::triggered, this, &PageView::slotToggleChangeColors ); + // Change Colors action menu + d->aColorModeMenu = new CheckableActionMenu( QIcon::fromTheme( QStringLiteral("color-management") ), i18n( "&Color Mode" ), this ); + // Add the menu to the action collection, so it can be plugged into the tool bar. + // It will not confuse the Configure Shortcuts dialog, because KActionMenu does not accept a shortcut. + ac->addAction( QStringLiteral( "color_mode_menu"), d->aColorModeMenu ); + + d->aEnableColorModes = new QAction( i18nc( "@item:inmenu color mode", "&Change Colors" ), this ); + d->aEnableColorModes->setCheckable( true ); + d->aEnableColorModes->setChecked( Okular::SettingsCore::changeColors() ); + ac->addAction( QStringLiteral( "enable_color_modes"), d->aEnableColorModes ); + d->aColorModeMenu->addAction( d->aEnableColorModes ); + d->aColorModeMenu->setDefaultAction( d->aEnableColorModes ); + connect( d->aEnableColorModes, &QAction::triggered, this, &PageView::slotSetChangeColors ); + connect( d->aEnableColorModes, &QAction::triggered, [=] (bool checked) { qDebug() << "enable_color_modes triggered, now" << checked; } ); + connect( d->aColorModeMenu, &QAction::triggered, [=] (bool checked) { qDebug() << "color_mode_menu triggered, now" << checked; } ); + + d->aColorModeMenu->addSeparator(); + QActionGroup * cmGroup = new QActionGroup( this ); + auto addColorMode = [=] ( QAction * a, QString name, Okular::SettingsCore::EnumRenderMode::type id ) { + a->setCheckable( true ); + a->setData( int( id ) ); + d->aColorModeMenu->addAction( a ); + ac->addAction( name, a ); + cmGroup->addAction( a ); + }; + QAction * tmpAction = new QAction( i18nc( "@item:inmenu color mode", "&Invert Colors" ), this ); + addColorMode( tmpAction, + "color_mode_inverted", Okular::SettingsCore::EnumRenderMode::Inverted ); + connect( tmpAction, &QAction::triggered, [=] (bool checked) { qDebug() << "color_mode_inverted triggered, now" << checked; } ); + tmpAction = new QAction( i18nc( "@item:inmenu color mode", "Change &Paper Color" ), this ); + addColorMode( tmpAction, + "color_mode_paper", Okular::SettingsCore::EnumRenderMode::Paper ); + connect( tmpAction, &QAction::triggered, [=] (bool checked) { qDebug() << "color_mode_paper triggered, now" << checked; } ); + tmpAction = new QAction( i18nc( "@item:inmenu color mode", "Change &Dark && Light Colors" ), this ); + addColorMode( tmpAction, + "color_mode_recolor", Okular::SettingsCore::EnumRenderMode::Recolor ); + connect( tmpAction, &QAction::triggered, [=] (bool checked) { qDebug() << "color_mode_recolor triggered, now" << checked; } ); + tmpAction = new QAction( i18nc( "@item:inmenu color mode", "Convert to &Black && White" ), this ); + addColorMode( tmpAction, + "color_mode_black_white", Okular::SettingsCore::EnumRenderMode::BlackWhite ); + connect( tmpAction, &QAction::triggered, [=] (bool checked) { qDebug() << "color_mode_black_white triggered, now" << checked; } ); + + const int rm = Okular::SettingsCore::renderMode(); + for ( QAction * a : cmGroup->actions() ) + { + if ( a->data().toInt() == rm ) + { + a->setChecked( true ); + break; + } + } + + connect( cmGroup, &QActionGroup::triggered, this, &PageView::slotSetChangeColorsMode ); + + d->aColorModeMenu->addSeparator(); + QAction * aConfigure = new QAction( QIcon::fromTheme( QStringLiteral("configure") ), + i18nc( "@item:inmenu color mode", "C&onfigure..." ), this ); + d->aColorModeMenu->addAction( aConfigure ); + connect( aConfigure, &QAction::triggered, part, &Okular::Part::slotAccessibilityPreferences ); } // WARNING: 'setupViewerActions' must have been called before this method @@ -5507,13 +5693,30 @@ Okular::SettingsCore::setChangeColors(active); Okular::Settings::self()->save(); viewport()->update(); + d->aEnableColorModes->setChecked( active ); + d->aColorModeMenu->setChecked( active ); } void PageView::slotToggleChangeColors() { slotSetChangeColors( !Okular::SettingsCore::changeColors() ); } +void PageView::slotSetChangeColorsMode( QAction * action ) +{ + const int rm = action->data().toInt(); + if ( Okular::SettingsCore::renderMode() == rm ) + { + // Assigning a shortcut to a color mode only makes sense if it can toggle the feature on and off. + slotToggleChangeColors(); + } + else + { + Okular::SettingsCore::setRenderMode( rm ); + slotSetChangeColors( true ); + } +} + void PageView::slotFitWindowToPage() { const PageViewItem *currentPageItem = nullptr;