HaikuDepot: add work status view

* Add new WorkStatusView which keeps the user informed about what's
  happening. It's a status bar at the bottom of the window which
  shows on the left side either a spinning barber pole (for
  operations without a progress), or a progress bar (for download
  progress). Next to that is a text view showing a descriptive
  status text.

* Currently, it will notify of the following operations:
  - Repository refresh (barber pole)
  - Background packet actions, like preparation of install or uninstall
    (barber pole)
  - Package downloads, including downloads of dependencies (progress
    bar). Status text indicates the name of the package currently
    being downloaded (if any), and how many more packages are queued
    for download after it (if any).

* Hooks into PackageListView to be notified of package status changes
  (such as becoming pending or download progress)

* When the package currently being downloaded is also selected in the
  list view, the user sees the progress bar in WorkStatusView
  as well as the one in the PackageInfoView, which is redundant. This
  still needs a good solution...
This commit is contained in:
Julian Harnath 2017-11-23 22:46:32 +01:00
parent d737433974
commit 125d42d95b
7 changed files with 278 additions and 3 deletions

View File

@ -84,7 +84,8 @@ Application HaikuDepot :
ScrollableGroupView.cpp
SharedBitmap.cpp
UserLoginWindow.cpp
WorkStatusView.cpp
# network + server - model
DumpExportPkg.cpp
DumpExportPkgCategory.cpp

View File

@ -4,6 +4,7 @@
* Copyright 2013, Rene Gollent, rene@gollent.com.
* Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de.
* Copyright 2016, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2017, Julian Harnath <julian.harnath@rwth-aachen.de>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
@ -56,6 +57,7 @@
#include "support.h"
#include "ScreenshotWindow.h"
#include "UserLoginWindow.h"
#include "WorkStatusView.h"
#undef B_TRANSLATION_CONTEXT
@ -162,6 +164,9 @@ MainWindow::MainWindow(const BMessage& settings)
.Add(fFeaturedPackagesView)
;
fWorkStatusView = new WorkStatusView("work status");
fPackageListView->AttachWorkStatusView(fWorkStatusView);
BView* listArea = new BView("list area", 0);
fListLayout = new BCardLayout();
listArea->SetLayout(fListLayout);
@ -183,6 +188,7 @@ MainWindow::MainWindow(const BMessage& settings)
.End()
.Add(fPackageInfoView)
.End()
.Add(fWorkStatusView)
;
fSplitView->SetCollapsible(0, false);
@ -305,6 +311,7 @@ MainWindow::MessageReceived(BMessage* message)
fModelWorker = B_BAD_THREAD_ID;
_AdoptModel();
fFilterView->AdoptModel(fModel);
fWorkStatusView->SetIdle();
break;
}
case B_SIMPLE_DATA:
@ -500,6 +507,20 @@ MainWindow::MessageReceived(BMessage* message)
_ShowScreenshot();
break;
case MSG_PACKAGE_WORKER_BUSY:
{
BString reason;
status_t status = message->FindString("reason", &reason);
if (status != B_OK)
break;
fWorkStatusView->SetBusy(reason);
break;
}
case MSG_PACKAGE_WORKER_IDLE:
fWorkStatusView->SetIdle();
break;
default:
BWindow::MessageReceived(message);
break;
@ -1093,6 +1114,8 @@ MainWindow::_StartRefreshWorker(bool force)
if (parameters == NULL)
return;
fWorkStatusView->SetBusy(B_TRANSLATE("Refreshing..."));
ObjectDeleter<RefreshWorkerParameters> deleter(parameters);
fModelWorker = spawn_thread(&_RefreshModelThreadWorker, "model loader",
B_LOW_PRIORITY, parameters);
@ -1142,7 +1165,15 @@ MainWindow::_PackageActionWorker(void* arg)
window->fPendingActions.Remove(0);
}
BMessenger messenger(window);
BMessage busyMessage(MSG_PACKAGE_WORKER_BUSY);
BString text(ref->Label());
text << "...";
busyMessage.AddString("reason", text);
messenger.SendMessage(&busyMessage);
ref->Perform();
messenger.SendMessage(MSG_PACKAGE_WORKER_IDLE);
}
return 0;

