Early work on a package manager GUI.

There is nothing functional yet, I just wanted to try some ideas from the GCI results.

Code is mostly based on Web+ download window, which hapenned to match the mockups pretty well.
Icon is derived from Zumi's work (mix of two different icons)
Icon handling for apps themselves and categories is not done yet, because it depends on how they will be handled by the backend we chose
The install/remove/info buttons don't do anything yet.

Things I'm not sure about :
 * What to put in the menu ? Should it replicate the icons at the bottom ?
 * How to browse categories ? Is a "go up" button enough ? Should we have a BPopupMenu with all categories in a tree (I don't like menutrees in BPopupMenus...) ? A BOutlineListView would be wasting space... any other idea ?
 * Should we keep textual buttons like this, or graphical ones like in the mockup ? Or maybe both ?


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@40040 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Adrien Destugues 2010-12-29 22:01:16 +00:00
parent d074928c03
commit 87ff4c051b
8 changed files with 871 additions and 0 deletions

View File

@ -38,6 +38,7 @@ HaikuSubInclude midiplayer ;
HaikuSubInclude networkstatus ;
HaikuSubInclude overlayimage ;
HaikuSubInclude packageinstaller ;
HaikuSubInclude packagemanager ;
HaikuSubInclude pairs ;
HaikuSubInclude people ;
HaikuSubInclude poorman ;

View File

@ -0,0 +1,317 @@
/*
* Copyright (C) 2010 Stephan Aßmus <superstippi@gmx.de>
* Copyright (C) 2010 Adrien Destugues <pulkomandy@pulkomandy.ath.cx>
*
* Distributed under the terms of the MIT licence.
*/
#include "ApplicationView.h"
#include <stdio.h>
#include <Alert.h>
#include <Bitmap.h>
#include <Button.h>
#include <Clipboard.h>
#include <Directory.h>
#include <Entry.h>
#include <FindDirectory.h>
#include <GroupLayoutBuilder.h>
#include <MenuItem.h>
#include <NodeInfo.h>
#include <NodeMonitor.h>
#include <PopUpMenu.h>
#include <Roster.h>
#include <SpaceLayoutItem.h>
#include <StatusBar.h>
#include <StringView.h>
class IconView : public BView {
public:
IconView(const BEntry& entry)
:
BView("Download icon", B_WILL_DRAW),
fIconBitmap(BRect(0, 0, 31, 31), 0, B_RGBA32),
fDimmedIcon(false)
{
SetDrawingMode(B_OP_OVER);
SetTo(entry);
}
IconView()
:
BView("Download icon", B_WILL_DRAW),
fIconBitmap(BRect(0, 0, 31, 31), 0, B_RGBA32),
fDimmedIcon(false)
{
SetDrawingMode(B_OP_OVER);
memset(fIconBitmap.Bits(), 0, fIconBitmap.BitsLength());
}
IconView(const BMessage* archive)
:
BView("Download icon", B_WILL_DRAW),
fIconBitmap((BMessage*)archive),
fDimmedIcon(true)
{
SetDrawingMode(B_OP_OVER);
}
void SetTo(const BEntry& entry)
{
BNode node(&entry);
BNodeInfo info(&node);
info.GetTrackerIcon(&fIconBitmap, B_LARGE_ICON);
Invalidate();
}
void SetIconDimmed(bool iconDimmed)
{
if (fDimmedIcon != iconDimmed) {
fDimmedIcon = iconDimmed;
Invalidate();
}
}
bool IsIconDimmed() const
{
return fDimmedIcon;
}
status_t SaveSettings(BMessage* archive)
{
return fIconBitmap.Archive(archive);
}
virtual void AttachedToWindow()
{
SetViewColor(Parent()->ViewColor());
}
virtual void Draw(BRect updateRect)
{
if (fDimmedIcon) {
SetDrawingMode(B_OP_ALPHA);
SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_OVERLAY);
SetHighColor(0, 0, 0, 100);
}
DrawBitmapAsync(&fIconBitmap);
}
virtual BSize MinSize()
{
return BSize(fIconBitmap.Bounds().Width(), fIconBitmap.Bounds().Height());
}
virtual BSize PreferredSize()
{
return MinSize();
}
virtual BSize MaxSize()
{
return MinSize();
}
private:
BBitmap fIconBitmap;
bool fDimmedIcon;
};
class SmallButton : public BButton {
public:
SmallButton(const char* label, BMessage* message = NULL)
:
BButton(label, message)
{
BFont font;
GetFont(&font);
float size = ceilf(font.Size() * 0.8);
font.SetSize(max_c(8, size));
SetFont(&font, B_FONT_SIZE);
}
};
// #pragma mark - ApplicationView
ApplicationView::ApplicationView(const char* name, const char* icon,
const char* description)
:
BGroupView(B_HORIZONTAL, 8)
{
Init(NULL);
fNameView->SetText(name);
fInfoView->SetText(description);
}
ApplicationView::ApplicationView(const BMessage* archive)
:
BGroupView(B_HORIZONTAL, 8)
{
Init(archive);
BString temp;
archive->FindString("appname",&temp);
fNameView->SetText(temp);
archive->FindString("appdesc",&temp);
float ver = 0.0;
archive->FindFloat("appver", ver);
int size = 0;
archive->FindInt32("appsize", size);
temp << " Version: " << ver << " Size: " << size;
fInfoView->SetText(temp);
}
bool
ApplicationView::Init(const BMessage* archive)
{
SetViewColor(245, 245, 245);
SetFlags(Flags() | B_FULL_UPDATE_ON_RESIZE | B_WILL_DRAW);
if (archive) {
fIconView = new IconView(archive);
} else
fIconView = new IconView();
if (archive) {
fTopButton = new SmallButton("Info", new BMessage('NADA'));
fBottomButton = new SmallButton("Install", new BMessage('NADA'));
}
fInfoView = new BStringView("info view", "");
fNameView = new BStringView("name view", "");
BGroupLayout* layout = GroupLayout();
layout->SetInsets(8, 5, 5, 6);
layout->AddView(fIconView);
BView* verticalGroup = BGroupLayoutBuilder(B_VERTICAL, 3)
.Add(fNameView)
.Add(fInfoView)
.TopView()
;
verticalGroup->SetViewColor(ViewColor());
layout->AddView(verticalGroup);
if (fTopButton && fBottomButton) {
verticalGroup = BGroupLayoutBuilder(B_VERTICAL, 3)
.Add(fTopButton)
.Add(fBottomButton)
.TopView()
;
}
verticalGroup->SetViewColor(ViewColor());
layout->AddView(verticalGroup);
BFont font;
fInfoView->GetFont(&font);
float fontSize = font.Size() * 0.8f;
font.SetSize(max_c(8.0f, fontSize));
fInfoView->SetFont(&font, B_FONT_SIZE);
fInfoView->SetHighColor(tint_color(fInfoView->LowColor(),
B_DARKEN_4_TINT));
fInfoView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
fNameView->GetFont(&font);
font.SetSize(font.Size() * 2.0f);
fNameView->SetFont(&font, B_FONT_SIZE);
fNameView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
return true;
}
void
ApplicationView::AttachedToWindow()
{
if (fTopButton)
fTopButton->SetTarget(this);
if (fBottomButton)
fBottomButton->SetTarget(this);
}
void
ApplicationView::AllAttached()
{
SetViewColor(B_TRANSPARENT_COLOR);
SetLowColor(245, 245, 245);
SetHighColor(tint_color(LowColor(), B_DARKEN_1_TINT));
}
void
ApplicationView::Draw(BRect updateRect)
{
BRect bounds(Bounds());
bounds.bottom--;
FillRect(bounds, B_SOLID_LOW);
bounds.bottom++;
StrokeLine(bounds.LeftBottom(), bounds.RightBottom());
}
void
ApplicationView::MessageReceived(BMessage* message)
{
switch (message->what) {
case B_NODE_MONITOR:
{
int32 opCode;
if (message->FindInt32("opcode", &opCode) != B_OK)
break;
switch (opCode) {
case B_ENTRY_MOVED:
{
// Follow the entry to the new location
dev_t device;
ino_t directory;
const char* name;
if (message->FindInt32("device",
reinterpret_cast<int32*>(&device)) != B_OK
|| message->FindInt64("to directory",
reinterpret_cast<int64*>(&directory)) != B_OK
|| message->FindString("name", &name) != B_OK
|| strlen(name) == 0) {
break;
}
Window()->PostMessage(SAVE_SETTINGS);
break;
}
case B_ATTR_CHANGED:
{
fIconView->SetIconDimmed(false);
break;
}
}
break;
}
default:
BGroupView::MessageReceived(message);
}
}
void
ApplicationView::ShowContextMenu(BPoint screenWhere)
{
screenWhere += BPoint(2, 2);
BPopUpMenu* contextMenu = new BPopUpMenu("download context");
BMenuItem* copyURL = new BMenuItem("Copy URL to clipboard",
new BMessage('NADA'));
contextMenu->AddItem(copyURL);
BMenuItem* openFolder = new BMenuItem("Open containing folder",
new BMessage('NADA'));
contextMenu->AddItem(openFolder);
contextMenu->SetTargetForItems(this);
contextMenu->Go(screenWhere, true, true, true);
}

