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:
parent
8f265e03c9
commit
65f4a22735
@ -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;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -540,11 +546,28 @@ private:
|
|||||||
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();
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user