diff options
-rw-r--r-- | Applications/IRCClient/IRCChannel.cpp | 9 | ||||
-rw-r--r-- | Applications/IRCClient/IRCChannel.h | 2 | ||||
-rw-r--r-- | Applications/IRCClient/IRCClient.cpp | 40 | ||||
-rw-r--r-- | Applications/IRCClient/IRCClient.h | 7 | ||||
-rw-r--r-- | Applications/IRCClient/IRCClientWindow.cpp | 14 | ||||
-rw-r--r-- | Applications/IRCClient/IRCClientWindow.h | 2 | ||||
-rw-r--r-- | Applications/IRCClient/IRCLogBufferModel.cpp | 14 | ||||
-rw-r--r-- | Applications/IRCClient/IRCLogBufferModel.h | 2 | ||||
-rw-r--r-- | LibGUI/GTableModel.h | 2 | ||||
-rw-r--r-- | LibGUI/GTableView.cpp | 3 | ||||
-rw-r--r-- | LibGUI/GTextEditor.cpp | 40 | ||||
-rw-r--r-- | LibGUI/GTextEditor.h | 14 |
12 files changed, 126 insertions, 23 deletions
diff --git a/Applications/IRCClient/IRCChannel.cpp b/Applications/IRCClient/IRCChannel.cpp index 1c6bec841b..45e7acb0b7 100644 --- a/Applications/IRCClient/IRCChannel.cpp +++ b/Applications/IRCClient/IRCChannel.cpp @@ -40,8 +40,13 @@ void IRCChannel::add_message(char prefix, const String& name, const String& text void IRCChannel::dump() const { printf("IRCChannel{%p}: %s\n", this, m_name.characters()); - for (auto& member : m_members) { + for (auto& member : m_members) printf(" (%c)%s\n", member.prefix ? member.prefix : ' ', member.name.characters()); - } log().dump(); } + +void IRCChannel::say(const String& text) +{ + m_client.send_privmsg(m_name, text); + add_message(' ', m_client.nickname(), text); +} diff --git a/Applications/IRCClient/IRCChannel.h b/Applications/IRCClient/IRCChannel.h index d43702320b..b0c85bd132 100644 --- a/Applications/IRCClient/IRCChannel.h +++ b/Applications/IRCClient/IRCChannel.h @@ -26,6 +26,8 @@ public: void dump() const; + void say(const String&); + const IRCLogBuffer& log() const { return *m_log; } IRCLogBuffer& log() { return *m_log; } diff --git a/Applications/IRCClient/IRCClient.cpp b/Applications/IRCClient/IRCClient.cpp index b78f4c2a97..478412cfc1 100644 --- a/Applications/IRCClient/IRCClient.cpp +++ b/Applications/IRCClient/IRCClient.cpp @@ -237,6 +237,30 @@ void IRCClient::handle(const Message& msg, const String&) m_log->add_message(0, "Server", String::format("[%s] %s", msg.command.characters(), msg.arguments[1].characters())); } +void IRCClient::send_privmsg(const String& target, const String& text) +{ + send(String::format("PRIVMSG %s :%s\r\n", target.characters(), text.characters())); +} + +void IRCClient::handle_user_input_in_channel(const String& channel_name, const String& input) +{ + if (input.is_empty()) + return; + ensure_channel(channel_name).say(input); +} + +void IRCClient::handle_user_input_in_query(const String& query_name, const String& input) +{ + if (input.is_empty()) + return; +} + +void IRCClient::handle_user_input_in_server(const String& input) +{ + if (input.is_empty()) + return; +} + bool IRCClient::is_nick_prefix(char ch) const { switch (ch) { @@ -297,6 +321,17 @@ IRCQuery& IRCClient::ensure_query(const String& name) return query_reference; } +IRCChannel& IRCClient::ensure_channel(const String& name) +{ + auto it = m_channels.find(name); + if (it != m_channels.end()) + return *(*it).value; + auto channel = IRCChannel::create(*this, name); + auto& channel_reference = *channel; + m_channels.set(name, channel.copy_ref()); + return channel_reference; +} + void IRCClient::handle_ping(const Message& msg) { if (msg.arguments.size() < 0) @@ -310,10 +345,7 @@ void IRCClient::handle_join(const Message& msg) if (msg.arguments.size() != 1) return; auto& channel_name = msg.arguments[0]; - auto it = m_channels.find(channel_name); - ASSERT(it == m_channels.end()); - auto channel = IRCChannel::create(*this, channel_name); - m_channels.set(channel_name, move(channel)); + ensure_channel(channel_name); if (on_join) on_join(channel_name); } diff --git a/Applications/IRCClient/IRCClient.h b/Applications/IRCClient/IRCClient.h index fffa333aec..04771b722a 100644 --- a/Applications/IRCClient/IRCClient.h +++ b/Applications/IRCClient/IRCClient.h @@ -13,6 +13,7 @@ class IRCClientWindowListModel; class GNotifier; class IRCClient { + friend class IRCChannel; public: IRCClient(const String& address, int port = 6667); ~IRCClient(); @@ -45,6 +46,10 @@ public: const IRCClientWindow& window_at(int index) const { return *m_windows.at(index); } IRCClientWindow& window_at(int index) { return *m_windows.at(index); } + void handle_user_input_in_channel(const String& channel_name, const String&); + void handle_user_input_in_query(const String& query_name, const String&); + void handle_user_input_in_server(const String&); + private: struct Message { String prefix; @@ -57,6 +62,7 @@ private: void send_user(); void send_nick(); void send_pong(const String& server); + void send_privmsg(const String& target, const String&); void process_line(); void handle_join(const Message&); void handle_ping(const Message&); @@ -64,6 +70,7 @@ private: void handle_privmsg(const Message&); void handle(const Message&, const String& verbatim); IRCQuery& ensure_query(const String& name); + IRCChannel& ensure_channel(const String& name); String m_hostname; int m_port { 0 }; diff --git a/Applications/IRCClient/IRCClientWindow.cpp b/Applications/IRCClient/IRCClientWindow.cpp index 374b277b37..c4eb12ae80 100644 --- a/Applications/IRCClient/IRCClientWindow.cpp +++ b/Applications/IRCClient/IRCClientWindow.cpp @@ -3,6 +3,7 @@ #include "IRCLogBufferModel.h" #include <LibGUI/GBoxLayout.h> #include <LibGUI/GTableView.h> +#include <LibGUI/GTextEditor.h> #include <LibGUI/GTextBox.h> IRCClientWindow::IRCClientWindow(IRCClient& client, Type type, const String& name, GWidget* parent) @@ -16,6 +17,19 @@ IRCClientWindow::IRCClientWindow(IRCClient& client, Type type, const String& nam m_table_view->set_headers_visible(false); m_table_view->set_font(Font::default_fixed_width_font()); + m_text_editor = new GTextEditor(GTextEditor::SingleLine, this); + m_text_editor->set_size_policy(SizePolicy::Fill, SizePolicy::Fixed); + m_text_editor->set_preferred_size({ 0, 18 }); + m_text_editor->on_return_pressed = [this] (GTextEditor& editor) { + if (m_type == Channel) + m_client.handle_user_input_in_channel(m_name, editor.text()); + else if (m_type == Query) + m_client.handle_user_input_in_query(m_name, editor.text()); + else if (m_type == Server) + m_client.handle_user_input_in_server(editor.text()); + m_text_editor->clear(); + }; + m_client.register_subwindow(*this); } diff --git a/Applications/IRCClient/IRCClientWindow.h b/Applications/IRCClient/IRCClientWindow.h index a8ef445617..4a31316e47 100644 --- a/Applications/IRCClient/IRCClientWindow.h +++ b/Applications/IRCClient/IRCClientWindow.h @@ -5,6 +5,7 @@ class IRCClient; class IRCLogBuffer; class GTableView; +class GTextEditor; class IRCClientWindow : public GWidget { public: @@ -29,5 +30,6 @@ private: Type m_type; String m_name; GTableView* m_table_view { nullptr }; + GTextEditor* m_text_editor { nullptr }; RetainPtr<IRCLogBuffer> m_log_buffer; }; diff --git a/Applications/IRCClient/IRCLogBufferModel.cpp b/Applications/IRCClient/IRCLogBufferModel.cpp index 4eb170d858..69835aaee9 100644 --- a/Applications/IRCClient/IRCLogBufferModel.cpp +++ b/Applications/IRCClient/IRCLogBufferModel.cpp @@ -2,6 +2,7 @@ #include "IRCLogBuffer.h" #include <stdio.h> #include <time.h> +#include <SharedGraphics/Font.h> IRCLogBufferModel::IRCLogBufferModel(Retained<IRCLogBuffer>&& log_buffer) : m_log_buffer(move(log_buffer)) @@ -19,14 +20,13 @@ int IRCLogBufferModel::row_count() const int IRCLogBufferModel::column_count() const { - return 4; + return Column::__Count; } String IRCLogBufferModel::column_name(int column) const { switch (column) { case Column::Timestamp: return "Time"; - case Column::Prefix: return "@"; case Column::Name: return "Name"; case Column::Text: return "Text"; } @@ -37,8 +37,7 @@ GTableModel::ColumnMetadata IRCLogBufferModel::column_metadata(int column) const { switch (column) { case Column::Timestamp: return { 60, TextAlignment::CenterLeft }; - case Column::Prefix: return { 10, TextAlignment::CenterLeft }; - case Column::Name: return { 70, TextAlignment::CenterRight }; + case Column::Name: return { 70, TextAlignment::CenterRight, &Font::default_bold_font() }; case Column::Text: return { 800, TextAlignment::CenterLeft }; } ASSERT_NOT_REACHED(); @@ -52,12 +51,7 @@ GVariant IRCLogBufferModel::data(const GModelIndex& index, Role) const auto* tm = localtime(&entry.timestamp); return String::format("%02u:%02u:%02u", tm->tm_hour, tm->tm_min, tm->tm_sec); } - case Column::Prefix: { - if (!entry.prefix) - return String(""); - return String(&entry.prefix, 1); - } - case Column::Name: return entry.sender; + case Column::Name: return String::format("<%c%s>", entry.prefix ? entry.prefix : ' ', entry.sender.characters()); case Column::Text: return entry.text; } ASSERT_NOT_REACHED(); diff --git a/Applications/IRCClient/IRCLogBufferModel.h b/Applications/IRCClient/IRCLogBufferModel.h index 5f5fe30c89..7605ca3bab 100644 --- a/Applications/IRCClient/IRCLogBufferModel.h +++ b/Applications/IRCClient/IRCLogBufferModel.h @@ -8,9 +8,9 @@ class IRCLogBufferModel final : public GTableModel { public: enum Column { Timestamp = 0, - Prefix, Name, Text, + __Count, }; explicit IRCLogBufferModel(Retained<IRCLogBuffer>&&); diff --git a/LibGUI/GTableModel.h b/LibGUI/GTableModel.h index 200796f8a0..781986d880 100644 --- a/LibGUI/GTableModel.h +++ b/LibGUI/GTableModel.h @@ -8,6 +8,7 @@ #include <LibGUI/GVariant.h> #include <SharedGraphics/TextAlignment.h> +class Font; class GTableView; enum class GSortOrder { None, Ascending, Descending }; @@ -38,6 +39,7 @@ public: struct ColumnMetadata { int preferred_width { 0 }; TextAlignment text_alignment { TextAlignment::CenterLeft }; + const Font* font { nullptr }; }; enum class Role { Display, Sort, Custom }; diff --git a/LibGUI/GTableView.cpp b/LibGUI/GTableView.cpp index 2934ab4910..d5212bed0f 100644 --- a/LibGUI/GTableView.cpp +++ b/LibGUI/GTableView.cpp @@ -167,6 +167,7 @@ void GTableView::paint_event(GPaintEvent& event) for (int column_index = 0; column_index < m_model->column_count(); ++column_index) { auto column_metadata = m_model->column_metadata(column_index); int column_width = column_metadata.preferred_width; + const Font& font = column_metadata.font ? *column_metadata.font : this->font(); bool is_key_column = m_model->key_column() == column_index; Rect cell_rect(horizontal_padding() + x_offset, y, column_width, item_height()); if (is_key_column) { @@ -177,7 +178,7 @@ void GTableView::paint_event(GPaintEvent& event) if (data.is_bitmap()) painter.blit(cell_rect.location(), data.as_bitmap(), data.as_bitmap().rect()); else - painter.draw_text(cell_rect, data.to_string(), column_metadata.text_alignment, text_color); + painter.draw_text(cell_rect, data.to_string(), font, column_metadata.text_alignment, text_color); x_offset += column_width + horizontal_padding() * 2; } ++painted_item_index; diff --git a/LibGUI/GTextEditor.cpp b/LibGUI/GTextEditor.cpp index ecc52ca7fa..5e9b8a8813 100644 --- a/LibGUI/GTextEditor.cpp +++ b/LibGUI/GTextEditor.cpp @@ -9,13 +9,15 @@ #include <fcntl.h> #include <stdio.h> -GTextEditor::GTextEditor(GWidget* parent) +GTextEditor::GTextEditor(Type type, GWidget* parent) : GWidget(parent) + , m_type(type) { set_font(GFontDatabase::the().get_by_name("Csilla Thin")); m_vertical_scrollbar = new GScrollBar(Orientation::Vertical, this); m_vertical_scrollbar->set_step(4); + m_vertical_scrollbar->set_visible(is_multi_line()); m_vertical_scrollbar->on_change = [this] (int) { update(); }; @@ -23,12 +25,14 @@ GTextEditor::GTextEditor(GWidget* parent) m_horizontal_scrollbar = new GScrollBar(Orientation::Horizontal, this); m_horizontal_scrollbar->set_step(4); m_horizontal_scrollbar->set_big_step(30); + m_horizontal_scrollbar->set_visible(is_multi_line()); m_horizontal_scrollbar->on_change = [this] (int) { update(); }; m_corner_widget = new GWidget(this); m_corner_widget->set_fill_with_background_color(true); + m_corner_widget->set_visible(is_multi_line()); m_lines.append(make<Line>()); } @@ -63,9 +67,11 @@ void GTextEditor::set_text(const String& text) void GTextEditor::resize_event(GResizeEvent& event) { update_scrollbar_ranges(); - m_vertical_scrollbar->set_relative_rect(event.size().width() - m_vertical_scrollbar->preferred_size().width(), 0, m_vertical_scrollbar->preferred_size().width(), event.size().height() - m_horizontal_scrollbar->preferred_size().height()); - m_horizontal_scrollbar->set_relative_rect(0, event.size().height() - m_horizontal_scrollbar->preferred_size().height(), event.size().width() - m_vertical_scrollbar->preferred_size().width(), m_horizontal_scrollbar->preferred_size().height()); - m_corner_widget->set_relative_rect(m_horizontal_scrollbar->rect().right() + 1, m_vertical_scrollbar->rect().bottom() + 1, m_horizontal_scrollbar->height(), m_vertical_scrollbar->width()); + if (is_multi_line()) { + m_vertical_scrollbar->set_relative_rect(event.size().width() - m_vertical_scrollbar->preferred_size().width(), 0, m_vertical_scrollbar->preferred_size().width(), event.size().height() - m_horizontal_scrollbar->preferred_size().height()); + m_horizontal_scrollbar->set_relative_rect(0, event.size().height() - m_horizontal_scrollbar->preferred_size().height(), event.size().width() - m_vertical_scrollbar->preferred_size().width(), m_horizontal_scrollbar->preferred_size().height()); + m_corner_widget->set_relative_rect(m_horizontal_scrollbar->rect().right() + 1, m_vertical_scrollbar->rect().bottom() + 1, m_horizontal_scrollbar->height(), m_vertical_scrollbar->width()); + } } void GTextEditor::update_scrollbar_ranges() @@ -441,6 +447,11 @@ void GTextEditor::insert_at_cursor(char ch) bool at_head = m_cursor.column() == 0; bool at_tail = m_cursor.column() == current_line().length(); if (ch == '\n') { + if (is_single_line()) { + if (on_return_pressed) + on_return_pressed(*this); + return; + } if (at_tail || at_head) { m_lines.insert(m_cursor.line() + (at_tail ? 1 : 0), make<Line>()); update_scrollbar_ranges(); @@ -689,6 +700,27 @@ bool GTextEditor::write_to_file(const String& path) return true; } +String GTextEditor::text() const +{ + StringBuilder builder; + for (int i = 0; i < line_count(); ++i) { + auto& line = *m_lines[i]; + builder.append(line.characters(), line.length()); + if (i != line_count() - 1) + builder.append('\n'); + } + return builder.to_string(); +} + +void GTextEditor::clear() +{ + m_lines.clear(); + m_lines.append(make<Line>()); + m_selection.clear(); + set_cursor(0, 0); + update(); +} + String GTextEditor::selected_text() const { if (!has_selection()) diff --git a/LibGUI/GTextEditor.h b/LibGUI/GTextEditor.h index de6e0355a5..07ea9e7909 100644 --- a/LibGUI/GTextEditor.h +++ b/LibGUI/GTextEditor.h @@ -62,9 +62,14 @@ private: class GTextEditor : public GWidget { public: - explicit GTextEditor(GWidget* parent); + enum Type { MultiLine, SingleLine }; + GTextEditor(Type, GWidget* parent); virtual ~GTextEditor() override; + Type type() const { return m_type; } + bool is_single_line() const { return m_type == SingleLine; } + bool is_multi_line() const { return m_type == MultiLine; } + Function<void(GTextEditor&)> on_cursor_change; void set_text(const String&); @@ -84,11 +89,16 @@ public: bool has_selection() const { return m_selection.is_valid(); } String selected_text() const; + String text() const; + + void clear(); void cut(); void copy(); void paste(); + Function<void(GTextEditor&)> on_return_pressed; + private: virtual void paint_event(GPaintEvent&) override; virtual void resize_event(GResizeEvent&) override; @@ -141,6 +151,8 @@ private: void insert_at_cursor_or_replace_selection(const String&); void delete_selection(); + Type m_type { MultiLine }; + GScrollBar* m_vertical_scrollbar { nullptr }; GScrollBar* m_horizontal_scrollbar { nullptr }; GWidget* m_corner_widget { nullptr }; |