View File

@ -0,0 +1,54 @@
/*
* Copyright (C) 2010 Stephan Aßmus <superstippi@gmx.de>
* Copyright (C) 2010 Adrien Destugues <pulkomandy@pulkomandy.ath.cx>
*
* Distributed under the terms of the MIT licence.
*/
#ifndef DOWNLOAD_PROGRESS_VIEW_H
#define DOWNLOAD_PROGRESS_VIEW_H
#include <GroupView.h>
#include <Path.h>
#include <String.h>
class BEntry;
class BStatusBar;
class BStringView;
class BWebDownload;
class IconView;
class SmallButton;
enum {
SAVE_SETTINGS = 'svst'
};
class ApplicationView : public BGroupView {
public:
ApplicationView(const char* name,
const char* icon,
const char* description);
ApplicationView(const BMessage* archive);
bool Init(const BMessage* archive = NULL);
virtual void AttachedToWindow();
virtual void AllAttached();
virtual void Draw(BRect updateRect);
virtual void MessageReceived(BMessage* message);
void ShowContextMenu(BPoint screenWhere);
private:
IconView* fIconView;
BStringView* fNameView;
BStringView* fInfoView;
SmallButton* fTopButton;
SmallButton* fBottomButton;
};
#endif // DOWNLOAD_PROGRESS_VIEW_H