View File

@ -1,6 +1,7 @@
/*
* Copyright 2013-2014, Stephan Aßmus <superstippi@gmx.de>.
* Copyright 2013, Rene Gollent <rene@gollent.com>.
* Copyright 2017, Julian Harnath <julian.harnath@rwth-aachen.de>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef MAIN_WINDOW_H
@ -24,11 +25,14 @@ class PackageActionsView;
class PackageInfoView;
class PackageListView;
class ScreenshotWindow;
class WorkStatusView;
enum {
MSG_MAIN_WINDOW_CLOSED = 'mwcl',
MSG_PACKAGE_SELECTED = 'pkgs',
MSG_PACKAGE_WORKER_BUSY = 'pkwb',
MSG_PACKAGE_WORKER_IDLE = 'pkwi',
};
@ -95,6 +99,7 @@ private:
PackageListView* fPackageListView;
PackageInfoView* fPackageInfoView;
BSplitView* fSplitView;
WorkStatusView* fWorkStatusView;
ScreenshotWindow* fScreenshotWindow;

View File

@ -20,6 +20,7 @@
#include <Window.h>
#include "MainWindow.h"
#include "WorkStatusView.h"
#undef B_TRANSLATION_CONTEXT
@ -723,7 +724,8 @@ PackageListView::PackageListView(BLocker* modelLock)
:
BColumnListView("package list view", 0, B_FANCY_BORDER, true),
fModelLock(modelLock),
fPackageListener(new(std::nothrow) PackageListener(this))
fPackageListener(new(std::nothrow) PackageListener(this)),
fWorkStatusView(NULL)
{
float scale = be_plain_font->Size() / 12.f;
float spacing = be_control_look->DefaultItemSpacing() * 2;
@ -802,8 +804,13 @@ PackageListView::MessageReceived(BMessage* message)
row->UpdateSummary();
if ((changes & PKG_CHANGED_RATINGS) != 0)
row->UpdateRating();
if ((changes & PKG_CHANGED_STATE) != 0)
if ((changes & PKG_CHANGED_STATE) != 0) {
row->UpdateState();
if (fWorkStatusView != NULL) {
fWorkStatusView->PackageStatusChanged(
row->Package());
}
}
if ((changes & PKG_CHANGED_SIZE) != 0)
row->UpdateSize();
if ((changes & PKG_CHANGED_ICON) != 0)
@ -897,6 +904,13 @@ PackageListView::SelectPackage(const PackageInfoRef& package)
}
void
PackageListView::AttachWorkStatusView(WorkStatusView* view)
{
fWorkStatusView = view;
}
PackageRow*
PackageListView::_FindRow(const PackageInfoRef& package, PackageRow* parent)
{

View File

@ -16,6 +16,8 @@
class PackageRow;
class PackageListener;
class WorkStatusView;
class PackageListView : public BColumnListView {
public:
@ -35,6 +37,8 @@ public:
void SelectPackage(const PackageInfoRef& package);
void AttachWorkStatusView(WorkStatusView* view);
private:
PackageRow* _FindRow(const PackageInfoRef& package,
PackageRow* parent = NULL);
@ -47,6 +51,8 @@ private:
BLocker* fModelLock;
ItemCountView* fItemCountView;
PackageListener* fPackageListener;
WorkStatusView* fWorkStatusView;
};
#endif // PACKAGE_LIST_VIEW_H

View File

@ -0,0 +1,165 @@
/*
* Copyright 2017 Julian Harnath <julian.harnath@rwth-aachen.de>
* All rights reserved. Distributed under the terms of the MIT license.
*/
#include "WorkStatusView.h"
#include <CardLayout.h>
#include <Catalog.h>
#include <LayoutBuilder.h>
#include <SeparatorView.h>
#include <StatusBar.h>
#include <StringView.h>
#include <stdio.h>
#include "BarberPole.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "WorkStatusView"
WorkStatusView::WorkStatusView(const char* name)
:
BView(name, 0),
fProgressBar(new BStatusBar("progress bar")),
fBarberPole(new BarberPole("barber pole")),
fProgressLayout(new BCardLayout()),
fProgressView(new BView("progress view", 0)),
fStatusText(new BStringView("status text", NULL))
{
fProgressView->SetLayout(fProgressLayout);
fProgressLayout->AddView(fBarberPole);
fProgressLayout->AddView(fProgressBar);
fProgressBar->SetMaxValue(1.0f);
BLayoutBuilder::Group<>(this, B_VERTICAL, 0)
.Add(new BSeparatorView())
.AddGroup(B_HORIZONTAL)
.SetInsets(5, 5, 5, 5)
.Add(fProgressLayout, 0.2f)
.Add(fStatusText)
.AddGlue()
.End()
;
}
WorkStatusView::~WorkStatusView()
{
}
void
WorkStatusView::SetBusy(const BString& text)
{
SetText(text);
SetBusy();
}
void
WorkStatusView::SetBusy()
{
fBarberPole->Start();
if (fProgressLayout->VisibleIndex() != 0)
fProgressLayout->SetVisibleItem((int32)0);
}
void
WorkStatusView::SetIdle()
{
fBarberPole->Stop();
fProgressLayout->SetVisibleItem((int32)0);
SetText(NULL);
}
void
WorkStatusView::SetProgress(float value)
{
fProgressBar->SetTo(value);
if (fProgressLayout->VisibleIndex() != 1)
fProgressLayout->SetVisibleItem(1);
}
void
WorkStatusView::SetText(const BString& text)
{
fStatusText->SetText(text);
}
void
WorkStatusView::PackageStatusChanged(const PackageInfoRef& package)
{
switch (package->State()) {
case DOWNLOADING:
fPendingPackages.erase(package->Name());
if (package->Name() != fDownloadingPackage) {
fDownloadingPackage = package->Name();
_SetTextDownloading(package->Title());
}
SetProgress(package->DownloadProgress());
break;
case PENDING:
fPendingPackages.insert(package->Name());
if (package->Name() == fDownloadingPackage)
fDownloadingPackage = "";
if (fDownloadingPackage.IsEmpty()) {
_SetTextPendingDownloads();
SetBusy();
}
break;
case NONE:
case ACTIVATED:
case INSTALLED:
case UNINSTALLED:
if (package->Name() == fDownloadingPackage)
fDownloadingPackage = "";
fPendingPackages.erase(package->Name());
if (fPendingPackages.empty())
SetIdle();
else {
_SetTextPendingDownloads();
SetBusy();
}
break;
}
}
void
WorkStatusView::_SetTextPendingDownloads()
{
BString text;
const size_t pendingCount = fPendingPackages.size();
text << pendingCount;
if (pendingCount > 1)
text << B_TRANSLATE(" packages to download");
else
text << B_TRANSLATE(" package to download");
SetText(text);
}
void
WorkStatusView::_SetTextDownloading(const BString& title)
{
BString text(B_TRANSLATE("Downloading package "));
text << title;
if (!fPendingPackages.empty()) {
text << " (";
text << fPendingPackages.size();
text << B_TRANSLATE(" more to download)");
}
SetText(text);
}

View File

@ -0,0 +1,53 @@
/*
* Copyright 2017 Julian Harnath <julian.harnath@rwth-aachen.de>
* All rights reserved. Distributed under the terms of the MIT license.
*/
#ifndef WORK_STATUS_VIEW_H
#define WORK_STATUS_VIEW_H
#include <View.h>
#include <set>
#include "PackageInfo.h"
class BarberPole;
class BCardLayout;
class BMessageRunner;
class BStatusBar;
class BStringView;
class WorkStatusView : public BView {
public:
WorkStatusView(const char* name);
~WorkStatusView();
void SetBusy(const BString& text);
void SetBusy();
void SetIdle();
void SetProgress(float value);
void SetText(const BString& text);
void PackageStatusChanged(
const PackageInfoRef& package);
private:
void _SetTextPendingDownloads();
void _SetTextDownloading(const BString& title);
private:
BStatusBar* fProgressBar;
BarberPole* fBarberPole;
BCardLayout* fProgressLayout;
BView* fProgressView;
BStringView* fStatusText;
BString fDownloadingPackage;
std::set<BString> fPendingPackages;
};
#endif // WORK_STATUS_VIEW_H