From d466f2634d94df44402c5779395a8f3798151d45 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Mon, 18 Mar 2019 04:54:07 +0100 Subject: [PATCH] LibGUI: Add GTableModel::Role::ForegroundColor. This makes it possible to specify the text color for each table cell. Use this to make the IRCClient show unread window list items in red. --- .../FileManager/DirectoryTableModel.cpp | 22 +++++------ Applications/IRCClient/IRCAppWindow.cpp | 2 +- .../IRCClient/IRCChannelMemberListModel.cpp | 10 +++-- Applications/IRCClient/IRCLogBufferModel.cpp | 28 ++++++------- Applications/IRCClient/IRCWindowListModel.cpp | 26 +++++++++---- .../ProcessManager/ProcessTableModel.cpp | 39 ++++++++++--------- LibGUI/GTableModel.h | 2 +- LibGUI/GTableView.cpp | 21 ++++++---- LibGUI/GVariant.cpp | 16 ++++++++ LibGUI/GVariant.h | 17 ++++++++ 10 files changed, 120 insertions(+), 63 deletions(-) diff --git a/Applications/FileManager/DirectoryTableModel.cpp b/Applications/FileManager/DirectoryTableModel.cpp index e04b544098..a1a0967eab 100644 --- a/Applications/FileManager/DirectoryTableModel.cpp +++ b/Applications/FileManager/DirectoryTableModel.cpp @@ -151,18 +151,18 @@ GVariant DirectoryTableModel::data(const GModelIndex& index, Role role) const } ASSERT_NOT_REACHED(); } - ASSERT(role == Role::Display); - switch (index.column()) { - case Column::Icon: return icon_for(entry); - case Column::Name: return entry.name; - case Column::Size: return (int)entry.size; - case Column::Owner: return name_for_uid(entry.uid); - case Column::Group: return name_for_gid(entry.gid); - case Column::Permissions: return permission_string(entry.mode); - case Column::Inode: return (int)entry.inode; + if (role == Role::Display) { + switch (index.column()) { + case Column::Icon: return icon_for(entry); + case Column::Name: return entry.name; + case Column::Size: return (int)entry.size; + case Column::Owner: return name_for_uid(entry.uid); + case Column::Group: return name_for_gid(entry.gid); + case Column::Permissions: return permission_string(entry.mode); + case Column::Inode: return (int)entry.inode; + } } - ASSERT_NOT_REACHED(); - + return { }; } void DirectoryTableModel::update() diff --git a/Applications/IRCClient/IRCAppWindow.cpp b/Applications/IRCClient/IRCAppWindow.cpp index 34181fd637..d61e56fcf3 100644 --- a/Applications/IRCClient/IRCAppWindow.cpp +++ b/Applications/IRCClient/IRCAppWindow.cpp @@ -120,7 +120,7 @@ void IRCAppWindow::setup_widgets() m_window_list->set_alternating_row_colors(false); m_window_list->set_model(OwnPtr(m_client.client_window_list_model())); m_window_list->set_size_policy(SizePolicy::Fixed, SizePolicy::Fill); - m_window_list->set_preferred_size({ 120, 0 }); + m_window_list->set_preferred_size({ 100, 0 }); m_client.client_window_list_model()->on_activation = [this] (IRCWindow& window) { m_container->set_active_widget(&window); window.clear_unread_count(); diff --git a/Applications/IRCClient/IRCChannelMemberListModel.cpp b/Applications/IRCClient/IRCChannelMemberListModel.cpp index 21830ec4a9..442cdd3384 100644 --- a/Applications/IRCClient/IRCChannelMemberListModel.cpp +++ b/Applications/IRCClient/IRCChannelMemberListModel.cpp @@ -39,12 +39,14 @@ GTableModel::ColumnMetadata IRCChannelMemberListModel::column_metadata(int colum ASSERT_NOT_REACHED(); } -GVariant IRCChannelMemberListModel::data(const GModelIndex& index, Role) const +GVariant IRCChannelMemberListModel::data(const GModelIndex& index, Role role) const { - switch (index.column()) { - case Column::Name: return m_channel.member_at(index.row()); + if (role == Role::Display) { + switch (index.column()) { + case Column::Name: return m_channel.member_at(index.row()); + } } - ASSERT_NOT_REACHED(); + return { }; } void IRCChannelMemberListModel::update() diff --git a/Applications/IRCClient/IRCLogBufferModel.cpp b/Applications/IRCClient/IRCLogBufferModel.cpp index 64de1bbca1..07262fb59f 100644 --- a/Applications/IRCClient/IRCLogBufferModel.cpp +++ b/Applications/IRCClient/IRCLogBufferModel.cpp @@ -43,21 +43,23 @@ GTableModel::ColumnMetadata IRCLogBufferModel::column_metadata(int column) const ASSERT_NOT_REACHED(); } -GVariant IRCLogBufferModel::data(const GModelIndex& index, Role) const +GVariant IRCLogBufferModel::data(const GModelIndex& index, Role role) const { - 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); + 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; + } } - 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; - } - ASSERT_NOT_REACHED(); + return { }; } void IRCLogBufferModel::update() diff --git a/Applications/IRCClient/IRCWindowListModel.cpp b/Applications/IRCClient/IRCWindowListModel.cpp index b6aa3239a8..214e2a9747 100644 --- a/Applications/IRCClient/IRCWindowListModel.cpp +++ b/Applications/IRCClient/IRCWindowListModel.cpp @@ -40,17 +40,29 @@ GTableModel::ColumnMetadata IRCWindowListModel::column_metadata(int column) cons ASSERT_NOT_REACHED(); } -GVariant IRCWindowListModel::data(const GModelIndex& index, Role) const +GVariant IRCWindowListModel::data(const GModelIndex& index, Role role) const { - switch (index.column()) { - case Column::Name: { - auto& window = m_client.window_at(index.row()); - if (!window.unread_count()) + if (role == Role::Display) { + switch (index.column()) { + case Column::Name: { + auto& window = m_client.window_at(index.row()); + if (window.unread_count()) + return String::format("%s (%d)", window.name().characters(), window.unread_count()); return window.name(); - return String::format("%s (%d)\n", window.name().characters(), window.unread_count()); + } + } } + if (role == Role::ForegroundColor) { + switch (index.column()) { + case Column::Name: { + auto& window = m_client.window_at(index.row()); + if (window.unread_count()) + return Color(Color::Red); + return Color(Color::Black); + } + } } - ASSERT_NOT_REACHED(); + return { }; } void IRCWindowListModel::update() diff --git a/Applications/ProcessManager/ProcessTableModel.cpp b/Applications/ProcessManager/ProcessTableModel.cpp index 58277b5dd0..ffe20f210c 100644 --- a/Applications/ProcessManager/ProcessTableModel.cpp +++ b/Applications/ProcessManager/ProcessTableModel.cpp @@ -99,25 +99,28 @@ GVariant ProcessTableModel::data(const GModelIndex& index, Role role) const return { }; } - switch (index.column()) { - case Column::Icon: return *m_generic_process_icon; - case Column::PID: return process.current_state.pid; - case Column::State: return process.current_state.state; - case Column::User: return process.current_state.user; - case Column::Priority: - if (process.current_state.priority == "High") - return *m_high_priority_icon; - if (process.current_state.priority == "Low") - return *m_low_priority_icon; - if (process.current_state.priority == "Normal") - return *m_normal_priority_icon; - return process.current_state.priority; - case Column::Linear: return pretty_byte_size(process.current_state.linear); - case Column::Physical: return pretty_byte_size(process.current_state.physical); - case Column::CPU: return process.current_state.cpu_percent; - case Column::Name: return process.current_state.name; + if (role == Role::Display) { + switch (index.column()) { + case Column::Icon: return *m_generic_process_icon; + case Column::PID: return process.current_state.pid; + case Column::State: return process.current_state.state; + case Column::User: return process.current_state.user; + case Column::Priority: + if (process.current_state.priority == "High") + return *m_high_priority_icon; + if (process.current_state.priority == "Low") + return *m_low_priority_icon; + if (process.current_state.priority == "Normal") + return *m_normal_priority_icon; + return process.current_state.priority; + case Column::Linear: return pretty_byte_size(process.current_state.linear); + case Column::Physical: return pretty_byte_size(process.current_state.physical); + case Column::CPU: return process.current_state.cpu_percent; + case Column::Name: return process.current_state.name; + } } - ASSERT_NOT_REACHED(); + + return { }; } void ProcessTableModel::update() diff --git a/LibGUI/GTableModel.h b/LibGUI/GTableModel.h index 781986d880..659ddae91a 100644 --- a/LibGUI/GTableModel.h +++ b/LibGUI/GTableModel.h @@ -42,7 +42,7 @@ public: const Font* font { nullptr }; }; - enum class Role { Display, Sort, Custom }; + enum class Role { Display, Sort, Custom, ForegroundColor, BackgroundColor }; virtual ~GTableModel(); diff --git a/LibGUI/GTableView.cpp b/LibGUI/GTableView.cpp index c8aa9e6b49..9d4b528b5b 100644 --- a/LibGUI/GTableView.cpp +++ b/LibGUI/GTableView.cpp @@ -116,15 +116,14 @@ void GTableView::paint_event(GPaintEvent& event) int y_offset = header_height(); for (int row_index = 0; row_index < m_model->row_count(); ++row_index) { + bool is_selected_row = row_index == m_model->selected_index().row(); int y = y_offset + painted_item_index * item_height(); Color background_color; Color key_column_background_color; - Color text_color; - if (row_index == m_model->selected_index().row()) { + if (is_selected_row) { background_color = is_focused() ? Color::from_rgb(0x84351a) : Color::from_rgb(0x606060); key_column_background_color = is_focused() ? Color::from_rgb(0x84351a) : Color::from_rgb(0x606060); - text_color = Color::White; } else { if (alternating_row_colors() && (painted_item_index % 2)) { background_color = Color(210, 210, 210); @@ -133,10 +132,9 @@ void GTableView::paint_event(GPaintEvent& event) background_color = Color::White; key_column_background_color = Color(235, 235, 235); } - text_color = Color::Black; } - painter.fill_rect(row_rect(painted_item_index), background_color); + int x_offset = 0; for (int column_index = 0; column_index < m_model->column_count(); ++column_index) { auto column_metadata = m_model->column_metadata(column_index); @@ -148,11 +146,18 @@ void GTableView::paint_event(GPaintEvent& event) auto cell_rect_for_fill = cell_rect.inflated(horizontal_padding() * 2, 0); painter.fill_rect(cell_rect_for_fill, key_column_background_color); } - auto data = m_model->data({ row_index, column_index }); - if (data.is_bitmap()) + GModelIndex cell_index(row_index, column_index); + auto data = m_model->data(cell_index); + if (data.is_bitmap()) { painter.blit(cell_rect.location(), data.as_bitmap(), data.as_bitmap().rect()); - else + } else { + Color text_color; + if (is_selected_row) + text_color = Color::White; + else + text_color = m_model->data(cell_index, GTableModel::Role::ForegroundColor).to_color(Color::Black); 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/GVariant.cpp b/LibGUI/GVariant.cpp index 7def775885..1e8b7d187a 100644 --- a/LibGUI/GVariant.cpp +++ b/LibGUI/GVariant.cpp @@ -1,5 +1,9 @@ #include +GVariant::GVariant() +{ +} + GVariant::~GVariant() { switch (m_type) { @@ -48,6 +52,12 @@ GVariant::GVariant(const GraphicsBitmap& value) AK::retain_if_not_null(m_value.as_bitmap); } +GVariant::GVariant(Color color) + : m_type(Type::Color) +{ + m_value.as_color = color.value(); +} + bool GVariant::operator==(const GVariant& other) const { if (m_type != other.m_type) @@ -63,6 +73,8 @@ bool GVariant::operator==(const GVariant& other) const return as_string() == other.as_string(); case Type::Bitmap: return m_value.as_bitmap == other.m_value.as_bitmap; + case Type::Color: + return m_value.as_color == other.m_value.as_color; case Type::Invalid: break; } @@ -85,6 +97,8 @@ bool GVariant::operator<(const GVariant& other) const case Type::Bitmap: // FIXME: Maybe compare bitmaps somehow differently? return m_value.as_bitmap < other.m_value.as_bitmap; + case Type::Color: + return m_value.as_color < other.m_value.as_color; case Type::Invalid: break; } @@ -104,6 +118,8 @@ String GVariant::to_string() const return as_string(); case Type::Bitmap: return "[GraphicsBitmap]"; + case Type::Color: + return as_color().to_string(); case Type::Invalid: break; } diff --git a/LibGUI/GVariant.h b/LibGUI/GVariant.h index 87c7f27175..1346188015 100644 --- a/LibGUI/GVariant.h +++ b/LibGUI/GVariant.h @@ -11,6 +11,7 @@ public: GVariant(int); GVariant(const String&); GVariant(const GraphicsBitmap&); + GVariant(Color); ~GVariant(); enum class Type { @@ -20,6 +21,7 @@ public: Float, String, Bitmap, + Color, }; bool is_valid() const { return m_type != Type::Invalid; } @@ -28,6 +30,7 @@ public: bool is_float() const { return m_type == Type::Float; } bool is_string() const { return m_type == Type::String; } bool is_bitmap() const { return m_type == Type::Bitmap; } + bool is_color() const { return m_type == Type::Color; } Type type() const { return m_type; } bool as_bool() const @@ -60,6 +63,19 @@ public: return *m_value.as_bitmap; } + Color as_color() const + { + ASSERT(type() == Type::Color); + return Color::from_rgba(m_value.as_color); + } + + Color to_color(Color default_value) const + { + if (type() == Type::Color) + return as_color(); + return default_value; + } + String to_string() const; bool operator==(const GVariant&) const; @@ -72,6 +88,7 @@ private: bool as_bool; int as_int; float as_float; + RGBA32 as_color; } m_value; Type m_type { Type::Invalid };