View File

@ -0,0 +1,263 @@
/*
* Copyright (C) 2010 Stephan Aßmus <superstippi@gmx.de>
* Copyright (C) 2010 Adrien Destugues <pulkomandy@pulkomandy.ath.cx>
*
* Distributed under the terms of the MIT licence.
*/
#include "ApplicationWindow.h"
#include <stdio.h>
#include <Alert.h>
#include <Button.h>
#include <ControlLook.h>
#include <Entry.h>
#include <File.h>
#include <FindDirectory.h>
#include <GroupLayout.h>
#include <GroupLayoutBuilder.h>
#include <MenuBar.h>
#include <MenuItem.h>
#include <Path.h>
#include <Roster.h>
#include <ScrollView.h>
#include <SeparatorView.h>
#include <SpaceLayoutItem.h>
#include "ApplicationView.h"
enum {
INIT = 'init',
};
class ApplicationsContainerView : public BGroupView {
public:
ApplicationsContainerView()
:
BGroupView(B_VERTICAL, 0.0)
{
SetFlags(Flags() | B_PULSE_NEEDED);
SetViewColor(245, 245, 245);
AddChild(BSpaceLayoutItem::CreateGlue());
}
virtual BSize MinSize()
{
BSize minSize = BGroupView::MinSize();
return BSize(minSize.width, 80);
}
protected:
virtual void DoLayout()
{
BGroupView::DoLayout();
if (BScrollBar* scrollBar = ScrollBar(B_VERTICAL)) {
BSize minSize = BGroupView::MinSize();
float height = Bounds().Height();
float max = minSize.height - height;
scrollBar->SetRange(0, max);
if (minSize.height > 0)
scrollBar->SetProportion(height / minSize.height);
else
scrollBar->SetProportion(1);
}
}
};
class ApplicationContainerScrollView : public BScrollView {
public:
ApplicationContainerScrollView(BView* target)
:
BScrollView("Applications scroll view", target, 0, false, true,
B_NO_BORDER)
{
}
protected:
virtual void DoLayout()
{
BScrollView::DoLayout();
// Tweak scroll bar layout to hide part of the frame for better looks.
BScrollBar* scrollBar = ScrollBar(B_VERTICAL);
scrollBar->MoveBy(1, -1);
scrollBar->ResizeBy(0, 2);
Target()->ResizeBy(1, 0);
// Set the scroll steps
if (BView* item = Target()->ChildAt(0)) {
scrollBar->SetSteps(item->MinSize().height + 1,
item->MinSize().height + 1);
}
}
};
// #pragma mark -
ApplicationWindow::ApplicationWindow(BRect frame, bool visible)
: BWindow(frame, "Package Manager",
B_TITLED_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL,
B_AUTO_UPDATE_SIZE_LIMITS | B_ASYNCHRONOUS_CONTROLS | B_NOT_ZOOMABLE),
fMinimizeOnClose(false)
{
SetPulseRate(1000000);
SetLayout(new BGroupLayout(B_VERTICAL, 0.0));
ApplicationsContainerView* downloadsGroupView = new ApplicationsContainerView();
fApplicationViewsLayout = downloadsGroupView->GroupLayout();
BMenuBar* menuBar = new BMenuBar("Menu bar");
BMenu* menu = new BMenu("Actions");
menu->AddItem(new BMenuItem("Apply changes",
new BMessage('NADA')));
menu->AddItem(new BMenuItem("Exit", new BMessage(B_QUIT_REQUESTED)));
menuBar->AddItem(menu);
fApplicationsScrollView = new ApplicationContainerScrollView(downloadsGroupView);
fDiscardButton = new BButton("Revert",
new BMessage('NADA'));
fDiscardButton->SetEnabled(false);
fApplyChangesButton = new BButton("Apply",
new BMessage('NADA'));
fApplyChangesButton->SetEnabled(false);
const float spacing = be_control_look->DefaultItemSpacing();
AddChild(BGroupLayoutBuilder(B_VERTICAL, 0.0)
.Add(menuBar)
.Add(fApplicationsScrollView)
.Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER))
.Add(BGroupLayoutBuilder(B_HORIZONTAL, spacing)
.AddGlue()
.Add(fApplyChangesButton)
.Add(fDiscardButton)
.SetInsets(12, 5, 12, 5)
)
);
PostMessage(INIT);
if (!visible)
Hide();
Show();
}
ApplicationWindow::~ApplicationWindow()
{
}
void
ApplicationWindow::DispatchMessage(BMessage* message, BHandler* target)
{
// We need to intercept mouse down events inside the area of download
// progress views (regardless of whether they have children at the click),
// so that they may display a context menu.
BPoint where;
int32 buttons;
if (message->what == B_MOUSE_DOWN
&& message->FindPoint("screen_where", &where) == B_OK
&& message->FindInt32("buttons", &buttons) == B_OK
&& (buttons & B_SECONDARY_MOUSE_BUTTON) != 0) {
for (int32 i = fApplicationViewsLayout->CountItems() - 1;
BLayoutItem* item = fApplicationViewsLayout->ItemAt(i); i--) {
ApplicationView* view = dynamic_cast<ApplicationView*>(
item->View());
if (!view)
continue;
BPoint viewWhere(where);
view->ConvertFromScreen(&viewWhere);
if (view->Bounds().Contains(viewWhere)) {
view->ShowContextMenu(where);
return;
}
}
}
BWindow::DispatchMessage(message, target);
}
void
ApplicationWindow::MessageReceived(BMessage* message)
{
switch (message->what) {
case INIT:
{
break;
}
default:
BWindow::MessageReceived(message);
break;
}
}
bool
ApplicationWindow::QuitRequested()
{
if (fMinimizeOnClose) {
if (!IsMinimized())
Minimize(true);
} else {
if (!IsHidden())
Hide();
}
return false;
}
void
ApplicationWindow::SetMinimizeOnClose(bool minimize)
{
if (Lock()) {
fMinimizeOnClose = minimize;
Unlock();
}
}
// #pragma mark - private
void
ApplicationWindow::AddCategory(const char* name, const char* icon,
const char* description)
{
ApplicationView* category = new ApplicationView(name, icon, description);
fApplicationViewsLayout->AddView(0, category);
}
void
ApplicationWindow::AddApplication(const BMessage* info)
{
ApplicationView* app = new ApplicationView(info);
fApplicationViewsLayout->AddView(0, app);
}
void
ApplicationWindow::_ValidateButtonStatus()
{
int32 finishedCount = 0;
int32 missingCount = 0;
for (int32 i = fApplicationViewsLayout->CountItems() - 1;
BLayoutItem* item = fApplicationViewsLayout->ItemAt(i); i--) {
ApplicationView* view = dynamic_cast<ApplicationView*>(
item->View());
if (!view)
continue;
}
fDiscardButton->SetEnabled(finishedCount > 0);
fApplyChangesButton->SetEnabled(missingCount > 0);
}

