Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] feat: TableView widget #768

Draft
wants to merge 22 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
949a318
Implemented rudimentary table widget, currently not interactible
Adam-Cosner Dec 27, 2024
8021271
Merge branch 'master' of https://github.com/Adam-Cosner/libcosmic
Adam-Cosner Dec 27, 2024
8132f8f
Compact view implemented
Adam-Cosner Dec 30, 2024
19dfbcb
Merge branch 'pop-os:master' into master
Adam-Cosner Dec 30, 2024
fc531b5
Added table view example
Adam-Cosner Dec 30, 2024
e608192
Merge branch 'master' of https://github.com/Adam-Cosner/libcosmic
Adam-Cosner Dec 30, 2024
ad4ed38
Fixed table view issues with selection on compact view and some spaci…
Adam-Cosner Dec 30, 2024
5c96696
Merge branch 'pop-os:master' into master
Adam-Cosner Jan 1, 2025
825792e
Added custom widget version of table
Adam-Cosner Jan 2, 2025
dd9d1c0
Revert "Added custom widget version of table"
Adam-Cosner Jan 3, 2025
2401c2a
Added table function for generic table view creation
Adam-Cosner Jan 4, 2025
2305e25
Use derive_setters for TableView
Adam-Cosner Jan 4, 2025
91c35e8
Made compact and standard table view into separate widgets
Adam-Cosner Jan 4, 2025
4111fcb
Added doc inline to widget
Adam-Cosner Jan 5, 2025
68fdd8f
Added category context menus
Adam-Cosner Jan 6, 2025
9f2e554
Merge branch 'pop-os:master' into master
Adam-Cosner Jan 11, 2025
0b0e143
Merge branch 'master' of https://github.com/Adam-Cosner/libcosmic
Adam-Cosner Jan 11, 2025
ea4e6c6
Added item contexts and changed the context menu functions to take bu…
Adam-Cosner Jan 11, 2025
1e28f39
Ran cargo fmt
Adam-Cosner Jan 11, 2025
d4bce76
Fixed warnings
Adam-Cosner Jan 11, 2025
160070c
Added more mouse interaction options
Adam-Cosner Jan 11, 2025
b29d8f4
Merge branch 'pop-os:master' into master
Adam-Cosner Jan 14, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions examples/table-view/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "table-view"
version = "0.1.0"
edition = "2021"

[dependencies]
tracing = "0.1.37"
tracing-subscriber = "0.3.17"
tracing-log = "0.2.0"
chrono = "*"

[dependencies.libcosmic]
features = ["debug", "multi-window", "wayland", "winit", "desktop", "tokio"]
path = "../.."
default-features = false
211 changes: 211 additions & 0 deletions examples/table-view/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
// Copyright 2023 System76 <[email protected]>
// SPDX-License-Identifier: MPL-2.0

//! Table API example

use chrono::Datelike;
use cosmic::app::{Core, Settings, Task};
use cosmic::iced_core::Size;
use cosmic::prelude::*;
use cosmic::widget::table;
use cosmic::widget::{self, nav_bar};
use cosmic::{executor, iced};

#[derive(Debug, Default, PartialEq, Eq, Clone, Copy)]
pub enum Category {
#[default]
Name,
Date,
Size,
}

impl std::fmt::Display for Category {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
Self::Name => "Name",
Self::Date => "Date",
Self::Size => "Size",
})
}
}

impl table::ItemCategory for Category {
fn width(&self) -> iced::Length {
match self {
Self::Name => iced::Length::Fill,
Self::Date => iced::Length::Fixed(200.0),
Self::Size => iced::Length::Fixed(150.0),
}
}
}

struct Item {
name: String,
date: chrono::DateTime<chrono::Local>,
size: u64,
}

impl Default for Item {
fn default() -> Self {
Self {
name: Default::default(),
date: Default::default(),
size: Default::default(),
}
}
}

impl table::ItemInterface<Category> for Item {
fn get_icon(&self, category: Category) -> Option<cosmic::widget::Icon> {
if category == Category::Name {
Some(cosmic::widget::icon::from_name("application-x-executable-symbolic").icon())
} else {
None
}
}

fn get_text(&self, category: Category) -> std::borrow::Cow<'static, str> {
match category {
Category::Name => self.name.clone().into(),
Category::Date => self.date.format("%Y/%m/%d").to_string().into(),
Category::Size => format!("{} items", self.size).into(),
}
}

fn compare(&self, other: &Self, category: Category) -> std::cmp::Ordering {
match category {
Category::Name => self.name.to_lowercase().cmp(&other.name.to_lowercase()),
Category::Date => self.date.cmp(&other.date),
Category::Size => self.size.cmp(&other.size),
}
}
}

/// Runs application with these settings
#[rustfmt::skip]
fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
let _ = tracing_log::LogTracer::init();

let settings = Settings::default()
.size(Size::new(1024., 768.));

cosmic::app::run::<App>(settings, ())?;

Ok(())
}

/// Messages that are used specifically by our [`App`].
#[derive(Clone, Debug)]
pub enum Message {
ItemSelect(table::Entity),
CategorySelect(Category, bool),
}

/// The [`App`] stores application-specific state.
pub struct App {
core: Core,
table_model: table::SingleSelectModel<Item, Category>,
}

