|
Wt examples
3.3.0
|
A self-contained chat widget. More...
#include <SimpleChatWidget.h>

Public Member Functions | |
| SimpleChatWidget (SimpleChatServer &server, Wt::WContainerWidget *parent=0) | |
| Create a chat widget that will connect to the given server. | |
| ~SimpleChatWidget () | |
| Delete a chat widget. | |
| void | connect () |
| void | disconnect () |
| void | letLogin () |
| Show a simple login screen. | |
| bool | startChat (const Wt::WString &user) |
| Start a chat for the given user. | |
| void | logout () |
| SimpleChatServer & | server () |
| int | userCount () |
| const Wt::WString & | userName () const |
Protected Member Functions | |
| virtual void | createLayout (Wt::WWidget *messages, Wt::WWidget *userList, Wt::WWidget *messageEdit, Wt::WWidget *sendButton, Wt::WWidget *logoutButton) |
| virtual void | updateUsers () |
| virtual void | newMessage () |
| virtual void | render (Wt::WFlags< Wt::RenderFlag > flags) |
| bool | loggedIn () const |
Private Types | |
| typedef std::map< Wt::WString, bool > | UserMap |
Private Member Functions | |
| void | login () |
| void | send () |
| void | updateUser () |
| void | processChatEvent (const ChatEvent &event) |
Private Attributes | |
| UserMap | users_ |
| SimpleChatServer & | server_ |
| bool | loggedIn_ |
| Wt::JSlot | clearInput_ |
| Wt::WString | user_ |
| Wt::WLineEdit * | userNameEdit_ |
| Wt::WText * | statusMsg_ |
| Wt::WContainerWidget * | messages_ |
| Wt::WContainerWidget * | messageEditArea_ |
| Wt::WTextArea * | messageEdit_ |
| Wt::WPushButton * | sendButton_ |
| Wt::WContainerWidget * | userList_ |
| Wt::WSound * | messageReceived_ |
A self-contained chat widget.
Definition at line 34 of file SimpleChatWidget.h.
typedef std::map<Wt::WString, bool> SimpleChatWidget::UserMap [private] |
Definition at line 82 of file SimpleChatWidget.h.
| SimpleChatWidget::SimpleChatWidget | ( | SimpleChatServer & | server, |
| Wt::WContainerWidget * | parent = 0 |
||
| ) |
Create a chat widget that will connect to the given server.
Definition at line 26 of file SimpleChatWidget.C.
: WContainerWidget(parent), server_(server), loggedIn_(false), userList_(0), messageReceived_(0) { user_ = server_.suggestGuest(); letLogin(); }
| SimpleChatWidget::~SimpleChatWidget | ( | ) |
Delete a chat widget.
Definition at line 38 of file SimpleChatWidget.C.
{
delete messageReceived_;
logout();
disconnect();
}
| void SimpleChatWidget::connect | ( | ) |
Definition at line 45 of file SimpleChatWidget.C.
{
if (server_.connect
(this, boost::bind(&SimpleChatWidget::processChatEvent, this, _1)))
Wt::WApplication::instance()->enableUpdates(true);
}
| void SimpleChatWidget::createLayout | ( | Wt::WWidget * | messages, |
| Wt::WWidget * | userList, | ||
| Wt::WWidget * | messageEdit, | ||
| Wt::WWidget * | sendButton, | ||
| Wt::WWidget * | logoutButton | ||
| ) | [protected, virtual] |
Reimplemented in PopupChatWidget.
Definition at line 108 of file SimpleChatWidget.C.
{
/*
* Create a vertical layout, which will hold 3 rows,
* organized like this:
*
* WVBoxLayout
* --------------------------------------------
* | nested WHBoxLayout (vertical stretch=1) |
* | | |
* | messages | userList |
* | (horizontal stretch=1) | |
* | | |
* --------------------------------------------
* | message edit area |
* --------------------------------------------
* | WHBoxLayout |
* | send | logout |
* --------------------------------------------
*/
WVBoxLayout *vLayout = new WVBoxLayout();
// Create a horizontal layout for the messages | userslist.
WHBoxLayout *hLayout = new WHBoxLayout();
// Add widget to horizontal layout with stretch = 1
hLayout->addWidget(messages, 1);
messages->setStyleClass("chat-msgs");
// Add another widget to horizontal layout with stretch = 0
hLayout->addWidget(userList);
userList->setStyleClass("chat-users");
hLayout->setResizable(0, true);
// Add nested layout to vertical layout with stretch = 1
vLayout->addLayout(hLayout, 1);
// Add widget to vertical layout with stretch = 0
vLayout->addWidget(messageEdit);
messageEdit->setStyleClass("chat-noedit");
// Create a horizontal layout for the buttons.
hLayout = new WHBoxLayout();
// Add button to horizontal layout with stretch = 0
hLayout->addWidget(sendButton);
// Add button to horizontal layout with stretch = 0
hLayout->addWidget(logoutButton);
// Add nested layout to vertical layout with stretch = 0
vLayout->addLayout(hLayout, 0, AlignLeft);
setLayout(vLayout);
}
| void SimpleChatWidget::disconnect | ( | ) |
Definition at line 52 of file SimpleChatWidget.C.
{
if (server_.disconnect(this))
Wt::WApplication::instance()->enableUpdates(false);
}
| void SimpleChatWidget::letLogin | ( | ) |
Show a simple login screen.
Definition at line 58 of file SimpleChatWidget.C.
{
disconnect();
clear();
WVBoxLayout *vLayout = new WVBoxLayout();
setLayout(vLayout);
WHBoxLayout *hLayout = new WHBoxLayout();
vLayout->addLayout(hLayout, 0, AlignTop | AlignLeft);
hLayout->addWidget(new WLabel("User name:"), 0, AlignMiddle);
hLayout->addWidget(userNameEdit_ = new WLineEdit(user_), 0, AlignMiddle);
userNameEdit_->setFocus();
WPushButton *b = new WPushButton("Login");
hLayout->addWidget(b, 0, AlignMiddle);
b->clicked().connect(this, &SimpleChatWidget::login);
userNameEdit_->enterPressed().connect(this, &SimpleChatWidget::login);
vLayout->addWidget(statusMsg_ = new WText());
statusMsg_->setTextFormat(PlainText);
}
| bool SimpleChatWidget::loggedIn | ( | ) | const [protected] |
Definition at line 167 of file SimpleChatWidget.C.
{
return loggedIn_;
}
| void SimpleChatWidget::login | ( | ) | [private] |
Definition at line 84 of file SimpleChatWidget.C.
{
if (!loggedIn()) {
WString name = userNameEdit_->text();
if (!messageReceived_)
messageReceived_ = new WSound("sounds/message_received.mp3");
if (!startChat(name))
statusMsg_->setText("Sorry, name '" + escapeText(name) +
"' is already taken.");
}
}
| void SimpleChatWidget::logout | ( | ) |
| void SimpleChatWidget::newMessage | ( | ) | [protected, virtual] |
| void SimpleChatWidget::processChatEvent | ( | const ChatEvent & | event | ) | [private] |
Definition at line 319 of file SimpleChatWidget.C.
{
WApplication *app = WApplication::instance();
/*
* This is where the "server-push" happens. The chat server posts to this
* event from other sessions, see SimpleChatServer::postChatEvent()
*/
/*
* Format and append the line to the conversation.
*
* This is also the step where the automatic XSS filtering will kick in:
* - if another user tried to pass on some JavaScript, it is filtered away.
* - if another user did not provide valid XHTML, the text is automatically
* interpreted as PlainText
*/
/*
* If it is not a plain message, also update the user list.
*/
if (event.type() != ChatEvent::Message) {
if (event.type() == ChatEvent::Rename && event.user() == user_)
user_ = event.data();
updateUsers();
}
newMessage();
/*
* Anything else doesn't matter if we are not logged in.
*/
if (!loggedIn()) {
app->triggerUpdate();
return;
}
bool display = event.type() != ChatEvent::Message
|| !userList_
|| (users_.find(event.user()) != users_.end() && users_[event.user()]);
if (display) {
WText *w = new WText(messages_);
/*
* If it fails, it is because the content wasn't valid XHTML
*/
if (!w->setText(event.formattedHTML(user_, XHTMLText))) {
w->setText(event.formattedHTML(user_, PlainText));
w->setTextFormat(XHTMLText);
}
w->setInline(false);
w->setStyleClass("chat-msg");
/*
* Leave no more than 100 messages in the back-log
*/
if (messages_->count() > 100)
delete messages_->children()[0];
/*
* Little javascript trick to make sure we scroll along with new content
*/
app->doJavaScript(messages_->jsRef() + ".scrollTop += "
+ messages_->jsRef() + ".scrollHeight;");
/* If this message belongs to another user, play a received sound */
if (event.user() != user_ && messageReceived_)
messageReceived_->play();
}
/*
* This is the server push action: we propagate the updated UI to the client,
* (when the event was triggered by another user)
*/
app->triggerUpdate();
}
| void SimpleChatWidget::render | ( | Wt::WFlags< Wt::RenderFlag > | flags | ) | [protected, virtual] |
Definition at line 172 of file SimpleChatWidget.C.
{
if (flags & RenderFull) {
if (loggedIn()) {
/* Handle a page refresh correctly */
messageEdit_->setText(WString::Empty);
doJavaScript("setTimeout(function() { "
+ messages_->jsRef() + ".scrollTop += "
+ messages_->jsRef() + ".scrollHeight;}, 0);");
}
}
WContainerWidget::render(flags);
}
| void SimpleChatWidget::send | ( | ) | [private] |
Definition at line 274 of file SimpleChatWidget.C.
{
if (!messageEdit_->text().empty())
server_.sendMessage(user_, messageEdit_->text());
}
| SimpleChatServer& SimpleChatWidget::server | ( | ) | [inline] |
Definition at line 62 of file SimpleChatWidget.h.
{ return server_; }
| bool SimpleChatWidget::startChat | ( | const Wt::WString & | user | ) |
Start a chat for the given user.
Returns false if the user could not login.
Definition at line 187 of file SimpleChatWidget.C.
{
/*
* When logging in, we pass our processChatEvent method as the function that
* is used to indicate a new chat event for this user.
*/
if (server_.login(user)) {
loggedIn_ = true;
connect();
user_ = user;
clear();
userNameEdit_ = 0;
messages_ = new WContainerWidget();
userList_ = new WContainerWidget();
messageEdit_ = new WTextArea();
messageEdit_->setRows(2);
messageEdit_->setFocus();
// Display scroll bars if contents overflows
messages_->setOverflow(WContainerWidget::OverflowAuto);
userList_->setOverflow(WContainerWidget::OverflowAuto);
sendButton_ = new WPushButton("Send");
WPushButton *logoutButton = new WPushButton("Logout");
createLayout(messages_, userList_, messageEdit_, sendButton_, logoutButton);
/*
* Connect event handlers:
* - click on button
* - enter in text area
*
* We will clear the input field using a small custom client-side
* JavaScript invocation.
*/
// Create a JavaScript 'slot' (JSlot). The JavaScript slot always takes
// 2 arguments: the originator of the event (in our case the
// button or text area), and the JavaScript event object.
clearInput_.setJavaScript
("function(o, e) { setTimeout(function() {"
"" + messageEdit_->jsRef() + ".value='';"
"}, 0); }");
// Bind the C++ and JavaScript event handlers.
sendButton_->clicked().connect(this, &SimpleChatWidget::send);
messageEdit_->enterPressed().connect(this, &SimpleChatWidget::send);
sendButton_->clicked().connect(clearInput_);
messageEdit_->enterPressed().connect(clearInput_);
sendButton_->clicked().connect(messageEdit_, &WLineEdit::setFocus);
messageEdit_->enterPressed().connect(messageEdit_, &WLineEdit::setFocus);
// Prevent the enter from generating a new line, which is its default
// action
messageEdit_->enterPressed().preventDefaultAction();
logoutButton->clicked().connect(this, &SimpleChatWidget::logout);
WText *msg = new WText
("<div><span class='chat-info'>You are joining as "
+ escapeText(user_) + ".</span></div>",
messages_);
msg->setStyleClass("chat-msg");
if (!userList_->parent()) {
delete userList_;
userList_ = 0;
}
if (!sendButton_->parent()) {
delete sendButton_;
sendButton_ = 0;
}
if (!logoutButton->parent())
delete logoutButton;
updateUsers();
return true;
} else
return false;
}
| void SimpleChatWidget::updateUser | ( | ) | [private] |
| void SimpleChatWidget::updateUsers | ( | ) | [protected, virtual] |
Reimplemented in PopupChatWidget.
Definition at line 280 of file SimpleChatWidget.C.
{
if (userList_) {
userList_->clear();
SimpleChatServer::UserSet users = server_.users();
UserMap oldUsers = users_;
users_.clear();
for (SimpleChatServer::UserSet::iterator i = users.begin();
i != users.end(); ++i) {
WCheckBox *w = new WCheckBox(escapeText(*i), userList_);
w->setInline(false);
UserMap::const_iterator j = oldUsers.find(*i);
if (j != oldUsers.end())
w->setChecked(j->second);
else
w->setChecked(true);
users_[*i] = w->isChecked();
w->changed().connect(this, &SimpleChatWidget::updateUser);
if (*i == user_)
w->setStyleClass("chat-self");
}
}
}
| int SimpleChatWidget::userCount | ( | ) | [inline] |
Definition at line 64 of file SimpleChatWidget.h.
{ return users_.size(); }
| const Wt::WString& SimpleChatWidget::userName | ( | ) | const [inline] |
Definition at line 66 of file SimpleChatWidget.h.
{ return user_; }
Wt::JSlot SimpleChatWidget::clearInput_ [private] |
Definition at line 88 of file SimpleChatWidget.h.
bool SimpleChatWidget::loggedIn_ [private] |
Definition at line 86 of file SimpleChatWidget.h.
Wt::WTextArea* SimpleChatWidget::messageEdit_ [private] |
Definition at line 97 of file SimpleChatWidget.h.
Definition at line 96 of file SimpleChatWidget.h.
Wt::WSound* SimpleChatWidget::messageReceived_ [private] |
Definition at line 101 of file SimpleChatWidget.h.
Wt::WContainerWidget* SimpleChatWidget::messages_ [private] |
Definition at line 95 of file SimpleChatWidget.h.
Wt::WPushButton* SimpleChatWidget::sendButton_ [private] |
Definition at line 98 of file SimpleChatWidget.h.
SimpleChatServer& SimpleChatWidget::server_ [private] |
Definition at line 85 of file SimpleChatWidget.h.
Wt::WText* SimpleChatWidget::statusMsg_ [private] |
Definition at line 93 of file SimpleChatWidget.h.
Wt::WString SimpleChatWidget::user_ [private] |
Definition at line 90 of file SimpleChatWidget.h.
Wt::WContainerWidget* SimpleChatWidget::userList_ [private] |
Definition at line 99 of file SimpleChatWidget.h.
Wt::WLineEdit* SimpleChatWidget::userNameEdit_ [private] |
Definition at line 92 of file SimpleChatWidget.h.
UserMap SimpleChatWidget::users_ [private] |
Definition at line 83 of file SimpleChatWidget.h.
1.7.5.1