Improve code quality

This commit is contained in:
Felix Ernst 2022-04-25 12:52:05 +02:00
parent 8e55f2c240
commit 78cffd2979
15 changed files with 147 additions and 79 deletions

View file

@ -125,7 +125,7 @@ DolphinMainWindow::DolphinMainWindow() :
setComponentName(QStringLiteral("dolphin"), QGuiApplication::applicationDisplayName()); setComponentName(QStringLiteral("dolphin"), QGuiApplication::applicationDisplayName());
setObjectName(QStringLiteral("Dolphin#")); setObjectName(QStringLiteral("Dolphin#"));
// setStateConfigGroup("State"); setStateConfigGroup("State");
connect(&DolphinNewFileMenuObserver::instance(), &DolphinNewFileMenuObserver::errorMessage, connect(&DolphinNewFileMenuObserver::instance(), &DolphinNewFileMenuObserver::errorMessage,
this, &DolphinMainWindow::showErrorMessage); this, &DolphinMainWindow::showErrorMessage);
@ -1675,8 +1675,7 @@ void DolphinMainWindow::setupActions()
QAction *toggleSelectionModeAction = actionCollection()->addAction(QStringLiteral("toggle_selection_mode")); QAction *toggleSelectionModeAction = actionCollection()->addAction(QStringLiteral("toggle_selection_mode"));
// i18n: This action toggles a selection mode. // i18n: This action toggles a selection mode.
toggleSelectionModeAction->setText(i18nc("@action:inmenu", "Select Files and Folders")); toggleSelectionModeAction->setText(i18nc("@action:inmenu", "Select Files and Folders"));
// i18n: Opens a selection mode for selecting files/folders and later selecting an action that acts on them. // i18n: Opens a selection mode for selecting files/folders.
// So in a way "Select" here is used to mean both "Select files" and also "Select what to do" but mostly the first.
// The text is kept so unspecific because it will be shown on the toolbar where space is at a premium. // The text is kept so unspecific because it will be shown on the toolbar where space is at a premium.
toggleSelectionModeAction->setIconText(i18nc("@action:intoolbar", "Select")); toggleSelectionModeAction->setIconText(i18nc("@action:intoolbar", "Select"));
toggleSelectionModeAction->setWhatsThis(xi18nc("@info:whatsthis", "<para>This application only knows which files or folders should be acted on if they are" toggleSelectionModeAction->setWhatsThis(xi18nc("@info:whatsthis", "<para>This application only knows which files or folders should be acted on if they are"

View file

@ -22,10 +22,13 @@ void ActionTextHelper::registerTextWhenNothingIsSelected(QAction *action, QStrin
void ActionTextHelper::textsWhenNothingIsSelectedEnabled(bool enabled) void ActionTextHelper::textsWhenNothingIsSelectedEnabled(bool enabled)
{ {
for (auto i = m_registeredActionTextChanges.begin(); i != m_registeredActionTextChanges.end(); ++i) { for (auto i = m_registeredActionTextChanges.begin(); i != m_registeredActionTextChanges.end(); ++i) {
if (!i->action) { while (!i->action) {
i = m_registeredActionTextChanges.erase(i); i = m_registeredActionTextChanges.erase(i);
continue; if (i == m_registeredActionTextChanges.end()) {
break;
} }
}
if (enabled && i->textStateOfRegisteredText == TextWhenNothingIsSelected) { if (enabled && i->textStateOfRegisteredText == TextWhenNothingIsSelected) {
QString textWhenSomethingIsSelected = i->action->text(); QString textWhenSomethingIsSelected = i->action->text();
i->action->setText(i->registeredText); i->action->setText(i->registeredText);

View file

@ -17,6 +17,11 @@ namespace SelectionMode
/** /**
* @brief Helps changing the texts of actions depending on the current selection. * @brief Helps changing the texts of actions depending on the current selection.
*
* This is useful for actions that directly trigger a change when there is a selection and do something
* different when nothing is selected. For example should the copy action read "Copy" when items are
* selected but when no items are selected it can read "Copy…" since triggering it will enter selection
* mode and ask users to select the files they want to copy first.
*/ */
class ActionTextHelper : QObject class ActionTextHelper : QObject
{ {
@ -25,7 +30,7 @@ public:
/** /**
* Changes the text of \a action to \a text whenever textsWhenNothingIsSelectedEnabled(true) is called. * Changes the text of \a action to \a text whenever textsWhenNothingIsSelectedEnabled(true) is called.
* The texts can be changed back by calling textsWhenNothingIsSelectedEnabled(false) is called. * The texts can be changed back by calling textsWhenNothingIsSelectedEnabled(false).
* @see textsWhenNothingIsSelectedEnabled() * @see textsWhenNothingIsSelectedEnabled()
*/ */
void registerTextWhenNothingIsSelected(QAction *action, QString registeredText); void registerTextWhenNothingIsSelected(QAction *action, QString registeredText);

View file

@ -21,6 +21,9 @@ namespace SelectionMode
* @brief Small wrapper/helper class that contains an action and its widget. * @brief Small wrapper/helper class that contains an action and its widget.
* *
* This class takes neither the responsibility for deleting its action() nor its widget(). * This class takes neither the responsibility for deleting its action() nor its widget().
*
* This class is only used from BottomBarContentsContainer currently.
* @see BottomBarContentsContainer
*/ */
class ActionWithWidget class ActionWithWidget
{ {
@ -28,9 +31,9 @@ public:
ActionWithWidget(QAction *action); ActionWithWidget(QAction *action);
/** /**
* Connect @p action and @p button using copyActionDataToButton() and the * Connect @p action and @p button using copyActionDataToButton() and
* wraps the two together in the ActionWithWidget object. * wraps the two together in a ActionWithWidget object.
* ActionWithWidget doesn't take any ownership. * ActionWithWidget doesn't take any ownership over the parameters.
* *
* @see copyActionDataToButton() * @see copyActionDataToButton()
* *
@ -38,7 +41,7 @@ public:
*/ */
ActionWithWidget(QAction *action, QAbstractButton *button); ActionWithWidget(QAction *action, QAbstractButton *button);
/** @returns the action of this object. Crashes if that action has been deleted elsewhere in the meantime. */ /** @returns the action of this object. */
inline QAction *action() { inline QAction *action() {
Q_CHECK_PTR(m_action); Q_CHECK_PTR(m_action);
return m_action; return m_action;
@ -69,7 +72,7 @@ private:
/** /**
* A small helper method. * A small helper method.
* @return a button with the correct styling for the general mode of the SelectionModeBottomBar which can be added to its layout. * @return a button with the correct styling for the general mode of the BottomBarContentsContainer which can be added to its layout.
*/ */
QAbstractButton *newButtonForAction(QAction *action, QWidget *parent); QAbstractButton *newButtonForAction(QAction *action, QWidget *parent);

View file

@ -54,9 +54,11 @@ void BackgroundColorHelper::slotPaletteChanged()
{ {
updateBackgroundColor(); updateBackgroundColor();
for (auto i = m_colorControlledWidgets.begin(); i != m_colorControlledWidgets.end(); ++i) { for (auto i = m_colorControlledWidgets.begin(); i != m_colorControlledWidgets.end(); ++i) {
if (!*i) { while (!*i) {
i = m_colorControlledWidgets.erase(i); i = m_colorControlledWidgets.erase(i);
continue; if (i == m_colorControlledWidgets.end()) {
break;
}
} }
setBackgroundColorForWidget(*i, m_backgroundColor); setBackgroundColorForWidget(*i, m_backgroundColor);
} }
@ -83,7 +85,8 @@ void BackgroundColorHelper::updateBackgroundColor()
} }
m_backgroundColor = QColor::fromHsv(newHue, m_backgroundColor = QColor::fromHsv(newHue,
// Saturation should be closer to the active color because otherwise the selection mode color might overpower it. // Saturation should be closer to the saturation of the active color
// because otherwise the selection mode color might overpower it.
.7 * activeBackgroundColor.saturation() + .3 * positiveBackgroundColor.saturation(), .7 * activeBackgroundColor.saturation() + .3 * positiveBackgroundColor.saturation(),
(activeBackgroundColor.value() + positiveBackgroundColor.value()) / 2, (activeBackgroundColor.value() + positiveBackgroundColor.value()) / 2,
(activeBackgroundColor.alpha() + positiveBackgroundColor.alpha()) / 2); (activeBackgroundColor.alpha() + positiveBackgroundColor.alpha()) / 2);

View file

@ -28,20 +28,30 @@ public:
/** /**
* Changes the background color of @p widget to a distinct color scheme matching color which makes it clear that the widget belongs to the selection mode. * Changes the background color of @p widget to a distinct color scheme matching color which makes it clear that the widget belongs to the selection mode.
* The background color of @p widget will from now on be updated automatically when the palette of the application changes.
*/ */
void controlBackgroundColor(QWidget *widget); void controlBackgroundColor(QWidget *widget);
private: private:
BackgroundColorHelper(); BackgroundColorHelper();
/**
* Called when the palette of the application changes.
* Triggers updateBackgroundColor() and the updates the background color of m_colorControlledWidgets.
* @see updateBackgroundColor
*/
void slotPaletteChanged(); void slotPaletteChanged();
/** Calculates a new m_colorControlledWidgets based on the current colour scheme of the application. */
void updateBackgroundColor(); void updateBackgroundColor();
private: private:
/// The widgets who have given up control over the background color to BackgroundColorHelper.
std::vector<QPointer<QWidget>> m_colorControlledWidgets; std::vector<QPointer<QWidget>> m_colorControlledWidgets;
/// The color to be used for the widgets' backgrounds.
QColor m_backgroundColor; QColor m_backgroundColor;
/// Singleton object
static BackgroundColorHelper *s_instance; static BackgroundColorHelper *s_instance;
}; };

View file

@ -11,11 +11,6 @@
#include "backgroundcolorhelper.h" #include "backgroundcolorhelper.h"
#include "global.h" #include "global.h"
#include <KActionCollection>
#include <KFileItem>
#include <KFileItemListProperties>
#include <KStandardAction>
#include <QGridLayout> #include <QGridLayout>
#include <QResizeEvent> #include <QResizeEvent>
#include <QScrollArea> #include <QScrollArea>
@ -87,7 +82,6 @@ void BottomBar::setVisibleInternal(bool visible, Animated animated)
m_heightAnimation->setDuration(2 * m_heightAnimation->setDuration(2 *
style()->styleHint(QStyle::SH_Widget_Animation_Duration, nullptr, this) * style()->styleHint(QStyle::SH_Widget_Animation_Duration, nullptr, this) *
GlobalConfig::animationDurationFactor()); GlobalConfig::animationDurationFactor());
m_heightAnimation->setStartValue(height()); m_heightAnimation->setStartValue(height());
m_heightAnimation->setEasingCurve(QEasingCurve::OutCubic); m_heightAnimation->setEasingCurve(QEasingCurve::OutCubic);
if (visible) { if (visible) {
@ -166,7 +160,7 @@ void BottomBar::resizeEvent(QResizeEvent *resizeEvent)
return QWidget::resizeEvent(resizeEvent); return QWidget::resizeEvent(resizeEvent);
} }
m_contentsContainer->updateForNewWidth(); m_contentsContainer->adaptToNewBarWidth(width());
return QWidget::resizeEvent(resizeEvent); return QWidget::resizeEvent(resizeEvent);
} }

View file

@ -15,8 +15,6 @@
#include <QPropertyAnimation> #include <QPropertyAnimation>
#include <QWidget> #include <QWidget>
#include <memory>
class KActionCollection; class KActionCollection;
class KFileItemList; class KFileItemList;
class QAction; class QAction;
@ -30,7 +28,7 @@ namespace SelectionMode
class BottomBarContentsContainer; class BottomBarContentsContainer;
/** /**
* A bar used in selection mode that serves various purposes depending on what the user is currently trying to do. * @brief A bar used in selection mode that serves various purposes depending on what the user is currently trying to do.
* *
* The Contents enum below gives a rough idea about the different states this bar might have. * The Contents enum below gives a rough idea about the different states this bar might have.
* The bar is notified of various changes that make changing or updating the content worthwhile. * The bar is notified of various changes that make changing or updating the content worthwhile.
@ -69,11 +67,20 @@ public:
* *
* This bar might also not show itself when setVisible(true), when context menu actions are supposed to be shown * This bar might also not show itself when setVisible(true), when context menu actions are supposed to be shown
* for the selected items but no items have been selected yet. In that case it will only show itself once items were selected. * for the selected items but no items have been selected yet. In that case it will only show itself once items were selected.
*
* This bar might also ignore a setVisible(false) call, if it has PasteContents because that bar is supposed to stay visible
* even outside of selection mode.
*
* @param visible Whether this bar is supposed to be visible long term
* @param animated Whether this should be animated. The animation is skipped if the users' settings are configured that way.
*
* @see QWidget::setVisible() * @see QWidget::setVisible()
*/ */
void setVisible(bool visible, Animated animated); void setVisible(bool visible, Animated animated);
using QWidget::setVisible; // Makes sure that the setVisible() declaration above doesn't hide the one from QWidget.
/**
* Changes the contents of the bar to @p contents.
*/
void resetContents(Contents contents); void resetContents(Contents contents);
Contents contents() const; Contents contents() const;
@ -81,6 +88,7 @@ public:
QSize sizeHint() const override; QSize sizeHint() const override;
public Q_SLOTS: public Q_SLOTS:
/** Adapts the contents based on the selection in the related view. */
void slotSelectionChanged(const KFileItemList &selection, const QUrl &baseUrl); void slotSelectionChanged(const KFileItemList &selection, const QUrl &baseUrl);
/** Used to notify the m_selectionModeBottomBar that there is no other ViewContainer in the tab. */ /** Used to notify the m_selectionModeBottomBar that there is no other ViewContainer in the tab. */
@ -102,6 +110,8 @@ protected:
void resizeEvent(QResizeEvent *resizeEvent) override; void resizeEvent(QResizeEvent *resizeEvent) override;
private: private:
using QWidget::setVisible; // Makes sure that the setVisible() declaration above doesn't hide the one from QWidget so we can still use it privately.
/** /**
* Identical to SelectionModeBottomBar::setVisible() but doesn't change m_allowedToBeVisible. * Identical to SelectionModeBottomBar::setVisible() but doesn't change m_allowedToBeVisible.
* @see SelectionModeBottomBar::setVisible() * @see SelectionModeBottomBar::setVisible()
@ -119,7 +129,7 @@ private:
* This is necessary because this bar might have been setVisible(true) but there is no reason to show the bar currently so it was kept hidden. * This is necessary because this bar might have been setVisible(true) but there is no reason to show the bar currently so it was kept hidden.
* @see SelectionModeBottomBar::setVisible() */ * @see SelectionModeBottomBar::setVisible() */
bool m_allowedToBeVisible = false; bool m_allowedToBeVisible = false;
/// @see SelectionModeBottomBar::setVisible() /** @see SelectionModeBottomBar::setVisible() */
QPointer<QPropertyAnimation> m_heightAnimation; QPointer<QPropertyAnimation> m_heightAnimation;
}; };

View file

@ -69,8 +69,10 @@ void BottomBarContentsContainer::resetContents(BottomBar::Contents contents)
} }
} }
void BottomBarContentsContainer::updateForNewWidth() void BottomBarContentsContainer::adaptToNewBarWidth(int newBarWidth)
{ {
m_barWidth = newBarWidth;
if (m_contents == BottomBar::GeneralContents) { if (m_contents == BottomBar::GeneralContents) {
Q_ASSERT(m_overflowButton); Q_ASSERT(m_overflowButton);
if (unusedSpace() < 0) { if (unusedSpace() < 0) {
@ -94,7 +96,7 @@ void BottomBarContentsContainer::updateForNewWidth()
} }
} }
} else { } else {
// We have some unusedSpace(). Let's check if we can maybe add more of the contextual action's widgets. // We have some unusedSpace(). Let's check if we can maybe add more of the contextual actions' widgets.
for (auto i = m_generalBarActions.begin(); i != m_generalBarActions.end(); ++i) { for (auto i = m_generalBarActions.begin(); i != m_generalBarActions.end(); ++i) {
if (i->isWidgetVisible()) { if (i->isWidgetVisible()) {
continue; continue;
@ -130,6 +132,7 @@ void BottomBarContentsContainer::slotSelectionChanged(const KFileItemList &selec
auto contextActions = contextActionsFor(selection, baseUrl); auto contextActions = contextActionsFor(selection, baseUrl);
m_generalBarActions.clear(); m_generalBarActions.clear();
if (contextActions.empty()) { if (contextActions.empty()) {
// We have nothing to show
Q_ASSERT(qobject_cast<BottomBar *>(parentWidget()->parentWidget()->parentWidget())); Q_ASSERT(qobject_cast<BottomBar *>(parentWidget()->parentWidget()->parentWidget()));
if (isVisibleTo(parentWidget()->parentWidget()->parentWidget()->parentWidget())) { // is the bar visible if (isVisibleTo(parentWidget()->parentWidget()->parentWidget()->parentWidget())) { // is the bar visible
Q_EMIT barVisibilityChangeRequested(false); Q_EMIT barVisibilityChangeRequested(false);
@ -162,7 +165,7 @@ void BottomBarContentsContainer::addCopyContents()
// We claim to have PasteContents already so triggering the copy action next won't instantly hide the bottom bar. // We claim to have PasteContents already so triggering the copy action next won't instantly hide the bottom bar.
connect(copyButton, &QAbstractButton::clicked, [this]() { connect(copyButton, &QAbstractButton::clicked, [this]() {
if (GeneralSettings::showPasteBarAfterCopying()) { if (GeneralSettings::showPasteBarAfterCopying()) {
m_contents = BottomBar::Contents::PasteContents; m_contents = BottomBar::Contents::PasteContents; // prevents hiding
} }
}); });
// Connect the copy action as a second step. // Connect the copy action as a second step.
@ -232,7 +235,7 @@ void BottomBarContentsContainer::addCutContents()
// We claim to have PasteContents already so triggering the cut action next won't instantly hide the bottom bar. // We claim to have PasteContents already so triggering the cut action next won't instantly hide the bottom bar.
connect(cutButton, &QAbstractButton::clicked, [this]() { connect(cutButton, &QAbstractButton::clicked, [this]() {
if (GeneralSettings::showPasteBarAfterCopying()) { if (GeneralSettings::showPasteBarAfterCopying()) {
m_contents = BottomBar::Contents::PasteContents; m_contents = BottomBar::Contents::PasteContents; // prevents hiding
} }
}); });
// Connect the cut action as a second step. // Connect the cut action as a second step.
@ -400,9 +403,9 @@ void BottomBarContentsContainer::addPasteContents()
vBoxLayout->addWidget(pasteButton); vBoxLayout->addWidget(pasteButton);
auto *dismissButton = new QToolButton(this); auto *dismissButton = new QToolButton(this);
dismissButton->setText(i18nc("@action Dismisses a bar explaining how to use the Paste action", "Dismiss this Reminder")); dismissButton->setText(i18nc("@action Dismisses a bar explaining how to use the Paste action", "Dismiss This Reminder"));
connect(dismissButton, &QAbstractButton::clicked, this, actuallyLeaveSelectionMode); connect(dismissButton, &QAbstractButton::clicked, this, actuallyLeaveSelectionMode);
auto *dontRemindAgainAction = new QAction(i18nc("@action Dismisses an explanatory area and never shows it again", "Don't remind me again"), this); auto *dontRemindAgainAction = new QAction(i18nc("@action Dismisses an explanatory area and never shows it again", "Don't Remind Me Again"), this);
connect(dontRemindAgainAction, &QAction::triggered, this, []() { connect(dontRemindAgainAction, &QAction::triggered, this, []() {
GeneralSettings::setShowPasteBarAfterCopying(false); GeneralSettings::setShowPasteBarAfterCopying(false);
}); });
@ -516,9 +519,7 @@ int BottomBarContentsContainer::unusedSpace() const
} }
sumOfPreferredWidths += m_layout->itemAt(i)->sizeHint().width() + m_layout->spacing(); sumOfPreferredWidths += m_layout->itemAt(i)->sizeHint().width() + m_layout->spacing();
} }
Q_ASSERT(qobject_cast<BottomBar *>(parentWidget()->parentWidget()->parentWidget())); return m_barWidth - sumOfPreferredWidths - 20; // We consider all space used when there are only 20 pixels left
const int totalBarWidth = parentWidget()->parentWidget()->parentWidget()->width();
return totalBarWidth - sumOfPreferredWidths - 20; // We consider all space used when there are only 20 pixels left
// so there is some room to breath and not too much wonkyness while resizing. // so there is some room to breath and not too much wonkyness while resizing.
} }
@ -535,7 +536,7 @@ void BottomBarContentsContainer::updateExplanatoryLabelVisibility()
} }
} }
void BottomBarContentsContainer::updateMainActionButton(const KFileItemList& selection) void BottomBarContentsContainer::updateMainActionButton(const KFileItemList& selectedItems)
{ {
if (!m_mainAction.widget()) { if (!m_mainAction.widget()) {
return; return;
@ -543,45 +544,69 @@ void BottomBarContentsContainer::updateMainActionButton(const KFileItemList& sel
Q_ASSERT(qobject_cast<QAbstractButton *>(m_mainAction.widget())); Q_ASSERT(qobject_cast<QAbstractButton *>(m_mainAction.widget()));
// Users are nudged towards selecting items by having the button disabled when nothing is selected. // Users are nudged towards selecting items by having the button disabled when nothing is selected.
m_mainAction.widget()->setEnabled(selection.count() > 0 && m_mainAction.action()->isEnabled()); m_mainAction.widget()->setEnabled(selectedItems.count() > 0 && m_mainAction.action()->isEnabled());
QFontMetrics fontMetrics = m_mainAction.widget()->fontMetrics(); QFontMetrics fontMetrics = m_mainAction.widget()->fontMetrics();
QString buttonText; QString buttonText;
switch (m_contents) { switch (m_contents) {
case BottomBar::CopyContents: case BottomBar::CopyContents:
buttonText = i18ncp("@action A more elaborate and clearly worded version of the Copy action", // i18n: A more elaborate and clearly worded version of the Copy action
"Copy %2 to the Clipboard", "Copy %2 to the Clipboard", selection.count(), // %2 is a textual representation of the currently selected files or folders. This can be the name of
fileItemListToString(selection, fontMetrics.averageCharWidth() * 20, fontMetrics)); // the file/files like "file1" or "file1, file2 and file3" or an aggregate like "8 Selected Folders".
// If this sort of word puzzle can not be correctly translated in your language, translate it as "NULL" (without the quotes)
// and a fallback will be used.
buttonText = i18ncp("@action", "Copy %2 to the Clipboard", "Copy %2 to the Clipboard", selectedItems.count(), fileItemListToString(selectedItems, fontMetrics.averageCharWidth() * 20, fontMetrics));
// All these long lines can not be broken up with line breaks becaue the i18n call should be completely
// in the line following the "i18n:" comment without any line breaks within the i18n call
// or the comment might not be correctly extracted. See: https://commits.kde.org/kxmlgui/a31135046e1b3335b5d7bbbe6aa9a883ce3284c1
break; break;
case BottomBar::CopyLocationContents: case BottomBar::CopyLocationContents:
buttonText = i18ncp("@action A more elaborate and clearly worded version of the Copy Location action", // i18n: A more elaborate and clearly worded version of the Copy Location action
"Copy the Location of %2 to the Clipboard", "Copy the Location of %2 to the Clipboard", selection.count(), // %2 is a textual representation of the currently selected files or folders. This can be the name of
fileItemListToString(selection, fontMetrics.averageCharWidth() * 20, fontMetrics)); // the file/files like "file1" or "file1, file2 and file3" or an aggregate like "8 Selected Folders".
// If this sort of word puzzle can not be correctly translated in your language, translate it as "NULL" (without the quotes)
// and a fallback will be used.
buttonText = i18ncp("@action", "Copy the Location of %2 to the Clipboard", "Copy the Location of %2 to the Clipboard", selectedItems.count(), fileItemListToString(selectedItems, fontMetrics.averageCharWidth() * 20, fontMetrics));
break; break;
case BottomBar::CutContents: case BottomBar::CutContents:
buttonText = i18ncp("@action A more elaborate and clearly worded version of the Cut action", // i18n: A more elaborate and clearly worded version of the Cut action
"Cut %2 to the Clipboard", "Cut %2 to the Clipboard", selection.count(), // %2 is a textual representation of the currently selected files or folders. This can be the name of
fileItemListToString(selection, fontMetrics.averageCharWidth() * 20, fontMetrics)); // the file/files like "file1" or "file1, file2 and file3" or an aggregate like "8 Selected Folders".
// If this sort of word puzzle can not be correctly translated in your language, translate it as "NULL" (without the quotes)
// and a fallback will be used.
buttonText = i18ncp("@action", "Cut %2 to the Clipboard", "Cut %2 to the Clipboard", selectedItems.count(), fileItemListToString(selectedItems, fontMetrics.averageCharWidth() * 20, fontMetrics));
break; break;
case BottomBar::DeleteContents: case BottomBar::DeleteContents:
buttonText = i18ncp("@action A more elaborate and clearly worded version of the Delete action", // i18n: A more elaborate and clearly worded version of the Delete action
"Permanently Delete %2", "Permanently Delete %2", selection.count(), // %2 is a textual representation of the currently selected files or folders. This can be the name of
fileItemListToString(selection, fontMetrics.averageCharWidth() * 20, fontMetrics)); // the file/files like "file1" or "file1, file2 and file3" or an aggregate like "8 Selected Folders".
// If this sort of word puzzle can not be correctly translated in your language, translate it as "NULL" (without the quotes)
// and a fallback will be used.
buttonText = i18ncp("@action", "Permanently Delete %2", "Permanently Delete %2", selectedItems.count(), fileItemListToString(selectedItems, fontMetrics.averageCharWidth() * 20, fontMetrics));
break; break;
case BottomBar::DuplicateContents: case BottomBar::DuplicateContents:
buttonText = i18ncp("@action A more elaborate and clearly worded version of the Duplicate action", // i18n: A more elaborate and clearly worded version of the Duplicate action
"Duplicate %2", "Duplicate %2", selection.count(), // %2 is a textual representation of the currently selected files or folders. This can be the name of
fileItemListToString(selection, fontMetrics.averageCharWidth() * 20, fontMetrics)); // the file/files like "file1" or "file1, file2 and file3" or an aggregate like "8 Selected Folders".
// If this sort of word puzzle can not be correctly translated in your language, translate it as "NULL" (without the quotes)
// and a fallback will be used.
buttonText = i18ncp("@action", "Duplicate %2", "Duplicate %2", selectedItems.count(), fileItemListToString(selectedItems, fontMetrics.averageCharWidth() * 20, fontMetrics));
break; break;
case BottomBar::MoveToTrashContents: case BottomBar::MoveToTrashContents:
buttonText = i18ncp("@action A more elaborate and clearly worded version of the Trash action", // i18n: A more elaborate and clearly worded version of the Trash action
"Move %2 to the Trash", "Move %2 to the Trash", selection.count(), // %2 is a textual representation of the currently selected files or folders. This can be the name of
fileItemListToString(selection, fontMetrics.averageCharWidth() * 20, fontMetrics)); // the file/files like "file1" or "file1, file2 and file3" or an aggregate like "8 Selected Folders".
// If this sort of word puzzle can not be correctly translated in your language, translate it as "NULL" (without the quotes)
// and a fallback will be used.
buttonText = i18ncp("@action", "Move %2 to the Trash", "Move %2 to the Trash", selectedItems.count(), fileItemListToString(selectedItems, fontMetrics.averageCharWidth() * 20, fontMetrics));
break; break;
case BottomBar::RenameContents: case BottomBar::RenameContents:
buttonText = i18ncp("@action A more elaborate and clearly worded version of the Rename action", // i18n: A more elaborate and clearly worded version of the Rename action
"Rename %2", "Rename %2", selection.count(), // %2 is a textual representation of the currently selected files or folders. This can be the name of
fileItemListToString(selection, fontMetrics.averageCharWidth() * 20, fontMetrics)); // the file/files like "file1" or "file1, file2 and file3" or an aggregate like "8 Selected Folders".
// If this sort of word puzzle can not be correctly translated in your language, translate it as "NULL" (without the quotes)
// and a fallback will be used.
buttonText = i18ncp("@action", "Rename %2", "Rename %2", selectedItems.count(), fileItemListToString(selectedItems, fontMetrics.averageCharWidth() * 20, fontMetrics));
break; break;
default: default:
return; return;

View file

@ -43,6 +43,9 @@ public:
*/ */
explicit BottomBarContentsContainer(KActionCollection *actionCollection, QWidget *parent); explicit BottomBarContentsContainer(KActionCollection *actionCollection, QWidget *parent);
/**
* @param contents The kind of contents that should be contained instead.
*/
void resetContents(BottomBar::Contents contents); void resetContents(BottomBar::Contents contents);
inline BottomBar::Contents contents() const inline BottomBar::Contents contents() const
{ {
@ -53,9 +56,14 @@ public:
return contents() != BottomBar::GeneralContents || m_internalContextMenu; return contents() != BottomBar::GeneralContents || m_internalContextMenu;
} }
void updateForNewWidth(); /**
* Is called when the BottomBar resizes to let this ContentsContainer know that it should adapt its contents to the new width.
* Adapting is done by showing or hiding labels or buttons.
*/
void adaptToNewBarWidth(int newBarWidth);
public Q_SLOTS: public Q_SLOTS:
/** Adapts the contents based on the selection in the related view. */
void slotSelectionChanged(const KFileItemList &selection, const QUrl &baseUrl); void slotSelectionChanged(const KFileItemList &selection, const QUrl &baseUrl);
Q_SIGNALS: Q_SIGNALS:
@ -65,8 +73,8 @@ Q_SIGNALS:
void error(const QString &errorMessage); void error(const QString &errorMessage);
/** /**
* Sometimes the contents see no reason to be visible and request the bar to be hidden instead which emits this signal. * When it does not make sense to show any specific contents, this signal is emitted and the receiver hides the bar.
* This can later change e.g. because the user selected items. Then this signal is used to request showing of the bar. * Later it might sense to show it again e.g. because the user selected items. Then this signal is used to request showing of the bar.
*/ */
void barVisibilityChangeRequested(bool visible); void barVisibilityChangeRequested(bool visible);
@ -90,15 +98,15 @@ private:
void addRenameContents(); void addRenameContents();
/** /**
* Deletes all visible widgets and layouts from the bar. * Deletes every child layout and child widget of this container.
*/ */
void emptyBarContents(); void emptyBarContents();
/** /**
* @returns A vector containing contextual actions for the given \a selection in the \a baseUrl. * @returns A vector containing contextual actions for the given \a selectedItems in the \a baseUrl.
* Cut, Copy, Rename and MoveToTrash are always added. Any further contextual actions depend on * Cut, Copy, Rename and MoveToTrash are always added. Any further contextual actions depend on
* \a selection and \a baseUrl. \a selection and \a baseUrl can be empty/default constructed if * \a selectedItems and \a baseUrl.
* no item- or view-specific actions should be added aside from Cut, Copy, Rename, MoveToTrash. * If there are no \a selectedItems, an empty vector is returned and m_internalContextMenu is deleted.
* @param selectedItems The selected items for which contextual actions should be displayed. * @param selectedItems The selected items for which contextual actions should be displayed.
* @param baseUrl Base URL of the viewport the contextual actions apply to. * @param baseUrl Base URL of the viewport the contextual actions apply to.
*/ */
@ -107,7 +115,7 @@ private:
/** /**
* @returns the amount of pixels that can be spared to add more widgets. A negative value might * @returns the amount of pixels that can be spared to add more widgets. A negative value might
* be returned which signifies that some widgets should be hidden or removed from this bar to * be returned which signifies that some widgets should be hidden or removed from this bar to
* make sure that this SelectionModeBottomBar won't stretch the width of its parent. * make sure that this BottomBarContentsContainer can fully fit on the BottomBar.
*/ */
int unusedSpace() const; int unusedSpace() const;
@ -118,12 +126,12 @@ private:
void updateExplanatoryLabelVisibility(); void updateExplanatoryLabelVisibility();
/** /**
* Changes the text and enabled state of the main action button * Changes the text and enabled state of the main action button based on the amount of currently
* based on the amount of currently selected items and the state of the current m_mainAction. * selected items and the state of the current m_mainAction.
* The current main action depends on the current barContents. * The current main action depends on the current barContents.
* @param selection the currently selected fileItems. * @param selectedItems the currently selected fileItems.
*/ */
void updateMainActionButton(const KFileItemList &selection); void updateMainActionButton(const KFileItemList &selectedItems);
private: private:
/// All the actions that should be available from this bar when in general mode. /// All the actions that should be available from this bar when in general mode.
@ -141,11 +149,12 @@ private:
KActionCollection *m_actionCollection; KActionCollection *m_actionCollection;
/// Describes the current contents of the bar. /// Describes the current contents of the bar.
BottomBar::Contents m_contents; BottomBar::Contents m_contents;
/** The layout all the buttons and labels are added to. /// The main layout of this ContentsContainer that all the buttons and labels are added to.
* Do not confuse this with layout() because we do have a QScrollView in between this widget and m_layout. */
QHBoxLayout *m_layout; QHBoxLayout *m_layout;
/// The info label used for some of the BarContents. Is hidden for narrow widths. /// Caches the totalBarWidth as set in adaptToNewWidth(newBarWidth). */
int m_barWidth = 0;
/// The info label used for some of the Contents. Is hidden for narrow widths.
QPointer<QLabel> m_explanatoryLabel; QPointer<QLabel> m_explanatoryLabel;
}; };

View file

@ -42,7 +42,7 @@ TopBar::TopBar(QWidget *parent) :
fillParentLayout->setContentsMargins(0, 0, 0, 0); fillParentLayout->setContentsMargins(0, 0, 0, 0);
// Put the contents into a QScrollArea. This prevents increasing the view width // Put the contents into a QScrollArea. This prevents increasing the view width
// in case that not enough width for the contents is available. (this trick is also used in selectionmodebottombar.cpp.) // in case that not enough width for the contents is available. (this trick is also used in bottombar.cpp.)
auto scrollArea = new QScrollArea(this); auto scrollArea = new QScrollArea(this);
fillParentLayout->addWidget(scrollArea); fillParentLayout->addWidget(scrollArea);
scrollArea->setFrameShape(QFrame::NoFrame); scrollArea->setFrameShape(QFrame::NoFrame);
@ -111,9 +111,10 @@ void TopBar::setVisible(bool visible, Animated animated)
m_heightAnimation->start(QAbstractAnimation::DeleteWhenStopped); m_heightAnimation->start(QAbstractAnimation::DeleteWhenStopped);
} }
void TopBar::resizeEvent(QResizeEvent */* resizeEvent */) void TopBar::resizeEvent(QResizeEvent *resizeEvent)
{ {
updateLabelString(); updateLabelString();
return QWidget::resizeEvent(resizeEvent);
} }
void TopBar::updateLabelString() void TopBar::updateLabelString()

View file

@ -40,15 +40,17 @@ public:
* @see QWidget::setVisible() * @see QWidget::setVisible()
*/ */
void setVisible(bool visible, Animated animated); void setVisible(bool visible, Animated animated);
using QWidget::setVisible; // Makes sure that the setVisible() declaration above doesn't hide the one from QWidget.
Q_SIGNALS: Q_SIGNALS:
void leaveSelectionModeRequested(); void leaveSelectionModeRequested();
protected: protected:
void resizeEvent(QResizeEvent */* resizeEvent */) override; /** Calls updateLabelString() */
void resizeEvent(QResizeEvent *resizeEvent) override;
private: private:
using QWidget::setVisible; // Makes sure that the setVisible() declaration above doesn't hide the one from QWidget so we can still use it privately.
/** Decides whether the m_fullLabelString or m_shortLabelString should be used based on available width. */ /** Decides whether the m_fullLabelString or m_shortLabelString should be used based on available width. */
void updateLabelString(); void updateLabelString();

View file

@ -301,7 +301,6 @@ bool DolphinView::selectionMode() const
return m_container->controller()->selectionMode(); return m_container->controller()->selectionMode();
} }
void DolphinView::setPreviewsShown(bool show) void DolphinView::setPreviewsShown(bool show)
{ {
if (previewsShown() == show) { if (previewsShown() == show) {

View file

@ -112,6 +112,9 @@ public:
void setViewMode(Mode mode); void setViewMode(Mode mode);
Mode viewMode() const; Mode viewMode() const;
/**
* Enables or disables a mode for quick and easy selection of items.
*/
void setSelectionMode(bool enabled); void setSelectionMode(bool enabled);
bool selectionMode() const; bool selectionMode() const;

View file

@ -166,6 +166,7 @@ void DolphinViewActionHandler::createActions(SelectionMode::ActionTextHelper *ac
connect(copyPathAction, &QAction::triggered, this, &DolphinViewActionHandler::slotCopyPath); connect(copyPathAction, &QAction::triggered, this, &DolphinViewActionHandler::slotCopyPath);
if (actionTextHelper) { if (actionTextHelper) {
// The "…" at the end make clear that they won't trigger their respective actions directly.
actionTextHelper->registerTextWhenNothingIsSelected(trashAction, i18nc("@action:inmenu File", "Move to Trash…")); actionTextHelper->registerTextWhenNothingIsSelected(trashAction, i18nc("@action:inmenu File", "Move to Trash…"));
actionTextHelper->registerTextWhenNothingIsSelected(deleteAction, i18nc("@action:inmenu File", "Delete…")); actionTextHelper->registerTextWhenNothingIsSelected(deleteAction, i18nc("@action:inmenu File", "Delete…"));
actionTextHelper->registerTextWhenNothingIsSelected(duplicateAction, i18nc("@action:inmenu File", "Duplicate Here…")); actionTextHelper->registerTextWhenNothingIsSelected(duplicateAction, i18nc("@action:inmenu File", "Duplicate Here…"));
@ -438,6 +439,7 @@ void DolphinViewActionHandler::slotRename()
} else { } else {
Q_EMIT actionBeingHandled(); Q_EMIT actionBeingHandled();
m_currentView->renameSelectedItems(); m_currentView->renameSelectedItems();
// We don't exit selectionMode here because users might want to rename more items.
} }
} }