View File

@ -0,0 +1,51 @@
/*
* Copyright (C) 2010 Stephan Aßmus <superstippi@gmx.de>
* Copyright (C) 2010 Adrien Destugues <pulkomandy@pulkomandy.ath.cx>
*
* Distributed under the terms of the MIT Licence.
*/
#ifndef APPLICATION_WINDOW_H
#define APPLICATION_WINDOW_H
#include <String.h>
#include <Window.h>
class BButton;
class BFile;
class BGroupLayout;
class BScrollView;
class BWebApplication;
class SettingsMessage;
class ApplicationWindow : public BWindow {
public:
ApplicationWindow(BRect frame, bool visible);
virtual ~ApplicationWindow();
virtual void DispatchMessage(BMessage* message,
BHandler* target);
virtual void MessageReceived(BMessage* message);
virtual bool QuitRequested();
void SetMinimizeOnClose(bool minimize);
void AddCategory(const char* name,
const char* icon,
const char* description);
void AddApplication(const BMessage* info);
private:
void _ValidateButtonStatus();
private:
BScrollView* fApplicationsScrollView;
BGroupLayout* fApplicationViewsLayout;
BButton* fDiscardButton;
BButton* fApplyChangesButton;
BString fApplicationPath;
bool fMinimizeOnClose;
};
#endif // APPLICATION_WINDOW_H

