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 <Alert.h>
#include <Beep.h>
#include <CardLayout.h>
#include <Catalog.h>
#include <LayoutBuilder.h>
#include <MenuBar.h>
@ -87,6 +88,15 @@ MainWindow::Start()
}
void
MainWindow::Show()
{
if (fChatLayout->CountItems() == 0)
SetConversation(NULL);
BWindow::Show();
}
bool
MainWindow::QuitRequested()
{
@ -101,14 +111,20 @@ MainWindow::QuitRequested()
button_index = alert->Go();
}
AppPreferences::Get()->MainWindowListWeight
= fSplitView->ItemWeight((int32)0);
AppPreferences::Get()->MainWindowChatWeight
= fSplitView->ItemWeight((int32)1);
AppPreferences::Get()->MainWindowRect = Frame();
_SaveWeights();
if(button_index == 0) {
if (button_index == 0) {
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();
ReplicantStatusView::RemoveReplicant();
be_app->PostMessage(B_QUIT_REQUESTED);
@ -282,6 +298,7 @@ MainWindow::MessageReceived(BMessage* message)
}
case APP_SHOW_HELP:
{
// Borrowed from HaikuArchives/Calendar's _ShowHelp()
BPathFinder pathFinder;
BStringList paths;
BPath path;
@ -394,79 +411,52 @@ MainWindow::WorkspaceActivated(int32 workspace, bool active)
void
MainWindow::SetConversation(Conversation* chat)
{
fConversation = chat;
if (chat != NULL) {
SetConversationView(chat->GetView());
BString title(chat->GetName());
title << "" << APP_NAME;
SetTitle(title.String());
}
else {
SetConversationView(fBackupChatView);
if (chat == NULL) {
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
MainWindow::SetConversationView(ConversationView* chatView)
MainWindow::SetConversationView(ConversationView* view)
{
// Save split weights
float weightChat = fRightView->ItemWeight((int32)0);
float weightSend = fRightView->ItemWeight((int32)1);
float horizChat, horizList, vertChat, vertSend;
fChatView->GetWeights(&horizChat, &horizList, &vertChat, &vertSend);
int32 index = fChatLayout->IndexOfView(view);
if (index < 0) {
BLayoutItem* item = fChatLayout->AddView(view);
fChatLayout->SetVisibleItem(item);
} else
fChatLayout->SetVisibleItem(index);
fRightView->RemoveChild(fRightView->FindView("chatView"));
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;
_ApplyWeights();
}
@ -475,6 +465,13 @@ MainWindow::RemoveConversation(Conversation* chat)
{
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());
if (index > 0)
index--;
@ -506,17 +503,8 @@ MainWindow::_InitInterface()
true, false, B_NO_BORDER);
// Right-side of window, Chat + Textbox
fRightView = new BSplitView(B_VERTICAL, 0);
fChatLayout = new BCardLayout();
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)
.Add((fMenuBar = _CreateMenuBar()))
@ -527,12 +515,11 @@ MainWindow::_InitInterface()
.Add(listScroll, 1)
.Add(fStatusView)
.End()
.Add(fRightView, 5)
.Add(fChatLayout, 5)
.End()
.End()
.End();
SetConversation(NULL);
_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
MainWindow::_ToggleMenuItems()
{
@ -661,10 +682,12 @@ MainWindow::_ToggleMenuItems()
ConversationItem*
MainWindow::_EnsureConversationItem(BMessage* msg)
{
ChatMap chats = fServer->Conversations();
BString chat_id = msg->FindString("chat_id");
Conversation* chat = fServer->ConversationById(chat_id, msg->FindInt64("instance"));
_EnsureConversationView(chat);
// Add conversation item if necessary, return it
ConversationItem* item;
if (chat != NULL && (item = chat->GetListItem()) != NULL) {
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
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, Pier Luigi Fiorini. All rights reserved.
* Copyright 2021-2022, Jaidyn Levesque. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _MAIN_WINDOW_H
@ -11,6 +11,8 @@
#include "Server.h"
class BCardLayout;
class BLayoutItem;
class BMenu;
class BSplitView;
class BTextView;
@ -31,6 +33,7 @@ public:
MainWindow();
void Start();
void Show();
virtual bool QuitRequested();
virtual void MessageReceived(BMessage* message);
@ -40,7 +43,7 @@ public:
bool active);
void SetConversation(Conversation* chat);
void SetConversationView(ConversationView* chatView);
void SetConversationView(ConversationView* view);
void RemoveConversation(Conversation* chat);
void SortConversation(Conversation* chat);
@ -52,11 +55,16 @@ private:
BMenuBar* _CreateMenuBar();
BMenu* _CreateAccountsMenu();
void _RefreshAccountsMenu();
BMenu* _CreateProtocolMenu();
void _ToggleMenuItems();
ConversationItem*
_EnsureConversationItem(BMessage* msg);
void _EnsureConversationView(Conversation* chat);
void _ApplyWeights();
void _SaveWeights();
bool _PopulateWithAccounts(BMenu* menu,
ProtocolSettings* settings);
@ -67,15 +75,16 @@ private:
bool fWorkspaceChanged;
BMenuBar* fMenuBar;
KeyMap<Conversation*, BLayoutItem*> fChatList;
// Left panel, chat list
ConversationListView* fListView;
StatusView* fStatusView;
BSplitView* fSplitView;
// Right panel, chat
BSplitView* fRightView;
BCardLayout* fChatLayout;
Conversation* fConversation;
ConversationView* fChatView;
ConversationView* fBackupChatView;
};

View File

@ -17,6 +17,7 @@ public:
void AddItem(KEY k, TYPE t);
TYPE ValueFor(KEY, bool* found = NULL) const;
KEY KeyFor(TYPE, bool* found = NULL) const;
TYPE RemoveItemAt(int32 position);
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>
inline TYPE
KeyMap<KEY, TYPE>::RemoveItemAt(int32 position)