/// Implement [`cosmic::Application`] to integrate with COSMIC.
impl cosmic::Application for App {
/// Default async executor to use with the app.
type Executor = executor::Default;

/// Argument received [`cosmic::Application::new`].
type Flags = ();

/// Message type specific to our [`App`].
type Message = Message;

/// The unique application ID to supply to the window manager.
const APP_ID: &'static str = "org.cosmic.AppDemoTable";

fn core(&self) -> &Core {
&self.core
}

fn core_mut(&mut self) -> &mut Core {
&mut self.core
}

/// Creates the application, and optionally emits task on initialize.
fn init(core: Core, _: Self::Flags) -> (Self, Task<Self::Message>) {
let mut nav_model = nav_bar::Model::default();

nav_model.activate_position(0);

let mut table_model =
table::Model::new(vec![Category::Name, Category::Date, Category::Size]);

table_model.insert().item(Item {
name: "Foo".into(),
date: chrono::DateTime::default()
.with_day(1)
.unwrap()
.with_month(1)
.unwrap()
.with_year(1970)
.unwrap(),
size: 2,
});
table_model.insert().item(Item {
name: "Bar".into(),
date: chrono::DateTime::default()
.with_day(2)
.unwrap()
.with_month(1)
.unwrap()
.with_year(1970)
.unwrap(),
size: 4,
});
table_model.insert().item(Item {
name: "Baz".into(),
date: chrono::DateTime::default()
.with_day(3)
.unwrap()
.with_month(1)
.unwrap()
.with_year(1970)
.unwrap(),
size: 12,
});

let app = App { core, table_model };

let command = Task::none();

(app, command)
}

/// Handle application events here.
fn update(&mut self, message: Self::Message) -> Task<Self::Message> {
match message {
Message::ItemSelect(entity) => self.table_model.activate(entity),
Message::CategorySelect(category, descending) => {
self.table_model.sort(category, descending)
}
}
Task::none()
}

/// Creates a view after each update.
fn view(&self) -> Element<Self::Message> {
cosmic::widget::responsive(|size| {
if size.width < 600.0 {
widget::compact_table(&self.table_model)
.on_item_select(Message::ItemSelect)
.on_category_select(Message::CategorySelect)
.apply(Element::from)
} else {
widget::table(&self.table_model)
.on_item_select(Message::ItemSelect)
.on_category_select(Message::CategorySelect)
.apply(Element::from)
}
})
.into()
}
}
4 changes: 3 additions & 1 deletion src/widget/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,9 @@ pub use spin_button::{spin_button, vertical as vertical_spin_button, SpinButton}

pub mod tab_bar;

pub mod table;
pub use table::{compact_table, table};
mmstick marked this conversation as resolved.
Show resolved Hide resolved

pub mod text;
#[doc(inline)]
pub use text::{text, Text};
Expand All @@ -337,7 +340,6 @@ pub use toggler::toggler;
pub use tooltip::{tooltip, Tooltip};
pub mod tooltip {
use crate::Element;
use std::borrow::Cow;

pub use iced::widget::tooltip::Position;

Expand Down
47 changes: 47 additions & 0 deletions src/widget/table/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//! A widget allowing the user to display tables of information with optional sorting by category
//!

pub mod model;
pub use model::{
category::ItemCategory,
category::ItemInterface,
selection::{MultiSelect, SingleSelect},
Entity, Model,
};
pub mod widget;
pub use widget::compact::CompactTableView;
pub use widget::standard::TableView;

pub type SingleSelectTableView<'a, Item, Category, Message> =
TableView<'a, SingleSelect, Item, Category, Message>;
pub type SingleSelectModel<Item, Category> = Model<SingleSelect, Item, Category>;

pub type MultiSelectTableView<'a, Item, Category, Message> =
TableView<'a, MultiSelect, Item, Category, Message>;
pub type MultiSelectModel<Item, Category> = Model<MultiSelect, Item, Category>;
Adam-Cosner marked this conversation as resolved.
Show resolved Hide resolved

pub fn table<'a, SelectionMode, Item, Category, Message>(
model: &'a Model<SelectionMode, Item, Category>,
) -> TableView<'a, SelectionMode, Item, Category, Message>
where
Message: Clone,
SelectionMode: Default,
Category: ItemCategory,
Item: ItemInterface<Category>,
Model<SelectionMode, Item, Category>: model::selection::Selectable,
{
TableView::new(model)
}

pub fn compact_table<'a, SelectionMode, Item, Category, Message>(
model: &'a Model<SelectionMode, Item, Category>,
) -> CompactTableView<'a, SelectionMode, Item, Category, Message>
where
Message: Clone,
SelectionMode: Default,
Category: ItemCategory,
Item: ItemInterface<Category>,
Model<SelectionMode, Item, Category>: model::selection::Selectable,
{
CompactTableView::new(model)
}
17 changes: 17 additions & 0 deletions src/widget/table/model/category.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use std::borrow::Cow;

use crate::widget::Icon;

/// Implementation of std::fmt::Display allows user to customize the header
/// Ideally, this is implemented on an enum.
pub trait ItemCategory: Default + std::fmt::Display + Clone + Copy + PartialEq + Eq {
/// Function that gets the width of the data
fn width(&self) -> iced::Length;
}

pub trait ItemInterface<Category: ItemCategory>: Default {
fn get_icon(&self, category: Category) -> Option<Icon>;
fn get_text(&self, category: Category) -> Cow<'static, str>;

fn compare(&self, other: &Self, category: Category) -> std::cmp::Ordering;
}
Loading
Loading