View File

@ -0,0 +1,15 @@
SubDir HAIKU_TOP src apps packagemanager ;
# UsePrivateHeaders shared interface ;
# SubDirHdrs $(HAIKU_TOP) headers libs zlib ;
#SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src kits interface ] ;
Application PackageManager :
ApplicationView.cpp
ApplicationWindow.cpp
PackageManager.cpp
: be $(HAIKU_LOCALE_LIBS) translation $(TARGET_LIBSUPC++)
: PackageManager.rdef
;

View File

@ -0,0 +1,144 @@
/*
* Copyright 2010, Haiku, Inc.
* Distributed under the terms of the MIT license.
*
* Author:
* Adrien Destugues <pulkomandy@pulkomandy.ath.cx>
*/
#include "ApplicationWindow.h"
#include <Alert.h>
#include <Application.h>
#include <Catalog.h>
#include <Entry.h>
#include <TextView.h>
#include <Locale.h>
#undef B_TRANSLATE_CONTEXT
#define B_TRANSLATE_CONTEXT "main"
class PackageManager : public BApplication {
public:
PackageManager();
~PackageManager();
void RefsReceived(BMessage *msg);
void ArgvReceived(int32 argc, char **argv);
void ReadyToRun();
void MessageReceived(BMessage *msg);
void AboutRequested();
private:
ApplicationWindow* fMainWindow;
};
PackageManager::PackageManager()
: BApplication("application/x-vnd.Haiku-PackageManager")
{
}
PackageManager::~PackageManager()
{
}
void
PackageManager::ReadyToRun()
{
// Open the main window
BRect frame(20, 40, 600, 400);
fMainWindow = new ApplicationWindow(frame, true);
// Add some test content to the window
fMainWindow->AddCategory("Test category", "development",
"Thisis a short description of the category");
BMessage entryInfo;
// This message is a convenient way of storing various infos about an app.
// What's inside :
// icon as an archived BBitmap
entryInfo.AddString("appname", "Test Application");
entryInfo.AddString("appdesc", "Some text telling what it does");
entryInfo.AddFloat("appver", 1.302);
// as a float so it can be compared to decide if there is an update
entryInfo.AddInt32("appsize", 123456); // this is in bytes
fMainWindow->AddApplication(&entryInfo);
fMainWindow->Show();
}
void
PackageManager::RefsReceived(BMessage *msg)
{
uint32 type;
int32 i, count;
status_t ret = msg->GetInfo("refs", &type, &count);
if (ret != B_OK || type != B_REF_TYPE)
return;
entry_ref ref;
for (i = 0; i < count; i++) {
if (msg->FindRef("refs", i, &ref) == B_OK) {
// TODO: handle bundle-files for installation
}
}
}
void
PackageManager::ArgvReceived(int32 argc, char **argv)
{
// TODO: handle command-line driven actions
}
void
PackageManager::MessageReceived(BMessage *msg)
{
switch (msg->what) {
default:
BApplication::MessageReceived(msg);
}
}
void
PackageManager::AboutRequested()
{
BAlert *about = new BAlert("about",
B_TRANSLATE("PackageManager\n"
"Written by Adrien Destugues\n"
"Copyright 2010 Haiku, Inc. \n"),
B_TRANSLATE("OK"));
BTextView *view = about->TextView();
BFont font;
view->SetStylable(true);
view->GetFont(&font);
font.SetFace(B_BOLD_FACE);
font.SetSize(font.Size() * 1.5);
view->SetFontAndColor(0, 17, &font);
about->Go();
}
int
main(void)
{
PackageManager app;
app.Run();
return 0;
}

