Use BCardLayout instead of manual BView switching

Structures MainWindow around a Conversations→BLayoutItem map &
BCardLayout, instead of directly removing and adding ConversationViews
every time.
This commit is contained in:
Jaidyn Ann 2022-02-20 21:00:56 -06:00
parent eb60c94d68
commit 6f01818e8a
3 changed files with 191 additions and 88 deletions

View File

@ -14,6 +14,7 @@
#include <Application.h> #include <Application.h>
#include <Alert.h> #include <Alert.h>
#include <Beep.h> #include <Beep.h>
#include <CardLayout.h>
#include <Catalog.h> #include <Catalog.h>
#include <LayoutBuilder.h> #include <LayoutBuilder.h>
#include <MenuBar.h> #include <MenuBar.h>
@ -87,6 +88,15 @@ MainWindow::Start()
} }
void
MainWindow::Show()
{
if (fChatLayout->CountItems() == 0)
SetConversation(NULL);
BWindow::Show();
}
bool bool
MainWindow::QuitRequested() MainWindow::QuitRequested()
{ {
@ -101,14 +111,20 @@ MainWindow::QuitRequested()
button_index = alert->Go(); button_index = alert->Go();
} }
AppPreferences::Get()->MainWindowListWeight
= fSplitView->ItemWeight((int32)0);
AppPreferences::Get()->MainWindowChatWeight
= fSplitView->ItemWeight((int32)1);
AppPreferences::Get()->MainWindowRect = Frame(); AppPreferences::Get()->MainWindowRect = Frame();
_SaveWeights();
if(button_index == 0) { if (button_index == 0) {
fServer->Quit(); fServer->Quit();
// ConversationViews will be removed by Server's deletion of Conversations,
// but some special views (protocol logs, the blank ConversationView)
// must be done manually
for (int i = fChatLayout->CountItems() - 1; i >= 0; i--)
fChatLayout->RemoveItem(i);
fChatLayout->RemoveSelf();
delete fChatLayout;
AppPreferences::Get()->Save(); AppPreferences::Get()->Save();
ReplicantStatusView::RemoveReplicant(); ReplicantStatusView::RemoveReplicant();
be_app->PostMessage(B_QUIT_REQUESTED); be_app->PostMessage(B_QUIT_REQUESTED);
@ -282,6 +298,7 @@ MainWindow::MessageReceived(BMessage* message)
} }
case APP_SHOW_HELP: case APP_SHOW_HELP:
{ {
// Borrowed from HaikuArchives/Calendar's _ShowHelp()
BPathFinder pathFinder; BPathFinder pathFinder;
BStringList paths; BStringList paths;
BPath path; BPath path;
@ -394,79 +411,52 @@ MainWindow::WorkspaceActivated(int32 workspace, bool active)
void void
MainWindow::SetConversation(Conversation* chat) MainWindow::SetConversation(Conversation* chat)
{ {
fConversation = chat; if (chat == NULL) {
if (chat != NULL) {
SetConversationView(chat->GetView());
BString title(chat->GetName());
title << "" << APP_NAME;
SetTitle(title.String());
}
else {
SetConversationView(fBackupChatView);
SetTitle(APP_NAME); SetTitle(APP_NAME);
SetConversationView(fBackupChatView);
return;
} }
_EnsureConversationView(chat);
bool found = false;
BLayoutItem* item = fChatList.ValueFor(chat, &found);
if (found == false)
return;
BString title(chat->GetName());
title << "" << APP_NAME;
SetTitle(title.String());
// Remove "Protocol" menu
BMenuItem* chatMenuItem = fMenuBar->FindItem(B_TRANSLATE("Chat"));
BMenuItem* protocolMenuItem = fMenuBar->FindItem(B_TRANSLATE("Protocol"));
if (protocolMenuItem != NULL)
fMenuBar->RemoveItem(protocolMenuItem);
// Populate new "Protocol" menu if need be
BMenu* protocolMenu = _CreateProtocolMenu();
if (protocolMenu != NULL)
fMenuBar->AddItem(protocolMenu, fMenuBar->IndexOf(chatMenuItem) + 1);
_SaveWeights();
fConversation = chat;
fChatLayout->SetVisibleItem(item);
_ApplyWeights();
} }
void void
MainWindow::SetConversationView(ConversationView* chatView) MainWindow::SetConversationView(ConversationView* view)
{ {
// Save split weights int32 index = fChatLayout->IndexOfView(view);
float weightChat = fRightView->ItemWeight((int32)0); if (index < 0) {
float weightSend = fRightView->ItemWeight((int32)1); BLayoutItem* item = fChatLayout->AddView(view);
float horizChat, horizList, vertChat, vertSend; fChatLayout->SetVisibleItem(item);
fChatView->GetWeights(&horizChat, &horizList, &vertChat, &vertSend); } else
fChatLayout->SetVisibleItem(index);
fRightView->RemoveChild(fRightView->FindView("chatView")); _ApplyWeights();
fChatView = chatView;
fRightView->AddChild(fChatView, 9);
// Remove "Protocol" menu
BMenuItem* chatMenuItem = fMenuBar->FindItem("Protocol");
BMenu* chatMenu;
if (chatMenuItem != NULL && (chatMenu = chatMenuItem->Submenu()) != NULL)
fMenuBar->RemoveItem(chatMenu);
// Add and populate "Protocol" menu, if appropriate
if (fConversation != NULL) {
ProtocolLooper* looper = fConversation->GetProtocolLooper();
BObjectList<BMessage> menuItems = looper->Protocol()->MenuBarItems();
for (int i = 0; i < menuItems.CountItems(); i++) {
BMessage* itemMsg = menuItems.ItemAt(i);
BMessage* msg = new BMessage(*itemMsg);
BMessage toSend;
msg->FindMessage("_msg", &toSend);
toSend.AddString("chat_id", fConversation->GetId());
toSend.AddInt64("instance", looper->GetInstance());
msg->ReplaceMessage("_msg", &toSend);
BMenuItem* item = new BMenuItem(msg);
if (item == NULL)
continue;
if (msg->GetBool("x_to_protocol", true) == true)
item->SetTarget(looper);
else
item->SetTarget(this);
chatMenu->AddItem(item);
}
}
// Apply saved weights
fSplitView->SetItemWeight(0, AppPreferences::Get()->MainWindowListWeight, true);
fSplitView->SetItemWeight(1, AppPreferences::Get()->MainWindowChatWeight, true);
fChatView->SetWeights(horizChat, horizList, vertChat, vertSend);
if (weightChat * weightSend != 0) {
fRightView->SetItemWeight(0, weightChat, true);
fRightView->SetItemWeight(1, weightSend, true);
}
// Save ChatView weights to settings
AppPreferences::Get()->ChatViewHorizChatWeight = horizChat;
AppPreferences::Get()->ChatViewHorizListWeight = horizList;
AppPreferences::Get()->ChatViewVertChatWeight = vertChat;
AppPreferences::Get()->ChatViewVertSendWeight = vertSend;
} }
@ -475,6 +465,13 @@ MainWindow::RemoveConversation(Conversation* chat)
{ {
SetConversation(NULL); SetConversation(NULL);
bool found = false;
BLayoutItem* item = fChatList.ValueFor(chat, &found);
if (item != NULL)
item->RemoveSelf();
if (found == true)
fChatList.RemoveItemFor(chat);
int32 index = fListView->IndexOf(chat->GetListItem()); int32 index = fListView->IndexOf(chat->GetListItem());
if (index > 0) if (index > 0)
index--; index--;
@ -506,17 +503,8 @@ MainWindow::_InitInterface()
true, false, B_NO_BORDER); true, false, B_NO_BORDER);
// Right-side of window, Chat + Textbox // Right-side of window, Chat + Textbox
fRightView = new BSplitView(B_VERTICAL, 0); fChatLayout = new BCardLayout();
fBackupChatView = new ConversationView(); fBackupChatView = new ConversationView();
fChatView = fBackupChatView;
// Load weights from settings
float horizChat, horizList, vertChat, vertSend;
horizChat = AppPreferences::Get()->ChatViewHorizChatWeight;
horizList = AppPreferences::Get()->ChatViewHorizListWeight;
vertChat = AppPreferences::Get()->ChatViewVertChatWeight;
vertSend = AppPreferences::Get()->ChatViewVertSendWeight;
fChatView->SetWeights(horizChat, horizList, vertChat, vertSend);
BLayoutBuilder::Group<>(this, B_VERTICAL) BLayoutBuilder::Group<>(this, B_VERTICAL)
.Add((fMenuBar = _CreateMenuBar())) .Add((fMenuBar = _CreateMenuBar()))
@ -527,12 +515,11 @@ MainWindow::_InitInterface()
.Add(listScroll, 1) .Add(listScroll, 1)
.Add(fStatusView) .Add(fStatusView)
.End() .End()
.Add(fRightView, 5) .Add(fChatLayout, 5)
.End() .End()
.End() .End()
.End(); .End();
SetConversation(NULL);
_ToggleMenuItems(); _ToggleMenuItems();
} }
@ -630,6 +617,40 @@ MainWindow::_RefreshAccountsMenu()
} }
BMenu*
MainWindow::_CreateProtocolMenu()
{
if (fConversation == NULL)
return NULL;
ProtocolLooper* looper = fConversation->GetProtocolLooper();
BObjectList<BMessage> menuItems = looper->Protocol()->MenuBarItems();
if (menuItems.CountItems() == 0)
return NULL;
BMenu* menu = new BMenu(B_TRANSLATE("Protocol"));
for (int i = 0; i < menuItems.CountItems(); i++) {
BMessage* itemMsg = menuItems.ItemAt(i);
BMessage* msg = new BMessage(*itemMsg);
BMessage toSend;
msg->FindMessage("_msg", &toSend);
toSend.AddString("chat_id", fConversation->GetId());
toSend.AddInt64("instance", looper->GetInstance());
msg->ReplaceMessage("_msg", &toSend);
BMenuItem* item = new BMenuItem(msg);
if (item == NULL)
continue;
if (msg->GetBool("x_to_protocol", true) == true)
item->SetTarget(looper);
else
item->SetTarget(this);
menu->AddItem(item);
}
return menu;
}
void void
MainWindow::_ToggleMenuItems() MainWindow::_ToggleMenuItems()
{ {
@ -661,10 +682,12 @@ MainWindow::_ToggleMenuItems()
ConversationItem* ConversationItem*
MainWindow::_EnsureConversationItem(BMessage* msg) MainWindow::_EnsureConversationItem(BMessage* msg)
{ {
ChatMap chats = fServer->Conversations();
BString chat_id = msg->FindString("chat_id"); BString chat_id = msg->FindString("chat_id");
Conversation* chat = fServer->ConversationById(chat_id, msg->FindInt64("instance")); Conversation* chat = fServer->ConversationById(chat_id, msg->FindInt64("instance"));
_EnsureConversationView(chat);
// Add conversation item if necessary, return it
ConversationItem* item; ConversationItem* item;
if (chat != NULL && (item = chat->GetListItem()) != NULL) { if (chat != NULL && (item = chat->GetListItem()) != NULL) {
if (fListView->HasItem(item)) if (fListView->HasItem(item))
@ -682,6 +705,62 @@ MainWindow::_EnsureConversationItem(BMessage* msg)
} }
void
MainWindow::_EnsureConversationView(Conversation* chat)
{
// Add conversation's view if necessary
bool added = false;
fChatList.ValueFor(chat, &added);
if (added == false) {
BLayoutItem* item = fChatLayout->AddView(chat->GetView());
if (item != NULL)
fChatList.AddItem(chat, item);
}
}
void
MainWindow::_ApplyWeights()
{
// Chat-list and chat-view splitter
fSplitView->SetItemWeight(0, AppPreferences::Get()->MainWindowListWeight, true);
fSplitView->SetItemWeight(1, AppPreferences::Get()->MainWindowChatWeight, true);
// Chat-view's weights
float horizChat, horizList, vertChat, vertSend;
horizChat = AppPreferences::Get()->ChatViewHorizChatWeight;
horizList = AppPreferences::Get()->ChatViewHorizListWeight;
vertChat = AppPreferences::Get()->ChatViewVertChatWeight;
vertSend = AppPreferences::Get()->ChatViewVertSendWeight;
BLayoutItem* item = fChatLayout->VisibleItem();
if (item != NULL)
((ConversationView*)item->View())->SetWeights(horizChat, horizList, vertChat, vertSend);
}
void
MainWindow::_SaveWeights()
{
// Chat-list and chat-view splitter
AppPreferences::Get()->MainWindowListWeight = fSplitView->ItemWeight((int32)0);
AppPreferences::Get()->MainWindowChatWeight = fSplitView->ItemWeight((int32)1);
// ChatView's weights
float horizChat, horizList, vertChat, vertSend;
BLayoutItem* item = fChatLayout->VisibleItem();
if (item == NULL)
return;
((ConversationView*)item->View())->GetWeights(&horizChat, &horizList, &vertChat, &vertSend);
AppPreferences::Get()->ChatViewHorizChatWeight = horizChat;
AppPreferences::Get()->ChatViewHorizListWeight = horizList;
AppPreferences::Get()->ChatViewVertChatWeight = vertChat;
AppPreferences::Get()->ChatViewVertSendWeight = vertSend;
}
bool bool
MainWindow::_PopulateWithAccounts(BMenu* menu, ProtocolSettings* settings) MainWindow::_PopulateWithAccounts(BMenu* menu, ProtocolSettings* settings)
{ {

View File

@ -1,7 +1,7 @@
/* /*
* Copyright 2021, Jaidyn Levesque. All rights reserved.
* Copyright 2009-2011, Andrea Anzani. All rights reserved. * Copyright 2009-2011, Andrea Anzani. All rights reserved.
* Copyright 2009-2011, Pier Luigi Fiorini. All rights reserved. * Copyright 2009-2011, Pier Luigi Fiorini. All rights reserved.
* Copyright 2021-2022, Jaidyn Levesque. All rights reserved.
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
*/ */
#ifndef _MAIN_WINDOW_H #ifndef _MAIN_WINDOW_H
@ -11,6 +11,8 @@
#include "Server.h" #include "Server.h"
class BCardLayout;
class BLayoutItem;
class BMenu; class BMenu;
class BSplitView; class BSplitView;
class BTextView; class BTextView;
@ -31,6 +33,7 @@ public:
MainWindow(); MainWindow();
void Start(); void Start();
void Show();
virtual bool QuitRequested(); virtual bool QuitRequested();
virtual void MessageReceived(BMessage* message); virtual void MessageReceived(BMessage* message);
@ -40,7 +43,7 @@ public:
bool active); bool active);
void SetConversation(Conversation* chat); void SetConversation(Conversation* chat);
void SetConversationView(ConversationView* chatView); void SetConversationView(ConversationView* view);
void RemoveConversation(Conversation* chat); void RemoveConversation(Conversation* chat);
void SortConversation(Conversation* chat); void SortConversation(Conversation* chat);
@ -52,11 +55,16 @@ private:
BMenuBar* _CreateMenuBar(); BMenuBar* _CreateMenuBar();
BMenu* _CreateAccountsMenu(); BMenu* _CreateAccountsMenu();
void _RefreshAccountsMenu(); void _RefreshAccountsMenu();
BMenu* _CreateProtocolMenu();
void _ToggleMenuItems(); void _ToggleMenuItems();
ConversationItem* ConversationItem*
_EnsureConversationItem(BMessage* msg); _EnsureConversationItem(BMessage* msg);
void _EnsureConversationView(Conversation* chat);
void _ApplyWeights();
void _SaveWeights();
bool _PopulateWithAccounts(BMenu* menu, bool _PopulateWithAccounts(BMenu* menu,
ProtocolSettings* settings); ProtocolSettings* settings);
@ -67,15 +75,16 @@ private:
bool fWorkspaceChanged; bool fWorkspaceChanged;
BMenuBar* fMenuBar; BMenuBar* fMenuBar;
KeyMap<Conversation*, BLayoutItem*> fChatList;
// Left panel, chat list // Left panel, chat list
ConversationListView* fListView; ConversationListView* fListView;
StatusView* fStatusView; StatusView* fStatusView;
BSplitView* fSplitView; BSplitView* fSplitView;
// Right panel, chat // Right panel, chat
BSplitView* fRightView; BCardLayout* fChatLayout;
Conversation* fConversation; Conversation* fConversation;
ConversationView* fChatView;
ConversationView* fBackupChatView; ConversationView* fBackupChatView;
}; };

View File

@ -17,6 +17,7 @@ public:
void AddItem(KEY k, TYPE t); void AddItem(KEY k, TYPE t);
TYPE ValueFor(KEY, bool* found = NULL) const; TYPE ValueFor(KEY, bool* found = NULL) const;
KEY KeyFor(TYPE, bool* found = NULL) const;
TYPE RemoveItemAt(int32 position); TYPE RemoveItemAt(int32 position);
TYPE RemoveItemFor(KEY); TYPE RemoveItemFor(KEY);
@ -68,6 +69,20 @@ KeyMap<KEY, TYPE>::ValueFor(KEY k, bool* found) const
} }
template<class KEY, class TYPE>
inline KEY
KeyMap<KEY, TYPE>::KeyFor(TYPE v, bool* found) const
{
*found = false;
for (int32 i = 0; i < CountItems(); i++)
if (ValueAt(i) == v) {
*found = true;
return KeyAt(i);
}
return NULL;
}
template<class KEY, class TYPE> template<class KEY, class TYPE>
inline TYPE inline TYPE
KeyMap<KEY, TYPE>::RemoveItemAt(int32 position) KeyMap<KEY, TYPE>::RemoveItemAt(int32 position)