mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 15:40:19 +00:00
IRCClient: Switch to using an HtmlView for the IRC window contents :^)
This seemed like a perfect fit for LibHTML. We can now style the IRC channels and queries however we like with the power of HTML and CSS. This patch doesn't do much in the way of styling, it just gets the basic mechanism into place.
This commit is contained in:
parent
98ff8ef0cf
commit
fa69b9fbb7
Notes:
sideshowbarker
2024-07-19 11:30:32 +09:00
Author: https://github.com/awesomekling Commit: https://github.com/SerenityOS/serenity/commit/fa69b9fbb72
8 changed files with 64 additions and 146 deletions
|
@ -7,6 +7,7 @@
|
|||
|
||||
class GAction;
|
||||
class GStackWidget;
|
||||
class GTableView;
|
||||
|
||||
class IRCAppWindow : public GWindow {
|
||||
public:
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
#include "IRCLogBuffer.h"
|
||||
#include "IRCLogBufferModel.h"
|
||||
#include <LibHTML/DOM/DocumentType.h>
|
||||
#include <LibHTML/DOM/ElementFactory.h>
|
||||
#include <LibHTML/DOM/HTMLBodyElement.h>
|
||||
#include <LibHTML/DOM/Text.h>
|
||||
#include <LibHTML/Dump.h>
|
||||
#include <LibHTML/Parser/HTMLParser.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
|
@ -9,8 +14,19 @@ NonnullRefPtr<IRCLogBuffer> IRCLogBuffer::create()
|
|||
}
|
||||
|
||||
IRCLogBuffer::IRCLogBuffer()
|
||||
: m_model(IRCLogBufferModel::create(*this))
|
||||
{
|
||||
m_document = adopt(*new Document);
|
||||
m_document->append_child(adopt(*new DocumentType(document())));
|
||||
auto html_element = create_element(document(), "html");
|
||||
m_document->append_child(html_element);
|
||||
auto head_element = create_element(document(), "head");
|
||||
html_element->append_child(head_element);
|
||||
auto style_element = create_element(document(), "style");
|
||||
style_element->append_child(adopt(*new Text(document(), "div { font-family: Csilla; font-weight: lighter; }")));
|
||||
head_element->append_child(style_element);
|
||||
auto body_element = create_element(document(), "body");
|
||||
html_element->append_child(body_element);
|
||||
m_container_element = body_element;
|
||||
}
|
||||
|
||||
IRCLogBuffer::~IRCLogBuffer()
|
||||
|
@ -19,19 +35,44 @@ IRCLogBuffer::~IRCLogBuffer()
|
|||
|
||||
void IRCLogBuffer::add_message(char prefix, const String& name, const String& text, Color color)
|
||||
{
|
||||
m_messages.enqueue({ time(nullptr), prefix, name, text, color });
|
||||
m_model->update();
|
||||
auto message_element = create_element(document(), "div");
|
||||
message_element->set_attribute("style", String::format("color: %s;", color.to_string().characters()));
|
||||
auto timestamp_element = create_element(document(), "span");
|
||||
auto now = time(nullptr);
|
||||
auto* tm = localtime(&now);
|
||||
auto timestamp_string = String::format("%02u:%02u:%02u ", tm->tm_hour, tm->tm_min, tm->tm_sec);
|
||||
timestamp_element->append_child(adopt(*new Text(document(), timestamp_string)));
|
||||
auto nick_element = create_element(document(), "b");
|
||||
nick_element->append_child(*new Text(document(), String::format("<%c%s> ", prefix ? prefix : ' ', name.characters())));
|
||||
auto text_element = create_element(document(), "span");
|
||||
text_element->append_child(*new Text(document(), text));
|
||||
message_element->append_child(timestamp_element);
|
||||
message_element->append_child(nick_element);
|
||||
message_element->append_child(text_element);
|
||||
m_container_element->append_child(message_element);
|
||||
|
||||
m_document->force_layout();
|
||||
}
|
||||
|
||||
void IRCLogBuffer::add_message(const String& text, Color color)
|
||||
{
|
||||
m_messages.enqueue({ time(nullptr), '\0', String(), text, color });
|
||||
m_model->update();
|
||||
auto message_element = create_element(document(), "div");
|
||||
message_element->set_attribute("style", String::format("color: %s;", color.to_string().characters()));
|
||||
auto timestamp_element = create_element(document(), "span");
|
||||
auto now = time(nullptr);
|
||||
auto* tm = localtime(&now);
|
||||
auto timestamp_string = String::format("%02u:%02u:%02u ", tm->tm_hour, tm->tm_min, tm->tm_sec);
|
||||
timestamp_element->append_child(adopt(*new Text(document(), timestamp_string)));
|
||||
auto text_element = create_element(document(), "span");
|
||||
text_element->append_child(*new Text(document(), text));
|
||||
message_element->append_child(timestamp_element);
|
||||
message_element->append_child(text_element);
|
||||
m_container_element->append_child(message_element);
|
||||
|
||||
m_document->force_layout();
|
||||
}
|
||||
|
||||
void IRCLogBuffer::dump() const
|
||||
{
|
||||
for (auto& message : m_messages) {
|
||||
printf("%u <%c%8s> %s\n", message.timestamp, message.prefix, message.sender.characters(), message.text.characters());
|
||||
}
|
||||
// FIXME: Remove me?
|
||||
}
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/String.h>
|
||||
#include <AK/CircularQueue.h>
|
||||
#include <AK/RefPtr.h>
|
||||
#include <AK/RefCounted.h>
|
||||
#include <AK/RefPtr.h>
|
||||
#include <AK/String.h>
|
||||
#include <LibDraw/Color.h>
|
||||
|
||||
class IRCLogBufferModel;
|
||||
#include <LibHTML/DOM/Document.h>
|
||||
|
||||
class IRCLogBuffer : public RefCounted<IRCLogBuffer> {
|
||||
public:
|
||||
|
@ -21,17 +19,15 @@ public:
|
|||
Color color { Color::Black };
|
||||
};
|
||||
|
||||
int count() const { return m_messages.size(); }
|
||||
const Message& at(int index) const { return m_messages.at(index); }
|
||||
void add_message(char prefix, const String& name, const String& text, Color = Color::Black);
|
||||
void add_message(const String& text, Color = Color::Black);
|
||||
void dump() const;
|
||||
|
||||
const IRCLogBufferModel* model() const { return m_model.ptr(); }
|
||||
IRCLogBufferModel* model() { return m_model.ptr(); }
|
||||
const Document& document() const { return *m_document; }
|
||||
Document& document() { return *m_document; }
|
||||
|
||||
private:
|
||||
IRCLogBuffer();
|
||||
NonnullRefPtr<IRCLogBufferModel> m_model;
|
||||
CircularQueue<Message, 1000> m_messages;
|
||||
RefPtr<Document> m_document;
|
||||
RefPtr<Element> m_container_element;
|
||||
};
|
||||
|
|
|
@ -1,81 +0,0 @@
|
|||
#include "IRCLogBufferModel.h"
|
||||
#include "IRCLogBuffer.h"
|
||||
#include <LibDraw/Font.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
IRCLogBufferModel::IRCLogBufferModel(NonnullRefPtr<IRCLogBuffer>&& log_buffer)
|
||||
: m_log_buffer(move(log_buffer))
|
||||
{
|
||||
}
|
||||
|
||||
IRCLogBufferModel::~IRCLogBufferModel()
|
||||
{
|
||||
}
|
||||
|
||||
int IRCLogBufferModel::row_count(const GModelIndex&) const
|
||||
{
|
||||
return m_log_buffer->count();
|
||||
}
|
||||
|
||||
int IRCLogBufferModel::column_count(const GModelIndex&) const
|
||||
{
|
||||
return Column::__Count;
|
||||
}
|
||||
|
||||
String IRCLogBufferModel::column_name(int column) const
|
||||
{
|
||||
switch (column) {
|
||||
case Column::Timestamp:
|
||||
return "Time";
|
||||
case Column::Name:
|
||||
return "Name";
|
||||
case Column::Text:
|
||||
return "Text";
|
||||
}
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
GModel::ColumnMetadata IRCLogBufferModel::column_metadata(int column) const
|
||||
{
|
||||
switch (column) {
|
||||
case Column::Timestamp:
|
||||
return { 60, TextAlignment::CenterLeft };
|
||||
case Column::Name:
|
||||
return { 70, TextAlignment::CenterRight, &Font::default_bold_font() };
|
||||
case Column::Text:
|
||||
return { 800, TextAlignment::CenterLeft };
|
||||
}
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
GVariant IRCLogBufferModel::data(const GModelIndex& index, Role role) const
|
||||
{
|
||||
if (role == Role::Display) {
|
||||
auto& entry = m_log_buffer->at(index.row());
|
||||
switch (index.column()) {
|
||||
case Column::Timestamp: {
|
||||
auto* tm = localtime(&entry.timestamp);
|
||||
return String::format("%02u:%02u:%02u", tm->tm_hour, tm->tm_min, tm->tm_sec);
|
||||
}
|
||||
case Column::Name:
|
||||
if (entry.sender.is_empty())
|
||||
return String::empty();
|
||||
return String::format("<%c%s>", entry.prefix ? entry.prefix : ' ', entry.sender.characters());
|
||||
case Column::Text:
|
||||
return entry.text;
|
||||
}
|
||||
}
|
||||
if (role == Role::ForegroundColor) {
|
||||
if (index.column() == Column::Timestamp)
|
||||
return Color(Color::MidGray);
|
||||
if (index.column() == Column::Text)
|
||||
return m_log_buffer->at(index.row()).color;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void IRCLogBufferModel::update()
|
||||
{
|
||||
did_update();
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <LibGUI/GModel.h>
|
||||
|
||||
class IRCLogBuffer;
|
||||
|
||||
class IRCLogBufferModel final : public GModel {
|
||||
public:
|
||||
enum Column {
|
||||
Timestamp = 0,
|
||||
Name,
|
||||
Text,
|
||||
__Count,
|
||||
};
|
||||
|
||||
static NonnullRefPtr<IRCLogBufferModel> create(NonnullRefPtr<IRCLogBuffer>&& log_buffer) { return adopt(*new IRCLogBufferModel(move(log_buffer))); }
|
||||
virtual ~IRCLogBufferModel() override;
|
||||
|
||||
virtual int row_count(const GModelIndex&) const override;
|
||||
virtual int column_count(const GModelIndex&) const override;
|
||||
virtual String column_name(int column) const override;
|
||||
virtual ColumnMetadata column_metadata(int column) const override;
|
||||
virtual GVariant data(const GModelIndex&, Role = Role::Display) const override;
|
||||
virtual void update() override;
|
||||
|
||||
private:
|
||||
explicit IRCLogBufferModel(NonnullRefPtr<IRCLogBuffer>&&);
|
||||
|
||||
NonnullRefPtr<IRCLogBuffer> m_log_buffer;
|
||||
};
|
|
@ -2,12 +2,12 @@
|
|||
#include "IRCChannel.h"
|
||||
#include "IRCChannelMemberListModel.h"
|
||||
#include "IRCClient.h"
|
||||
#include "IRCLogBufferModel.h"
|
||||
#include <LibGUI/GBoxLayout.h>
|
||||
#include <LibGUI/GSplitter.h>
|
||||
#include <LibGUI/GTableView.h>
|
||||
#include <LibGUI/GTextBox.h>
|
||||
#include <LibGUI/GTextEditor.h>
|
||||
#include <LibHTML/HtmlView.h>
|
||||
|
||||
IRCWindow::IRCWindow(IRCClient& client, void* owner, Type type, const String& name, GWidget* parent)
|
||||
: GWidget(parent)
|
||||
|
@ -21,15 +21,7 @@ IRCWindow::IRCWindow(IRCClient& client, void* owner, Type type, const String& na
|
|||
// Make a container for the log buffer view + (optional) member list.
|
||||
auto container = GSplitter::construct(Orientation::Horizontal, this);
|
||||
|
||||
m_table_view = GTableView::construct(container);
|
||||
m_table_view->set_size_columns_to_fit_content(true);
|
||||
m_table_view->set_headers_visible(false);
|
||||
m_table_view->set_font(Font::default_fixed_width_font());
|
||||
m_table_view->set_alternating_row_colors(false);
|
||||
|
||||
if (m_type == Server) {
|
||||
m_table_view->set_column_hidden(IRCLogBufferModel::Column::Name, true);
|
||||
}
|
||||
m_html_view = HtmlView::construct(container);
|
||||
|
||||
if (m_type == Channel) {
|
||||
auto member_view = GTableView::construct(container);
|
||||
|
@ -65,7 +57,7 @@ IRCWindow::~IRCWindow()
|
|||
void IRCWindow::set_log_buffer(const IRCLogBuffer& log_buffer)
|
||||
{
|
||||
m_log_buffer = &log_buffer;
|
||||
m_table_view->set_model(log_buffer.model());
|
||||
m_html_view->set_document(const_cast<Document*>(&log_buffer.document()));
|
||||
}
|
||||
|
||||
bool IRCWindow::is_active() const
|
||||
|
@ -80,7 +72,7 @@ void IRCWindow::did_add_message()
|
|||
m_client.aid_update_window_list();
|
||||
return;
|
||||
}
|
||||
m_table_view->scroll_to_bottom();
|
||||
m_html_view->scroll_to_bottom();
|
||||
}
|
||||
|
||||
void IRCWindow::clear_unread_count()
|
||||
|
|
|
@ -6,8 +6,8 @@ class IRCChannel;
|
|||
class IRCClient;
|
||||
class IRCQuery;
|
||||
class IRCLogBuffer;
|
||||
class GTableView;
|
||||
class GTextEditor;
|
||||
class HtmlView;
|
||||
|
||||
class IRCWindow : public GWidget {
|
||||
C_OBJECT(IRCWindow)
|
||||
|
@ -46,7 +46,7 @@ private:
|
|||
void* m_owner { nullptr };
|
||||
Type m_type;
|
||||
String m_name;
|
||||
RefPtr<GTableView> m_table_view;
|
||||
RefPtr<HtmlView> m_html_view;
|
||||
RefPtr<GTextEditor> m_text_editor;
|
||||
RefPtr<IRCLogBuffer> m_log_buffer;
|
||||
int m_unread_count { 0 };
|
||||
|
|
|
@ -5,7 +5,6 @@ OBJS = \
|
|||
IRCChannel.o \
|
||||
IRCQuery.o \
|
||||
IRCLogBuffer.o \
|
||||
IRCLogBufferModel.o \
|
||||
IRCAppWindow.o \
|
||||
IRCWindow.o \
|
||||
IRCWindowListModel.o \
|
||||
|
|
Loading…
Reference in a new issue