View File

@ -0,0 +1,26 @@
resource app_signature "application/x-vnd.Haiku-PackageManager";
resource app_flags B_SINGLE_LAUNCH;
resource vector_icon {
$"6E6369660C04006605000200060237D82B3BACE0BC056F381D3E4AB7654A7200"
$"00D6DEEEFFA2ACBF0200060237D82B3BACE0BC056F381D3E4AB7654A720000A1"
$"A7B3FF7A849903AAB0BA02000602BC42C0BAFFBEBB0A953C4AEB4A9136468CBF"
$"0080858EFF5C6066020006023B966D3B95A9BAFE123AFEB54997F949D8D800FF"
$"ED00FFFFB80002000602B885D93BCB6FBAC787B7B21C4B7A9C4A838200FFB800"
$"FFDC7D0B020006023C2D5037C5C6B380243805214A178B4603F200FEFFEAFFFF"
$"FF7905D905FF020002023AE951B9F63F3E400B3F4E9C462C4147F2CA0000A2FF"
$"00FF2BCD05FF130A043F5AC154CBA22C52274E0A07C6CCBF4A5049C334C6414C"
$"52495C57575E450A03254A284D383D0A083C25262D2245383D274E3F5A504954"
$"310A0A3D4640413C3F33453248284D274E3F5AC334C6413E4B0A05324833453C"
$"3F383D284D0A0540413D463E4BC334C64150490A0340413C253C3F0A033C2538"
$"3D3C3F0A04504954313C2540410A043C25262D2245383D0A043F5A3B4A233E27"
$"4E0A064B3E474244534756504954310A063C252D292E2D4036453554310A0450"
$"4947564B3E54310A044756445347424B3E0A043C252D29453554310A04453540"
$"362E2D2D2906069F0E24382438263434302C30343038402C3420342834282428"
$"150A0001001001158402040A0001011001158102040A0001021001158402040A"
$"0101031001178302040A020104000A030105000A030106000A040107000A0501"
$"08000A050109000A05010A000A01010B1001178302040A06010B000A01010C10"
$"01178302040A01010D1001178302040A07010E000A09010F000A080110000A0A"
$"0111000A0B011202BE1E0DBF64C43F64C4BE1E0D46D63B4A99F90A01011212BE"
$"1E0DBF64C43F64C4BE1E0D46D63B4A99F90117820004"
};