Commit 38a71d1c authored by Harald Sitter's avatar Harald Sitter 🏳️‍🌈
Browse files

replace previous summary with tidy overview widget

it occurred to us that the old disk view was fishy on a number of levels

- it had a non exhaustive filter list of bad mounts
- this then raised the question why the mounts are missing (when e.g.
the user expected them to be there)
- the free space on a partition is not reflective of the space filelight
will be able to determine the use of (e.g. when +x is missing on a dir
that dir will be missing from the fileview)
- when talking about bind mounts (such as inside a sandbox) the two
sizes will almost never add up
- when inside a sandbox potentially only one valid path is available
($home) so having the previous view was slightly weird

the grand new overview widget simply displays three buttons to get right
into scanning root, home, or a custom path. root is hidden inside a
sandbox.

this preserves most of the spirit of what the previous view wanted to do
while scaling better and doing away with judgment calls on the edge
cases such as bind mounts

BUG: 452275
BUG: 440152
BUG: 440151
BUG: 426594
parent 58e47b18
Pipeline #165524 passed with stage
in 6 minutes and 5 seconds
......@@ -41,6 +41,7 @@ find_package(KF5 ${KF5_MIN_VERSION} REQUIRED
XmlGui # For app
KIO # For part
I18n
IconThemes
)
find_package(KF5DocTools) # Optional, not needed on Windows for example.
......
......@@ -15,6 +15,7 @@ target_sources(filelight PRIVATE
radialMap/map.cpp
radialMap/widgetEvents.cpp
radialMap/labels.cpp
overviewWidget.cpp
scan.cpp
progressBox.cpp
Config.cpp
......@@ -22,7 +23,6 @@ target_sources(filelight PRIVATE
fileTree.cpp
localLister.cpp
remoteLister.cpp
summaryWidget.cpp
historyAction.cpp
mainWindow.cpp
main.cpp
......@@ -40,7 +40,6 @@ target_sources(filelight PRIVATE
Config.h
mainWindow.h
progressBox.h
summaryWidget.h
)
ecm_qt_declare_logging_category(filelight HEADER filelight_debug.h IDENTIFIER FILELIGHT_LOG CATEGORY_NAME org.kde.filelight DESCRIPTION "filelight" EXPORT FILELIGHT)
......@@ -59,6 +58,7 @@ target_link_libraries(filelight
KF5::I18n
KF5::XmlGui
KF5::KIOWidgets # Only used for KDirLister, may be able to move away from that.
KF5::IconThemes
Qt${QT_MAJOR_VERSION}::Svg
)
if (WIN32)
......
......@@ -15,7 +15,6 @@
#include "radialMap/widget.h"
#include "scan.h"
#include "settingsDialog.h"
#include "summaryWidget.h"
#include <cstdlib> //std::exit()
#include <iostream>
......@@ -39,12 +38,14 @@
#include <QStatusBar>
#include <QLineEdit>
#include "overviewWidget.h"
namespace Filelight {
MainWindow::MainWindow()
: KXmlGuiWindow()
, m_histories(nullptr)
, m_summary(nullptr)
, m_overviewWidget(nullptr)
, m_map(nullptr)
, m_started(false)
{
......@@ -142,7 +143,7 @@ void MainWindow::setupActions() //singleton function
action = ac->addAction(QStringLiteral("scan_root"), this, &MainWindow::slotScanRootFolder);
action->setText(i18n("Scan &Root Folder"));
action->setIcon(QIcon::fromTheme(QStringLiteral("folder-red")));
action->setIcon(QIcon::fromTheme(QStringLiteral("folder-root")));
action = ac->addAction(QStringLiteral("scan_rescan"), this, &MainWindow::rescan);
action->setText(i18n("Rescan"));
......@@ -163,7 +164,7 @@ void MainWindow::setupActions() //singleton function
action = ac->addAction(QStringLiteral("go_overview"), m_combo, [this]() {
slotAbortScan();
showSummary();
showOverview();
});
action->setText(i18nc("@action go to overview page, listing browsable mount points", "Overview"));
action->setIcon(QIcon::fromTheme(QStringLiteral("go-jump-locationbar")));
......@@ -358,7 +359,7 @@ void MainWindow::postInit()
if (url().isEmpty()) //if url is not empty openUrl() has been called immediately after ctor, which happens
{
m_map->hide();
showSummary();
showOverview();
//FIXME KXMLGUI is b0rked, it should allow us to set this
//BEFORE createGUI is called but it doesn't
......@@ -401,8 +402,8 @@ bool MainWindow::openUrl(const QUrl &u)
}
else
{
//we don't want to be using the summary screen anymore
hideSummary();
//we don't want to be using the overview screen anymore
hideOverview();
m_stateWidget->show();
m_layout->addWidget(m_stateWidget);
......@@ -425,7 +426,7 @@ bool MainWindow::closeUrl()
m_map->hide();
m_stateWidget->hide();
showSummary();
showOverview();
return true;
}
......@@ -475,10 +476,6 @@ void MainWindow::configFilelight()
connect(dialog, &SettingsDialog::canvasIsDirty, m_map, &RadialMap::Widget::refresh);
connect(dialog, &SettingsDialog::mapIsInvalid, m_manager, &ScanManager::emptyCache);
if (m_summary) {
connect(dialog, &SettingsDialog::canvasIsDirty, m_summary, &SummaryWidget::canvasDirtied);
}
dialog->show(); //deletes itself
}
......@@ -514,10 +511,10 @@ bool MainWindow::start(const QUrl &url)
void MainWindow::rescan()
{
if (m_summary && !m_summary->isHidden()) {
delete m_summary;
m_summary = nullptr;
showSummary();
if (m_overviewWidget && !m_overviewWidget->isHidden()) {
delete m_overviewWidget;
m_overviewWidget = nullptr;
showOverview();
return;
}
......@@ -548,7 +545,7 @@ void MainWindow::folderScanCompleted(Folder *tree)
m_map->hide();
m_stateWidget->hide();
showSummary();
showOverview();
setUrl(QUrl());
}
......@@ -568,23 +565,22 @@ void MainWindow::mapChanged(const Folder *tree)
m_numberOfFiles->setText(text);
}
void MainWindow::showSummary()
void MainWindow::showOverview()
{
if (m_summary == nullptr) {
m_summary = new SummaryWidget(widget());
m_summary->setObjectName(QStringLiteral("summaryWidget"));
connect(m_summary, &SummaryWidget::activated, this, &MainWindow::openUrl);
m_summary->show();
m_layout->addWidget(m_summary);
if (m_overviewWidget == nullptr) {
m_overviewWidget = new OverviewWidget(actionCollection(), widget());
m_overviewWidget->setObjectName(QStringLiteral("OverviewWidget"));
m_overviewWidget->show();
m_layout->addWidget(m_overviewWidget);
} else {
m_summary->show();
m_overviewWidget->show();
}
}
void MainWindow::hideSummary()
void MainWindow::hideOverview()
{
if (m_summary != nullptr) {
m_summary->hide();
if (m_overviewWidget != nullptr) {
m_overviewWidget->hide();
}
}
......
......@@ -30,7 +30,7 @@ class HistoryCollection;
namespace Filelight {
class ScanManager;
class SummaryWidget;
class OverviewWidget;
class MainWindow : public KXmlGuiWindow // Maybe use qmainwindow
{
......@@ -86,8 +86,8 @@ private:
void setupActions();
bool closeUrl();
QString prettyUrl(const QUrl &url) const;
void showSummary();
void hideSummary();
void showOverview();
void hideOverview();
bool start(const QUrl&);
KSqueezedTextLabel *m_status[2];
......@@ -96,7 +96,7 @@ private:
KRecentFilesAction *m_recentScans;
QLayout *m_layout;
SummaryWidget *m_summary;
OverviewWidget *m_overviewWidget;
RadialMap::Widget *m_map;
ProgressBox *m_stateWidget;
ScanManager *m_manager;
......
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
// SPDX-FileCopyrightText: 2022 Harald Sitter <sitter@kde.org>
#include "overviewWidget.h"
#include <QHBoxLayout>
#include <QLabel>
#include <QStandardPaths>
#include <QToolButton>
#include <KActionCollection>
#include <KIconLoader>
#include <KLocalizedString>
static constexpr auto arbitraryTinySpacing = 6;
Filelight::OverviewWidget::OverviewWidget(KActionCollection *actionCollection, QWidget *parent)
: QWidget(parent)
{
auto layout = new QVBoxLayout(this);
layout->setSpacing(arbitraryTinySpacing);
setLayout(layout);
layout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding));
auto iconLabel = new QLabel(this);
iconLabel->setPixmap(QIcon::fromTheme(QStringLiteral("filelight")).pixmap(KIconLoader::SizeEnormous));
layout->addWidget(iconLabel, 0, Qt::AlignCenter);
auto welcomeLabel = new QLabel(i18nc("@title", "Welcome to Filelight"), this);
QFont font = welcomeLabel->font();
font.setBold(true);
welcomeLabel->setFont(font);
layout->addWidget(welcomeLabel, 0, Qt::AlignCenter);
static constexpr auto arbitraryTinySpacing = 8;
layout->addSpacerItem(new QSpacerItem(0, arbitraryTinySpacing, QSizePolicy::Fixed));
addButtons(actionCollection);
layout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding));
}
void Filelight::OverviewWidget::addAction(QAction *action, QLayout *layout)
{
auto button = new QToolButton(this);
button->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
button->setIconSize(QSize(KIconLoader::SizeHuge, KIconLoader::SizeHuge));
button->setDefaultAction(action);
layout->addWidget(button);
}
void Filelight::OverviewWidget::addButtons(KActionCollection *actionCollection)
{
auto buttonLayout = new QHBoxLayout(this);
buttonLayout->setSpacing(arbitraryTinySpacing);
layout()->addItem(buttonLayout);
buttonLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding));
addAction(actionCollection->action(QStringLiteral("scan_folder")), buttonLayout);
addAction(actionCollection->action(QStringLiteral("scan_home")), buttonLayout);
static const bool inSandbox =
!(QStandardPaths::locate(QStandardPaths::RuntimeLocation, QLatin1String("flatpak-info")).isEmpty() ||
qEnvironmentVariableIsSet("SNAP"));
if (!inSandbox) {
addAction(actionCollection->action(QStringLiteral("scan_root")), buttonLayout);
}
buttonLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding));
}
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
// SPDX-FileCopyrightText: 2022 Harald Sitter <sitter@kde.org>
#pragma once
#include <QWidget>
class KActionCollection;
namespace Filelight {
class OverviewWidget : public QWidget
{
Q_OBJECT
public:
explicit OverviewWidget(KActionCollection *actionCollection, QWidget *parent);
private:
void addButtons(KActionCollection *actionCollection);
void addAction(QAction *action, QLayout *layout);
};
} // namespace Filelight
......@@ -298,8 +298,6 @@ void RadialMap::Widget::paintExplodedLabels(QPainter &paint) const
paint.drawLine(label->middleX, label->startY, label->startX, label->startY);
QString text = label->qs;
if (isSummary() && text == QLatin1String("free")) text = i18nc("Free as in part of the disk that is free", "free");
else if (isSummary() && text == QLatin1String("used")) text = i18nc("Used as in part of the disk that is used", "used");
paint.drawText(label->textX, label->textY, text);
}
......
......@@ -24,11 +24,10 @@
#include "sincos.h"
#include "widget.h"
RadialMap::Map::Map(bool summary)
RadialMap::Map::Map()
: m_visibleDepth(DEFAULT_RING_DEPTH)
, m_ringBreadth(MIN_RING_BREADTH)
, m_innerRadius(0)
, m_summary(summary)
{
//FIXME this is all broken. No longer is a maximum depth!
......@@ -259,32 +258,6 @@ void RadialMap::Map::colorise()
double deltaGreen = (double)(kdeColour[0].green() - kdeColour[1].green()) / 2880;
double deltaBlue = (double)(kdeColour[0].blue() - kdeColour[1].blue()) / 2880;
if (m_summary) { // Summary view has its own colors, special cased.
cp = Qt::gray;
cb = Qt::white;
m_signature[0][0]->setPalette(cp, cb);
// need to check in case there's no free space
if (m_signature[0].size() > 1) {
cb = QApplication::palette().highlight().color();
cb.getHsv(&h, &s1, &v1);
if (s1 > 80) {
s1 = 80;
}
v2 = v1 - int(contrast * v1);
s2 = s1 + int(contrast * (255 - s1));
cb.setHsv(h, s1, v1);
cp.setHsv(h, s2, v2);
m_signature[0][1]->setPalette(cp, cb);
}
return;
}
for (uint i = 0; i <= m_visibleDepth; ++i, darkness += 0.04) {
for (Segment *segment : m_signature[i]) {
switch (Config::scheme) {
......
......@@ -22,7 +22,7 @@ class Segment;
class Map
{
public:
explicit Map(bool summary);
Map();
~Map();
void make(const Folder *, bool = false);
......@@ -66,7 +66,6 @@ private:
int m_ringBreadth;
uint m_innerRadius; ///radius of inner circle
QString m_centerText;
bool m_summary;
qreal m_dpr;
uint MAP_2MARGIN;
......
......@@ -19,13 +19,12 @@
#include <QTimer> //member
RadialMap::Widget::Widget(QWidget *parent, bool isSummary)
RadialMap::Widget::Widget(QWidget *parent)
: QWidget(parent)
, m_tree(nullptr)
, m_focus(nullptr)
, m_map(isSummary)
, m_map()
, m_rootSegment(nullptr) //TODO we don't delete it, *shrug*
, m_isSummary(isSummary)
, m_toBeDeleted(nullptr)
{
setAcceptDrops(true);
......
......@@ -38,7 +38,7 @@ class Widget : public QWidget
Q_OBJECT
public:
explicit Widget(QWidget* = nullptr, bool = false);
explicit Widget(QWidget* = nullptr);
~Widget() override;
QString path() const;
QUrl url(File const * const = nullptr) const;
......@@ -47,10 +47,6 @@ public:
return m_tree != nullptr;
}
bool isSummary() const {
return m_isSummary;
}
friend class Label; //FIXME badness
void saveSvg(const QString &path) { m_map.saveSvg(path); }
......@@ -109,7 +105,6 @@ private:
QTimer m_timer;
Map m_map;
Segment *m_rootSegment;
const bool m_isSummary;
const Segment *m_toBeDeleted;
QLabel m_tooltip;
};
......
......@@ -158,51 +158,30 @@ void RadialMap::Widget::mouseMoveEvent(QMouseEvent *e)
setCursor(Qt::PointingHandCursor);
QString string;
if (isSummary()) {
if (strcmp("used", m_focus->file()->name8Bit()) == 0) {
string = i18nc("Tooltip of used space on the partition, %1 is path, %2 is size",
"%1\nUsed: %2",
m_focus->file()->parent()->displayPath(),
m_focus->file()->humanReadableSize());
} else if (strcmp("free", m_focus->file()->name8Bit()) == 0) {
string = i18nc("Tooltip of free space on the partition, %1 is path, %2 is size",
"%1\nFree: %2",
m_focus->file()->parent()->displayPath(),
m_focus->file()->humanReadableSize());
QString string = i18nc("Tooltip of file/folder, %1 is path, %2 is size",
"%1\n%2",
m_focus->file()->displayPath(),
m_focus->file()->humanReadableSize());
if (m_focus->file()->isFolder()) {
int files = static_cast<const Folder*>(m_focus->file())->children();
const uint percent = uint((100 * files) / (double)m_tree->children());
string += QLatin1Char('\n');
if (percent > 0) {
string += i18ncp("Tooltip of folder, %1 is number of files",
"%1 File (%2%)", "%1 Files (%2%)",
files, percent);
} else {
string = i18nc("Tooltip of file/folder, %1 is path, %2 is size",
"%1\n%2",
m_focus->file()->displayPath(),
m_focus->file()->humanReadableSize());
}
} else {
string = i18nc("Tooltip of file/folder, %1 is path, %2 is size",
"%1\n%2",
m_focus->file()->displayPath(),
m_focus->file()->humanReadableSize());
if (m_focus->file()->isFolder()) {
int files = static_cast<const Folder*>(m_focus->file())->children();
const uint percent = uint((100 * files) / (double)m_tree->children());
string += QLatin1Char('\n');
if (percent > 0) {
string += i18ncp("Tooltip of folder, %1 is number of files",
"%1 File (%2%)", "%1 Files (%2%)",
files, percent);
} else {
string += i18ncp("Tooltip of folder, %1 is number of files",
"%1 File", "%1 Files",
files);
}
string += i18ncp("Tooltip of folder, %1 is number of files",
"%1 File", "%1 Files",
files);
}
}
const QUrl url = Widget::url(m_focus->file());
if (m_focus == m_rootSegment && url != KIO::upUrl(url)) {
string += i18n("\nClick to go up to parent directory");
}
const QUrl url = Widget::url(m_focus->file());
if (m_focus == m_rootSegment && url != KIO::upUrl(url)) {
string += i18n("\nClick to go up to parent directory");
}
// Calculate a semi-sane size for the tooltip
......@@ -314,7 +293,7 @@ void RadialMap::Widget::mousePressEvent(QMouseEvent *e)
popup.addSeparator();
centerMap = popup.addAction(QIcon::fromTheme(QStringLiteral( "zoom-in" )), i18n("&Center Map Here"));
}
popup.addSeparator();
doNotScanItem = popup.addAction(QIcon::fromTheme(QStringLiteral("list-remove")), i18n("Add to Do &Not Scan List"));
rescanAction = popup.addAction(QIcon::fromTheme(QStringLiteral("view-refresh")), i18n("&Rescan"));
......
/***********************************************************************
* SPDX-FileCopyrightText: 2003-2004 Max Howell <max.howell@methylblue.com>
* SPDX-FileCopyrightText: 2008-2009 Martin Sandsmark <martin.sandsmark@kde.org>
*
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
***********************************************************************/
#include "summaryWidget.h"
#include "fileTree.h"
#include "radialMap/radialMap.h"
#include "radialMap/widget.h"
#include <KLocalizedString>
#include <QLabel>
#include <QApplication>
#include <QByteArray>
#include <QList>
#include <QMouseEvent>
#include <QLayout>
#include <QStorageInfo>
#include <QMap>
namespace Filelight
{
struct Disk
{
QString mount;
QString name;
qint64 size;
qint64 used;
qint64 free; //NOTE used+avail != size (clustersize!)
};
struct DiskList : QMap<QString, Disk>
{
DiskList();
};
class MyRadialMap : public RadialMap::Widget
{
Q_OBJECT
public:
MyRadialMap(QWidget *parent)
: RadialMap::Widget(parent, true)
{
}
virtual void setCursor(const QCursor &c)
{
if (focusSegment() && focusSegment()->file()->decodedName() == QLatin1String( "Used" ))
RadialMap::Widget::setCursor(c);
else
unsetCursor();
}
void mousePressEvent(QMouseEvent *e) override
{
if (focusSegment() == rootSegment() && e->button() == Qt::RightButton) {
// we will allow right clicks to the center circle
RadialMap::Widget::mousePressEvent(e);
} else if (e->button() == Qt::LeftButton) {
// and clicks to the used segment
Q_EMIT activated(url());
}
}
};
SummaryWidget::SummaryWidget(QWidget *parent)
: QWidget(parent)
{
qApp->setOverrideCursor(Qt::WaitCursor);
setLayout(new QGridLayout(this));
createDiskMaps();
qApp->restoreOverrideCursor();
}
SummaryWidget::~SummaryWidget()
{
qDeleteAll(m_disksFolders);
}
void SummaryWidget::createDiskMaps()
{
DiskList disks;
QString text;
for (DiskList::ConstIterator it = disks.constBegin(), end = disks.constEnd(); it != end; ++it)
{
Disk const &disk = it.value();
if (disk.free == 0 && disk.used == 0) {
continue;
}
QWidget *volume = new QWidget(this);
QVBoxLayout *volumeLayout = new QVBoxLayout(volume);
RadialMap::Widget *map = new MyRadialMap(this);
connect(this, &SummaryWidget::canvasDirtied, map, &RadialMap::Widget::refresh);
QWidget *info = new QWidget(this);
info->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
QHBoxLayout* horizontalLayout = new QHBoxLayout(info);
// Create the text under the radialMap.
if (disk.name.