timeline: Group virtual items in a single type
Now that the SDK timeline API handles most of the logic, there is just a lot of boilerplate code for little to no gain.
This commit is contained in:
parent
cfeae158ad
commit
2a07a1b67c
8 changed files with 205 additions and 347 deletions
|
@ -91,7 +91,6 @@ src/session/room/event/event_actions.rs
|
|||
src/session/room/member.rs
|
||||
src/session/room/member_role.rs
|
||||
src/session/room/mod.rs
|
||||
src/session/room/timeline/timeline_day_divider.rs
|
||||
src/session/room_creation/mod.rs
|
||||
src/session/room_list.rs
|
||||
src/session/sidebar/category/category_row.rs
|
||||
|
|
|
@ -9,10 +9,7 @@ use crate::{
|
|||
content::room_history::{
|
||||
message_row::MessageRow, DividerRow, RoomHistory, StateRow, TypingRow,
|
||||
},
|
||||
room::{
|
||||
Event, EventActions, EventTexture, PlaceholderKind, TimelineDayDivider, TimelineItem,
|
||||
TimelineNewMessagesDivider, TimelinePlaceholder,
|
||||
},
|
||||
room::{Event, EventActions, EventTexture, TimelineItem, VirtualItem, VirtualItemKind},
|
||||
},
|
||||
utils::BoundObjectWeakRef,
|
||||
};
|
||||
|
@ -270,49 +267,21 @@ impl ItemRow {
|
|||
|
||||
self.set_event_widget(event);
|
||||
self.set_action_group(self.set_event_actions(Some(event.upcast_ref())));
|
||||
} else if let Some(divider) = item.downcast_ref::<TimelineDayDivider>() {
|
||||
} else if let Some(item) = item.downcast_ref::<VirtualItem>() {
|
||||
self.set_popover(None);
|
||||
self.set_action_group(None);
|
||||
self.set_event_actions(None);
|
||||
|
||||
let child = if let Some(child) =
|
||||
self.child().and_then(|w| w.downcast::<DividerRow>().ok())
|
||||
{
|
||||
child
|
||||
} else {
|
||||
let child = DividerRow::new();
|
||||
self.set_child(Some(&child));
|
||||
child
|
||||
};
|
||||
|
||||
let binding = divider
|
||||
.bind_property("formatted-date", &child, "label")
|
||||
.flags(glib::BindingFlags::SYNC_CREATE)
|
||||
.build();
|
||||
imp.binding.replace(Some(binding));
|
||||
} else if let Some(item) = item.downcast_ref::<TimelinePlaceholder>() {
|
||||
match item.kind() {
|
||||
PlaceholderKind::Spinner => {
|
||||
if self
|
||||
.child()
|
||||
.filter(|widget| widget.is::<Spinner>())
|
||||
.is_none()
|
||||
{
|
||||
self.set_popover(None);
|
||||
self.set_action_group(None);
|
||||
self.set_event_actions(None);
|
||||
|
||||
VirtualItemKind::Spinner => {
|
||||
if !self.child().map_or(false, |widget| widget.is::<Spinner>()) {
|
||||
let spinner = Spinner::default();
|
||||
spinner.set_margin_top(12);
|
||||
spinner.set_margin_bottom(12);
|
||||
self.set_child(Some(&spinner));
|
||||
}
|
||||
}
|
||||
PlaceholderKind::Typing => {
|
||||
self.set_popover(None);
|
||||
self.set_action_group(None);
|
||||
self.set_event_actions(None);
|
||||
|
||||
VirtualItemKind::Typing => {
|
||||
let child = if let Some(child) =
|
||||
self.child().and_then(|w| w.downcast::<TypingRow>().ok())
|
||||
{
|
||||
|
@ -330,13 +299,41 @@ impl ItemRow {
|
|||
.map(|room| room.typing_list()),
|
||||
);
|
||||
}
|
||||
PlaceholderKind::TimelineStart => {
|
||||
self.set_popover(None);
|
||||
self.set_action_group(None);
|
||||
self.set_event_actions(None);
|
||||
|
||||
VirtualItemKind::TimelineStart => {
|
||||
let label = gettext("This is the start of the visible history");
|
||||
|
||||
if let Some(Ok(child)) = self.child().map(|w| w.downcast::<DividerRow>()) {
|
||||
child.set_label(&label);
|
||||
} else {
|
||||
let child = DividerRow::with_label(label);
|
||||
self.set_child(Some(&child));
|
||||
};
|
||||
}
|
||||
VirtualItemKind::DayDivider(date) => {
|
||||
let child = if let Some(child) =
|
||||
self.child().and_then(|w| w.downcast::<DividerRow>().ok())
|
||||
{
|
||||
child
|
||||
} else {
|
||||
let child = DividerRow::new();
|
||||
self.set_child(Some(&child));
|
||||
child
|
||||
};
|
||||
|
||||
let fmt = if date.year() == glib::DateTime::now_local().unwrap().year() {
|
||||
// Translators: This is a date format in the day divider without the
|
||||
// year
|
||||
gettext("%A, %B %e")
|
||||
} else {
|
||||
// Translators: This is a date format in the day divider with the year
|
||||
gettext("%A, %B %e, %Y")
|
||||
};
|
||||
|
||||
child.set_label(&date.format(&fmt).unwrap())
|
||||
}
|
||||
VirtualItemKind::NewMessages => {
|
||||
let label = gettext("New Messages");
|
||||
|
||||
if let Some(Ok(child)) = self.child().map(|w| w.downcast::<DividerRow>()) {
|
||||
child.set_label(&label);
|
||||
} else {
|
||||
|
@ -345,19 +342,6 @@ impl ItemRow {
|
|||
};
|
||||
}
|
||||
}
|
||||
} else if item.downcast_ref::<TimelineNewMessagesDivider>().is_some() {
|
||||
self.set_popover(None);
|
||||
self.set_action_group(None);
|
||||
self.set_event_actions(None);
|
||||
|
||||
let label = gettext("New Messages");
|
||||
|
||||
if let Some(Ok(child)) = self.child().map(|w| w.downcast::<DividerRow>()) {
|
||||
child.set_label(&label);
|
||||
} else {
|
||||
let child = DividerRow::with_label(label);
|
||||
self.set_child(Some(&child));
|
||||
};
|
||||
}
|
||||
}
|
||||
imp.item.replace(item);
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
mod timeline_day_divider;
|
||||
mod timeline_item;
|
||||
mod timeline_new_messages_divider;
|
||||
mod timeline_placeholder;
|
||||
mod virtual_item;
|
||||
|
||||
use std::{
|
||||
collections::{HashMap, VecDeque},
|
||||
|
@ -18,11 +16,11 @@ use matrix_sdk::{
|
|||
Error as MatrixError,
|
||||
};
|
||||
use ruma::events::AnySyncTimelineEvent;
|
||||
pub use timeline_day_divider::TimelineDayDivider;
|
||||
pub use timeline_item::{TimelineItem, TimelineItemExt, TimelineItemImpl};
|
||||
pub use timeline_new_messages_divider::TimelineNewMessagesDivider;
|
||||
pub use timeline_placeholder::{PlaceholderKind, TimelinePlaceholder};
|
||||
|
||||
pub use self::{
|
||||
timeline_item::{TimelineItem, TimelineItemExt, TimelineItemImpl},
|
||||
virtual_item::{VirtualItem, VirtualItemKind},
|
||||
};
|
||||
use super::{Event, EventKey, Room};
|
||||
use crate::{spawn, spawn_tokio};
|
||||
|
||||
|
@ -158,7 +156,7 @@ impl Timeline {
|
|||
let imp = self.imp();
|
||||
|
||||
if imp.has_typing.get() && position == self.n_items_in_list() {
|
||||
return Some(TimelinePlaceholder::typing().upcast());
|
||||
return Some(VirtualItem::typing().upcast());
|
||||
}
|
||||
|
||||
imp.list
|
||||
|
@ -387,9 +385,8 @@ impl Timeline {
|
|||
if let Some(event) = item.downcast_ref::<Event>() {
|
||||
self.imp().event_map.borrow_mut().remove(&event.key());
|
||||
} else if item
|
||||
.downcast_ref::<TimelinePlaceholder>()
|
||||
.filter(|item| item.kind() == PlaceholderKind::Spinner)
|
||||
.is_some()
|
||||
.downcast_ref::<VirtualItem>()
|
||||
.map_or(false, |item| item.kind() == VirtualItemKind::Spinner)
|
||||
&& self.state() == TimelineState::Loading
|
||||
{
|
||||
self.set_state(TimelineState::Ready)
|
||||
|
|
|
@ -1,130 +0,0 @@
|
|||
use gettextrs::gettext;
|
||||
use gtk::{glib, prelude::*, subclass::prelude::*};
|
||||
use ruma::MilliSecondsSinceUnixEpoch;
|
||||
|
||||
use super::{TimelineItem, TimelineItemImpl};
|
||||
|
||||
mod imp {
|
||||
use std::cell::RefCell;
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct TimelineDayDivider {
|
||||
/// The date of this divider.
|
||||
pub date: RefCell<Option<glib::DateTime>>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for TimelineDayDivider {
|
||||
const NAME: &'static str = "TimelineDayDivider";
|
||||
type Type = super::TimelineDayDivider;
|
||||
type ParentType = TimelineItem;
|
||||
}
|
||||
|
||||
impl ObjectImpl for TimelineDayDivider {
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||
vec![
|
||||
glib::ParamSpecBoxed::builder::<glib::DateTime>("date")
|
||||
.explicit_notify()
|
||||
.build(),
|
||||
glib::ParamSpecString::builder("formatted-date")
|
||||
.read_only()
|
||||
.build(),
|
||||
]
|
||||
});
|
||||
|
||||
PROPERTIES.as_ref()
|
||||
}
|
||||
|
||||
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
|
||||
match pspec.name() {
|
||||
"date" => self.obj().set_date(value.get().unwrap()),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||
let obj = self.obj();
|
||||
|
||||
match pspec.name() {
|
||||
"date" => obj.date().to_value(),
|
||||
"formatted-date" => obj.formatted_date().to_value(),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TimelineItemImpl for TimelineDayDivider {
|
||||
fn id(&self) -> String {
|
||||
format!(
|
||||
"TimelineDayDivider::{}",
|
||||
self.obj()
|
||||
.date()
|
||||
.map(|d| d.format("%F").unwrap())
|
||||
.unwrap_or_default()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
/// A day divider in the timeline.
|
||||
pub struct TimelineDayDivider(ObjectSubclass<imp::TimelineDayDivider>) @extends TimelineItem;
|
||||
}
|
||||
|
||||
impl TimelineDayDivider {
|
||||
pub fn new(date: glib::DateTime) -> Self {
|
||||
glib::Object::builder().property("date", &date).build()
|
||||
}
|
||||
|
||||
/// Creates a new `TimelineDayDivider` for the given timestamp.
|
||||
///
|
||||
/// If the timestamp is out of range for `glib::DateTime` (later than the
|
||||
/// end of year 9999), this fallbacks to creating a divider with the
|
||||
/// current local time.
|
||||
///
|
||||
/// Panics if an error occurred when accessing the current local time.
|
||||
pub fn with_timestamp(timestamp: MilliSecondsSinceUnixEpoch) -> Self {
|
||||
let date = glib::DateTime::from_unix_utc(timestamp.as_secs().into())
|
||||
.expect("The day divider timestamp should be before year 10,000");
|
||||
Self::new(date)
|
||||
}
|
||||
|
||||
/// The date of this divider.
|
||||
pub fn date(&self) -> Option<glib::DateTime> {
|
||||
self.imp().date.borrow().clone()
|
||||
}
|
||||
|
||||
/// Set the date of this divider.
|
||||
pub fn set_date(&self, date: Option<glib::DateTime>) {
|
||||
let imp = self.imp();
|
||||
|
||||
if imp.date.borrow().as_ref() == date.as_ref() {
|
||||
return;
|
||||
}
|
||||
|
||||
imp.date.replace(date);
|
||||
self.notify("date");
|
||||
self.notify("formatted-date");
|
||||
}
|
||||
|
||||
/// The localized representation of the date of this divider.
|
||||
pub fn formatted_date(&self) -> String {
|
||||
self.date()
|
||||
.map(|date| {
|
||||
let fmt = if date.year() == glib::DateTime::now_local().unwrap().year() {
|
||||
// Translators: This is a date format in the day divider without the year
|
||||
gettext("%A, %B %e")
|
||||
} else {
|
||||
// Translators: This is a date format in the day divider with the year
|
||||
gettext("%A, %B %e, %Y")
|
||||
};
|
||||
date.format(&fmt).unwrap().to_string()
|
||||
})
|
||||
.unwrap_or_default()
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
use gtk::{glib, prelude::*, subclass::prelude::*};
|
||||
use matrix_sdk::room::timeline::{TimelineItem as SdkTimelineItem, VirtualTimelineItem};
|
||||
use matrix_sdk::room::timeline::TimelineItem as SdkTimelineItem;
|
||||
|
||||
use super::{TimelineDayDivider, TimelineNewMessagesDivider, TimelinePlaceholder};
|
||||
use super::VirtualItem;
|
||||
use crate::session::{
|
||||
room::{Event, Member},
|
||||
Room,
|
||||
|
@ -124,20 +124,8 @@ impl TimelineItem {
|
|||
/// Constructs the proper child type.
|
||||
pub fn new(item: &SdkTimelineItem, room: &Room) -> Self {
|
||||
match item {
|
||||
SdkTimelineItem::Event(event) => {
|
||||
let event = Event::new(event.clone(), room);
|
||||
event.upcast()
|
||||
}
|
||||
SdkTimelineItem::Virtual(item) => match item {
|
||||
VirtualTimelineItem::DayDivider(ts) => {
|
||||
TimelineDayDivider::with_timestamp(*ts).upcast()
|
||||
}
|
||||
VirtualTimelineItem::ReadMarker => TimelineNewMessagesDivider::new().upcast(),
|
||||
VirtualTimelineItem::LoadingIndicator => TimelinePlaceholder::spinner().upcast(),
|
||||
VirtualTimelineItem::TimelineStart => {
|
||||
TimelinePlaceholder::timeline_start().upcast()
|
||||
}
|
||||
},
|
||||
SdkTimelineItem::Event(event) => Event::new(event.clone(), room).upcast(),
|
||||
SdkTimelineItem::Virtual(item) => VirtualItem::new(item).upcast(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
use gtk::{glib, subclass::prelude::*};
|
||||
|
||||
use super::{TimelineItem, TimelineItemImpl};
|
||||
|
||||
mod imp {
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct TimelineNewMessagesDivider;
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for TimelineNewMessagesDivider {
|
||||
const NAME: &'static str = "TimelineNewMessagesDivider";
|
||||
type Type = super::TimelineNewMessagesDivider;
|
||||
type ParentType = TimelineItem;
|
||||
}
|
||||
|
||||
impl ObjectImpl for TimelineNewMessagesDivider {}
|
||||
|
||||
impl TimelineItemImpl for TimelineNewMessagesDivider {
|
||||
fn id(&self) -> String {
|
||||
"TimelineNewMessagesDivider".to_owned()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
/// A divider for the read marker in the timeline.
|
||||
pub struct TimelineNewMessagesDivider(ObjectSubclass<imp::TimelineNewMessagesDivider>) @extends TimelineItem;
|
||||
}
|
||||
|
||||
impl TimelineNewMessagesDivider {
|
||||
pub fn new() -> Self {
|
||||
glib::Object::new()
|
||||
}
|
||||
}
|
|
@ -1,99 +0,0 @@
|
|||
use gtk::{glib, prelude::*, subclass::prelude::*};
|
||||
|
||||
use super::{TimelineItem, TimelineItemImpl};
|
||||
|
||||
#[derive(Debug, Default, Hash, Eq, PartialEq, Clone, Copy, glib::Enum)]
|
||||
#[repr(u32)]
|
||||
#[enum_type(name = "PlaceholderKind")]
|
||||
pub enum PlaceholderKind {
|
||||
#[default]
|
||||
Spinner = 0,
|
||||
Typing = 1,
|
||||
TimelineStart = 2,
|
||||
}
|
||||
|
||||
mod imp {
|
||||
use std::cell::Cell;
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct TimelinePlaceholder {
|
||||
/// The kind of placeholder.
|
||||
pub kind: Cell<PlaceholderKind>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for TimelinePlaceholder {
|
||||
const NAME: &'static str = "TimelinePlaceholder";
|
||||
type Type = super::TimelinePlaceholder;
|
||||
type ParentType = TimelineItem;
|
||||
}
|
||||
|
||||
impl ObjectImpl for TimelinePlaceholder {
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||
vec![glib::ParamSpecEnum::builder::<PlaceholderKind>("kind")
|
||||
.construct_only()
|
||||
.build()]
|
||||
});
|
||||
|
||||
PROPERTIES.as_ref()
|
||||
}
|
||||
|
||||
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
|
||||
match pspec.name() {
|
||||
"kind" => self.kind.set(value.get().unwrap()),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||
match pspec.name() {
|
||||
"kind" => self.kind.get().to_value(),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TimelineItemImpl for TimelinePlaceholder {
|
||||
fn id(&self) -> String {
|
||||
match self.obj().kind() {
|
||||
PlaceholderKind::Spinner => "TimelinePlaceholder::Spinner",
|
||||
PlaceholderKind::Typing => "TimelinePlaceholder::Typing",
|
||||
PlaceholderKind::TimelineStart => "TimelinePlaceholder::TimelineStart",
|
||||
}
|
||||
.to_owned()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
/// A loading spinner in the timeline.
|
||||
pub struct TimelinePlaceholder(ObjectSubclass<imp::TimelinePlaceholder>) @extends TimelineItem;
|
||||
}
|
||||
|
||||
impl TimelinePlaceholder {
|
||||
pub fn spinner() -> Self {
|
||||
glib::Object::new()
|
||||
}
|
||||
|
||||
pub fn typing() -> Self {
|
||||
glib::Object::builder()
|
||||
.property("kind", PlaceholderKind::Typing)
|
||||
.build()
|
||||
}
|
||||
|
||||
pub fn timeline_start() -> Self {
|
||||
glib::Object::builder()
|
||||
.property("kind", PlaceholderKind::TimelineStart)
|
||||
.build()
|
||||
}
|
||||
|
||||
/// The kind of placeholder.
|
||||
pub fn kind(&self) -> PlaceholderKind {
|
||||
self.imp().kind.get()
|
||||
}
|
||||
}
|
155
src/session/room/timeline/virtual_item.rs
Normal file
155
src/session/room/timeline/virtual_item.rs
Normal file
|
@ -0,0 +1,155 @@
|
|||
use gtk::{glib, prelude::*, subclass::prelude::*};
|
||||
use matrix_sdk::room::timeline::VirtualTimelineItem;
|
||||
use ruma::MilliSecondsSinceUnixEpoch;
|
||||
|
||||
use super::{TimelineItem, TimelineItemImpl};
|
||||
|
||||
#[derive(Debug, Default, Eq, PartialEq, Clone)]
|
||||
pub enum VirtualItemKind {
|
||||
#[default]
|
||||
Spinner,
|
||||
Typing,
|
||||
TimelineStart,
|
||||
DayDivider(glib::DateTime),
|
||||
NewMessages,
|
||||
}
|
||||
|
||||
impl VirtualItemKind {
|
||||
/// Convert this into a [`VirtualItemKindBoxed`].
|
||||
fn boxed(self) -> VirtualItemKindBoxed {
|
||||
VirtualItemKindBoxed(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, glib::Boxed)]
|
||||
#[boxed_type(name = "VirtualItemKindBoxed")]
|
||||
struct VirtualItemKindBoxed(VirtualItemKind);
|
||||
|
||||
mod imp {
|
||||
use std::cell::RefCell;
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct VirtualItem {
|
||||
/// The kind of virtual item.
|
||||
pub kind: RefCell<VirtualItemKind>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for VirtualItem {
|
||||
const NAME: &'static str = "TimelineVirtualItem";
|
||||
type Type = super::VirtualItem;
|
||||
type ParentType = TimelineItem;
|
||||
}
|
||||
|
||||
impl ObjectImpl for VirtualItem {
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||
vec![
|
||||
glib::ParamSpecBoxed::builder::<VirtualItemKindBoxed>("kind")
|
||||
.construct()
|
||||
.write_only()
|
||||
.build(),
|
||||
]
|
||||
});
|
||||
|
||||
PROPERTIES.as_ref()
|
||||
}
|
||||
|
||||
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
|
||||
match pspec.name() {
|
||||
"kind" => {
|
||||
let boxed = value.get::<VirtualItemKindBoxed>().unwrap();
|
||||
self.kind.replace(boxed.0);
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TimelineItemImpl for VirtualItem {
|
||||
fn id(&self) -> String {
|
||||
match self.obj().kind() {
|
||||
VirtualItemKind::Spinner => "VirtualItem::Spinner".to_owned(),
|
||||
VirtualItemKind::Typing => "VirtualItem::Typing".to_owned(),
|
||||
VirtualItemKind::TimelineStart => "VirtualItem::TimelineStart".to_owned(),
|
||||
VirtualItemKind::DayDivider(date) => {
|
||||
format!("VirtualItem::DayDivider({})", date.format("%F").unwrap())
|
||||
}
|
||||
VirtualItemKind::NewMessages => "VirtualItem::NewMessages".to_owned(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
/// A virtual item in the timeline.
|
||||
///
|
||||
/// A virtual item is an item not based on a timeline event.
|
||||
pub struct VirtualItem(ObjectSubclass<imp::VirtualItem>) @extends TimelineItem;
|
||||
}
|
||||
|
||||
impl VirtualItem {
|
||||
/// Create a new `VirtualItem` from a virtual timeline item.
|
||||
pub fn new(item: &VirtualTimelineItem) -> Self {
|
||||
match item {
|
||||
VirtualTimelineItem::DayDivider(ts) => Self::day_divider_with_timestamp(*ts),
|
||||
VirtualTimelineItem::ReadMarker => Self::new_messages(),
|
||||
VirtualTimelineItem::LoadingIndicator => Self::spinner(),
|
||||
VirtualTimelineItem::TimelineStart => Self::timeline_start(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a spinner virtual item.
|
||||
pub fn spinner() -> Self {
|
||||
glib::Object::builder()
|
||||
.property("kind", VirtualItemKind::Spinner.boxed())
|
||||
.build()
|
||||
}
|
||||
|
||||
/// Create a typing virtual item.
|
||||
pub fn typing() -> Self {
|
||||
glib::Object::builder()
|
||||
.property("kind", VirtualItemKind::Typing.boxed())
|
||||
.build()
|
||||
}
|
||||
|
||||
/// Create a timeline start virtual item.
|
||||
pub fn timeline_start() -> Self {
|
||||
glib::Object::builder()
|
||||
.property("kind", VirtualItemKind::TimelineStart.boxed())
|
||||
.build()
|
||||
}
|
||||
|
||||
/// Create a new messages virtual item.
|
||||
pub fn new_messages() -> Self {
|
||||
glib::Object::builder()
|
||||
.property("kind", VirtualItemKind::NewMessages.boxed())
|
||||
.build()
|
||||
}
|
||||
|
||||
/// Creates a new day divider virtual item for the given timestamp.
|
||||
///
|
||||
/// If the timestamp is out of range for `glib::DateTime` (later than the
|
||||
/// end of year 9999), this fallbacks to creating a divider with the
|
||||
/// current local time.
|
||||
///
|
||||
/// Panics if an error occurred when accessing the current local time.
|
||||
pub fn day_divider_with_timestamp(timestamp: MilliSecondsSinceUnixEpoch) -> Self {
|
||||
let date = glib::DateTime::from_unix_utc(timestamp.as_secs().into())
|
||||
.or_else(|_| glib::DateTime::now_local())
|
||||
.expect("We should be able to get the current time");
|
||||
|
||||
glib::Object::builder()
|
||||
.property("kind", VirtualItemKind::DayDivider(date).boxed())
|
||||
.build()
|
||||
}
|
||||
|
||||
/// The kind of virtual item.
|
||||
pub fn kind(&self) -> VirtualItemKind {
|
||||
self.imp().kind.borrow().clone()
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue