registrar: Shutdown Icon Animation

In the case that an application is blocked by a model panel during shutdown, the icon will now show a "waiting" animation
like in BeOS R5.

Fixes #7980

Change-Id: Iec79fcaf431407ff51430a1a8e4c8ec41571059d
Reviewed-on: https://review.haiku-os.org/c/haiku/+/4001
Reviewed-by: Jérôme Duval <jerome.duval@gmail.com>
Tested-by: Commit checker robot <no-reply+buildbot@haiku-os.org>
This commit is contained in:
CodeforEvolution 2021-05-13 23:22:30 -05:00 committed by Adrien Destugues
parent 8f265e03c9
commit 65f4a22735
2 changed files with 234 additions and 40 deletions

View File

@ -3,6 +3,7 @@
* Copyright 2006-2009, Axel Dörfler, axeld@pinc-software.de. * Copyright 2006-2009, Axel Dörfler, axeld@pinc-software.de.
* Copyright 2006-2008, Stephan Aßmus. * Copyright 2006-2008, Stephan Aßmus.
* Copyright 2006, Ryan Leavengood. * Copyright 2006, Ryan Leavengood.
* Copyright 2021, Jacob Secunda.
* *
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
*/ */
@ -76,6 +77,10 @@ static const int kStripeWidth = 30;
static const int kIconVSpacing = 6; static const int kIconVSpacing = 6;
static const int kIconSize = 32; static const int kIconSize = 32;
// The speed of the animation played when an application is blocked on a modal
// panel.
static const bigtime_t kIconAnimateInterval = 50000; // 0.05 s
// message what fields (must not clobber the registrar's message namespace) // message what fields (must not clobber the registrar's message namespace)
enum { enum {
MSG_PHASE_TIMED_OUT = 'phto', MSG_PHASE_TIMED_OUT = 'phto',
@ -414,18 +419,16 @@ public:
return B_OK; return B_OK;
} }
status_t AddApp(team_id team, BBitmap* miniIcon, BBitmap* largeIcon) status_t AddApp(team_id team, BBitmap* appIcon)
{ {
AppInfo* info = new(nothrow) AppInfo; AppInfo* info = new(nothrow) AppInfo;
if (!info) { if (!info) {
delete miniIcon; delete appIcon;
delete largeIcon;
return B_NO_MEMORY; return B_NO_MEMORY;
} }
info->team = team; info->team = team;
info->miniIcon = miniIcon; info->appIcon = appIcon;
info->largeIcon = largeIcon;
if (!fAppInfos.AddItem(info)) { if (!fAppInfos.AddItem(info)) {
delete info; delete info;
@ -480,6 +483,11 @@ public:
} }
} }
void SetWaitAnimationEnabled(bool enable)
{
fRootView->IconWaitAnimationEnabled(enable);
}
void SetWaitForShutdown() void SetWaitForShutdown()
{ {
fKillAppButton->Hide(); fKillAppButton->Hide();
@ -507,13 +515,11 @@ public:
private: private:
struct AppInfo { struct AppInfo {
team_id team; team_id team;
BBitmap *miniIcon; BBitmap* appIcon;
BBitmap *largeIcon;
~AppInfo() ~AppInfo()
{ {
delete miniIcon; delete appIcon;
delete largeIcon;
} }
}; };
@ -539,12 +545,29 @@ private:
class TAlertView : public BView { class TAlertView : public BView {
public: public:
TAlertView(BRect frame, const char* name, uint32 resizeMask, TAlertView(BRect frame, const char* name, uint32 resizeMask,
uint32 flags) uint32 flags)
: BView(frame, name, resizeMask, flags | B_WILL_DRAW), :
fAppInfo(NULL) BView(frame, name, resizeMask, flags | B_WILL_DRAW),
fCurrentIconBitmap(NULL),
fNormalIconBitmap(NULL),
fTintedIconBitmap(NULL),
fAnimationActive(false),
fAnimationWorker(-1),
fCurrentAnimationRow(-1),
fAnimationLightenPhase(true)
{ {
} }
~TAlertView()
{
atomic_set(&fAnimationActive, false);
wait_for_thread(fAnimationWorker, NULL);
delete fCurrentIconBitmap;
delete fNormalIconBitmap;
delete fTintedIconBitmap;
}
virtual void Draw(BRect updateRect) virtual void Draw(BRect updateRect)
{ {
BRect stripeRect = Bounds(); BRect stripeRect = Bounds();
@ -552,26 +575,193 @@ private:
SetHighColor(tint_color(ViewColor(), B_DARKEN_1_TINT)); SetHighColor(tint_color(ViewColor(), B_DARKEN_1_TINT));
FillRect(stripeRect); FillRect(stripeRect);
if (fAppInfo && fAppInfo->largeIcon) { if (fCurrentIconBitmap != NULL && fCurrentIconBitmap->IsValid()) {
if (fAppInfo->largeIcon->ColorSpace() == B_RGBA32) { if (fCurrentIconBitmap->ColorSpace() == B_RGBA32) {
SetDrawingMode(B_OP_ALPHA); SetDrawingMode(B_OP_ALPHA);
SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY); SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
} else } else
SetDrawingMode(B_OP_OVER); SetDrawingMode(B_OP_OVER);
DrawBitmapAsync(fAppInfo->largeIcon, DrawBitmap(fCurrentIconBitmap,
BPoint(kStripeWidth - kIconSize / 2, kIconVSpacing)); BPoint(kStripeWidth - kIconSize / 2, kIconVSpacing));
} }
} }
void SetAppInfo(AppInfo* info) void SetAppInfo(AppInfo* info)
{ {
fAppInfo = info; IconWaitAnimationEnabled(false);
BAutolock lock(Window());
if (!lock.IsLocked())
return;
delete fCurrentIconBitmap;
fCurrentIconBitmap = NULL;
delete fNormalIconBitmap;
fNormalIconBitmap = NULL;
delete fTintedIconBitmap;
fTintedIconBitmap = NULL;
if (info != NULL && info->appIcon != NULL
&& info->appIcon->IsValid()) {
fCurrentIconBitmap = new BBitmap(BRect(0, 0, 31, 31), B_RGBA32);
if (fCurrentIconBitmap == NULL
|| fCurrentIconBitmap->ImportBits(info->appIcon) != B_OK) {
delete fCurrentIconBitmap;
fCurrentIconBitmap = NULL;
}
}
Invalidate(); Invalidate();
} }
void IconWaitAnimationEnabled(bool enable)
{
if (atomic_get(&fAnimationActive) == enable)
return;
BAutolock lock(Window());
if (!lock.IsLocked())
return;
if (enable) {
if (fCurrentIconBitmap == NULL
|| !fCurrentIconBitmap->IsValid())
return;
if (fNormalIconBitmap == NULL
|| !fNormalIconBitmap->IsValid()) {
delete fNormalIconBitmap;
fNormalIconBitmap = NULL;
fNormalIconBitmap = new BBitmap(BRect(0, 0, 31, 31),
B_BITMAP_NO_SERVER_LINK, B_RGBA32);
if (fNormalIconBitmap == NULL
|| fNormalIconBitmap->ImportBits(fCurrentIconBitmap)
!= B_OK) {
delete fNormalIconBitmap;
fNormalIconBitmap = NULL;
return;
}
}
if (fTintedIconBitmap == NULL
|| !fTintedIconBitmap->IsValid()) {
delete fTintedIconBitmap;
fTintedIconBitmap = NULL;
fTintedIconBitmap = new BBitmap(BRect(0, 0, 31, 31),
B_BITMAP_NO_SERVER_LINK, B_RGBA32);
if (fTintedIconBitmap == NULL
|| fTintedIconBitmap->ImportBits(fNormalIconBitmap)
!= B_OK) {
delete fTintedIconBitmap;
fTintedIconBitmap = NULL;
return;
}
int32 width =
fTintedIconBitmap->Bounds().IntegerWidth() + 1;
int32 height =
fTintedIconBitmap->Bounds().IntegerHeight() + 1;
int32 rowLength = fTintedIconBitmap->BytesPerRow();
uint8* iconBits = (uint8*)fTintedIconBitmap->Bits();
for (int32 y = 0; y < height; y++) {
for (int32 x = 0; x < width; x++) {
int32 offset = (y * rowLength) + (x * 4);
rgb_color pixelColor = make_color(iconBits[offset],
iconBits[offset + 1], iconBits[offset + 2],
iconBits[offset + 3]);
pixelColor = tint_color(pixelColor,
B_DARKEN_2_TINT);
iconBits[offset] = pixelColor.red;
iconBits[offset + 1] = pixelColor.green;
iconBits[offset + 2] = pixelColor.blue;
iconBits[offset + 3] = pixelColor.alpha;
}
}
}
fAnimationWorker = spawn_thread(&_AnimateWaitIconWorker,
"thumb twiddling", B_DISPLAY_PRIORITY, this);
if (fAnimationWorker < B_NO_ERROR)
return;
atomic_set(&fAnimationActive, true);
if (resume_thread(fAnimationWorker) != B_OK)
atomic_set(&fAnimationActive, false);
} else {
atomic_set(&fAnimationActive, false);
wait_for_thread(fAnimationWorker, NULL);
fCurrentAnimationRow = -1;
fAnimationLightenPhase = true;
if (fCurrentIconBitmap != NULL && fNormalIconBitmap != NULL)
fCurrentIconBitmap->ImportBits(fNormalIconBitmap);
}
}
private: private:
const AppInfo *fAppInfo; status_t _AnimateWaitIcon()
{
while (atomic_get(&fAnimationActive)) {
if (LockLooperWithTimeout(kIconAnimateInterval) != B_OK)
continue;
if (fCurrentAnimationRow < 0) {
fCurrentAnimationRow =
fCurrentIconBitmap->Bounds().IntegerHeight();
fAnimationLightenPhase = !fAnimationLightenPhase;
}
BBitmap* sourceBitmap = fAnimationLightenPhase ?
fNormalIconBitmap : fTintedIconBitmap;
fCurrentIconBitmap->ImportBits(sourceBitmap,
BPoint(0, fCurrentAnimationRow),
BPoint(0, fCurrentAnimationRow),
sourceBitmap->Bounds().IntegerWidth(), 1);
fCurrentAnimationRow--;
Invalidate();
UnlockLooper();
snooze(kIconAnimateInterval);
}
return B_OK;
}
static status_t _AnimateWaitIconWorker(void* cookie)
{
TAlertView* ourView = (TAlertView*)cookie;
return ourView->_AnimateWaitIcon();
}
private:
BBitmap* fCurrentIconBitmap;
BBitmap* fNormalIconBitmap;
BBitmap* fTintedIconBitmap;
int32 fAnimationActive;
thread_id fAnimationWorker;
int32 fCurrentAnimationRow;
bool fAnimationLightenPhase;
}; };
private: private:
@ -1006,35 +1196,21 @@ ShutdownProcess::_AddShutdownWindowApps(AppInfoList& infos)
strerror(error)); strerror(error));
} }
// get the application icons // get the application icon
color_space format = B_RGBA32; BBitmap* appIcon = new(nothrow) BBitmap(BRect(0, 0, 31, 31),
B_BITMAP_NO_SERVER_LINK, B_RGBA32);
// mini icon if (appIcon != NULL) {
BBitmap* miniIcon = new(nothrow) BBitmap(BRect(0, 0, 15, 15), format); error = appIcon->InitCheck();
if (miniIcon != NULL) {
error = miniIcon->InitCheck();
if (error == B_OK) if (error == B_OK)
error = appFileInfo.GetTrackerIcon(miniIcon, B_MINI_ICON); error = appFileInfo.GetTrackerIcon(appIcon, B_LARGE_ICON);
if (error != B_OK) { if (error != B_OK) {
delete miniIcon; delete appIcon;
miniIcon = NULL; appIcon = NULL;
}
}
// mini icon
BBitmap* largeIcon = new(nothrow) BBitmap(BRect(0, 0, 31, 31), format);
if (largeIcon != NULL) {
error = largeIcon->InitCheck();
if (error == B_OK)
error = appFileInfo.GetTrackerIcon(largeIcon, B_LARGE_ICON);
if (error != B_OK) {
delete largeIcon;
largeIcon = NULL;
} }
} }
// add the app // add the app
error = fWindow->AddApp(info->team, miniIcon, largeIcon); error = fWindow->AddApp(info->team, appIcon);
if (error != B_OK) { if (error != B_OK) {
WARNING("ShutdownProcess::_AddShutdownWindowApps(): Failed to " WARNING("ShutdownProcess::_AddShutdownWindowApps(): Failed to "
"add app to the shutdown window: %s\n", strerror(error)); "add app to the shutdown window: %s\n", strerror(error));
@ -1098,6 +1274,17 @@ ShutdownProcess::_SetShutdownWindowKillButtonEnabled(bool enabled)
} }
void
ShutdownProcess::_SetShutdownWindowWaitAnimationEnabled(bool enabled)
{
if (fHasGUI) {
BAutolock _(fWindow);
fWindow->SetWaitAnimationEnabled(enabled);
}
}
void void
ShutdownProcess::_SetShutdownWindowWaitForShutdown() ShutdownProcess::_SetShutdownWindowWaitForShutdown()
{ {
@ -1719,6 +1906,7 @@ ShutdownProcess::_QuitBlockingApp(AppInfoList& list, team_id team,
_SetShutdownWindowText(buffer.String()); _SetShutdownWindowText(buffer.String());
_SetShutdownWindowCurrentApp(team); _SetShutdownWindowCurrentApp(team);
_SetShutdownWindowKillButtonEnabled(true); _SetShutdownWindowKillButtonEnabled(true);
_SetShutdownWindowWaitAnimationEnabled(true);
} }
if (modal || debugged) { if (modal || debugged) {
@ -1760,6 +1948,7 @@ ShutdownProcess::_QuitBlockingApp(AppInfoList& list, team_id team,
} }
_SetShutdownWindowKillButtonEnabled(false); _SetShutdownWindowKillButtonEnabled(false);
_SetShutdownWindowWaitAnimationEnabled(false);
if (appGone) if (appGone)
return; return;
@ -1816,6 +2005,7 @@ ShutdownProcess::_DisplayAbortingApp(team_id team)
buffer.ReplaceFirst("%appName%", appName); buffer.ReplaceFirst("%appName%", appName);
// set up the window // set up the window
_SetShutdownWindowWaitAnimationEnabled(false);
_SetShutdownWindowCurrentApp(team); _SetShutdownWindowCurrentApp(team);
_SetShutdownWindowText(buffer.String()); _SetShutdownWindowText(buffer.String());
_SetShutdownWindowWaitForAbortedOK(); _SetShutdownWindowWaitForAbortedOK();

View File

@ -1,5 +1,7 @@
/* /*
* Copyright 2005, Ingo Weinhold, bonefish@users.sf.net. * Copyright 2005, Ingo Weinhold, bonefish@users.sf.net.
* Copyright 2021, Jacob Secunda.
*
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
* *
* Manages the shutdown process. * Manages the shutdown process.
@ -64,6 +66,8 @@ private:
bool enabled); bool enabled);
void _SetShutdownWindowKillButtonEnabled( void _SetShutdownWindowKillButtonEnabled(
bool enabled); bool enabled);
void _SetShutdownWindowWaitAnimationEnabled(
bool enabled);
void _SetShutdownWindowWaitForShutdown(); void _SetShutdownWindowWaitForShutdown();
void _SetShutdownWindowWaitForAbortedOK(); void _SetShutdownWindowWaitForAbortedOK();