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

Split things off from window file #252

Merged
merged 5 commits into from
Sep 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
326 changes: 10 additions & 316 deletions src/ui/window.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
pub mod conversation_message_item;
pub mod dynamic_list_view;
pub mod folder_conversation_item;
pub mod folders_list_item;
pub mod message_view;

use gtk::prelude::*;
use gtk::subclass::prelude::*;
use gtk::{gio, glib, gsk, pango};
Expand All @@ -18,279 +24,10 @@ use crate::controllers::{Application, ApplicationMessage};
use crate::models;
use crate::models::folder_conversations_list::row_data::ConversationRowData;

use self::dynamic_list_view::DynamicListView;
use self::dynamic_list_view::{DynamicListView, DynamicListViewStore};
use self::folder_conversation_item::FolderConversationItem;

pub mod dynamic_list_view;

#[derive(Clone, Debug, Default)]
struct DynamicListViewStore<KeyType, ItemType> {
map: HashMap<KeyType, ItemType>,
}

impl<KeyType, ItemType> DynamicListViewStore<KeyType, ItemType>
where
KeyType: Eq + Hash,
{
fn len(&self) -> usize {
self.map.len()
}

fn insert(&mut self, item_position: KeyType, child: ItemType) -> Option<ItemType> {
self.map.insert(item_position, child)
}

fn get(&self, key: &KeyType) -> Option<&ItemType> {
self.map.get(key)
}
}

pub mod folders_list_item {
use super::*;

// Implementation sub-module of the GObject
mod imp {
use super::*;

// The actual data structure that stores our values. This is not accessible
// directly from the outside.
pub struct FoldersListItem {
pub folder: Rc<RefCell<Option<models::Folder>>>,
}

// Basic declaration of our type for the GObject type system
#[glib::object_subclass]
impl ObjectSubclass for FoldersListItem {
const NAME: &'static str = "FoldersListItem";
type Type = super::FoldersListItem;
type ParentType = gtk::Box;
// Called once at the very beginning of instantiation of each instance and
// creates the data structure that contains all our state
fn new() -> Self {
Self {
folder: Default::default(),
}
}
}
impl ObjectImpl for FoldersListItem {}
impl BoxImpl for FoldersListItem {}
impl WidgetImpl for FoldersListItem {}
}

// The public part
glib::wrapper! {
pub struct FoldersListItem(ObjectSubclass<imp::FoldersListItem>) @extends gtk::Widget, gtk::Box;
}
impl FoldersListItem {
pub fn new() -> FoldersListItem {
glib::Object::new::<FoldersListItem>()
}

pub fn new_with_folder(folder: &models::Folder) -> FoldersListItem {
let instance = Self::new();

let self_ = imp::FoldersListItem::from_obj(&instance);
//@TODO can we get rid of this clone?
self_.folder.replace(Some(folder.clone()));

instance
}

pub fn get_folder(&self) -> Rc<RefCell<Option<models::Folder>>> {
let self_ = imp::FoldersListItem::from_obj(self);
self_.folder.clone()
}
}
}

pub mod folder_conversation_item;

pub mod message_view {
use super::*;

// Implementation sub-module of the GObject
mod imp {
use crate::litehtml_callbacks;

use super::*;

// The actual data structure that stores our values. This is not accessible
// directly from the outside.
pub struct MessageView {
pub litehtml_callbacks: Rc<RefCell<litehtml_callbacks::Callbacks>>,
pub litehtml_context: Rc<RefCell<*mut core::ffi::c_void>>,
pub loaded: Rc<RefCell<bool>>,
}

// Basic declaration of our type for the GObject type system
#[glib::object_subclass]
impl ObjectSubclass for MessageView {
const NAME: &'static str = "MessageView";
type Type = super::MessageView;
type ParentType = gtk::Widget;
// Called once at the very beginning of instantiation of each instance and
// creates the data structure that contains all our state
fn new() -> Self {
Self {
litehtml_callbacks: Rc::new(RefCell::new(litehtml_callbacks::Callbacks::new())),
litehtml_context: Rc::new(RefCell::new(0 as *mut core::ffi::c_void)),
loaded: Rc::new(RefCell::new(false)),
}
}
}

impl ObjectImpl for MessageView {
fn constructed(&self) {
self.parent_constructed();

let obj = self.obj();
obj.set_vexpand(true);
}
}

impl WidgetImpl for MessageView {
fn snapshot(&self, snapshot: &gtk::Snapshot) {
if !*self.loaded.borrow() {
return;
}

let litehtml_context = self.litehtml_context.borrow_mut();

let mut callbacks = self.litehtml_callbacks.borrow_mut();
callbacks.clear_nodes();

unsafe {
bindings::setup::draw(*litehtml_context);
}

let nodes = callbacks.nodes();

let container_node = gsk::ContainerNode::new(&nodes);

snapshot.append_node(&container_node);
}

fn request_mode(&self) -> gtk::SizeRequestMode {
gtk::SizeRequestMode::HeightForWidth
}

fn measure(&self, orientation: gtk::Orientation, for_size: i32) -> (i32, i32, i32, i32) {
let litehtml_context = self.litehtml_context.borrow_mut();

match orientation {
gtk::Orientation::Horizontal => (0, 0, -1, -1),
gtk::Orientation::Vertical => {
let height = unsafe { bindings::setup::render(*litehtml_context, for_size * pango::SCALE) } / pango::SCALE;

(height, height, -1, -1)
}
_ => unimplemented!(),
}
}

fn size_allocate(&self, width: i32, height: i32, baseline: i32) {
let litehtml_context = self.litehtml_context.borrow_mut();

unsafe { bindings::setup::render(*litehtml_context, width * pango::SCALE) };

let obj = self.obj();

obj.queue_draw();
}
}
}

// The public part
glib::wrapper! {
pub struct MessageView(ObjectSubclass<imp::MessageView>) @extends gtk::Widget, @implements gtk::Buildable, gtk::Actionable;
}
impl MessageView {
pub fn new() -> MessageView {
glib::Object::new::<MessageView>()
}

pub fn load_content(&self, content: &String) {
let self_ = imp::MessageView::from_obj(self);

let master_stylesheet = include_str!("../ui/webview_stylesheet.css");
let master_stylesheet = CString::new(master_stylesheet).expect("Could not build master stylesheet CString");

let s = CString::new(&**content).expect("CString::new failed");

let mut litehtml_context = self_.litehtml_context.borrow_mut();

unsafe {
*litehtml_context = bindings::setup::setup_litehtml(
master_stylesheet.as_ptr(),
s.as_ptr(),
RefCell::as_ptr(&self_.litehtml_callbacks) as *mut core::ffi::c_void,
);
}

*self_.loaded.borrow_mut() = true;

self.queue_resize();
}
}
}

pub mod conversation_message_item {
use super::*;

// Implementation sub-module of the GObject
mod imp {
use super::*;

// The actual data structure that stores our values. This is not accessible
// directly from the outside.
pub struct ConversationMessageItem {
pub message: Rc<RefCell<Option<models::Message>>>,
}

// Basic declaration of our type for the GObject type system
#[glib::object_subclass]
impl ObjectSubclass for ConversationMessageItem {
const NAME: &'static str = "ConversationMessageItem";
type Type = super::ConversationMessageItem;
type ParentType = gtk::ListBoxRow;
// Called once at the very beginning of instantiation of each instance and
// creates the data structure that contains all our state
fn new() -> Self {
Self {
message: Default::default(),
}
}
}
impl ObjectImpl for ConversationMessageItem {}
impl ListBoxRowImpl for ConversationMessageItem {}
impl WidgetImpl for ConversationMessageItem {}
}

// The public part
glib::wrapper! {
pub struct ConversationMessageItem(ObjectSubclass<imp::ConversationMessageItem>) @extends gtk::ListBoxRow, gtk::Widget, @implements gtk::Buildable, gtk::Actionable;
}
impl ConversationMessageItem {
pub fn new() -> ConversationMessageItem {
glib::Object::new::<ConversationMessageItem>()
}

pub fn new_with_message(message: &models::Message) -> ConversationMessageItem {
let instance = Self::new();

let self_ = imp::ConversationMessageItem::from_obj(&instance);
//@TODO can we get rid of this clone?
self_.message.replace(Some(message.clone()));

instance
}

pub fn get_message(&self) -> Rc<RefCell<Option<models::Message>>> {
let self_ = imp::ConversationMessageItem::from_obj(self);
self_.message.clone()
}
}
}
use self::folders_list_item::FoldersListItem;
use self::message_view::MessageView;

mod imp {
use gtk::{
Expand Down Expand Up @@ -558,7 +295,7 @@ mod imp {
let attachments_list = gtk::Grid::new();
attachments_list.set_orientation(gtk::Orientation::Vertical);

let view = message_view::MessageView::new();
let view = MessageView::new();

let grid = gtk::Grid::new();
grid.set_orientation(gtk::Orientation::Vertical);
Expand Down Expand Up @@ -748,47 +485,4 @@ impl Window {
// public new void grab_focus () {
// listbox.grab_focus ();
// }

// let (roots, threads, envelopes) =
// self.identities.lock().expect("Unable to acquire identities lock")[0]
// .clone()
// .fetch_threads();

// let iter = roots.into_iter();
// for thread in iter {
// let thread_node =
// &threads.thread_nodes()[&threads.thread_ref(thread).root()];
// let root_envelope_hash = if let Some(h) =
// thread_node.message().or_else(|| { if
// thread_node.children().is_empty() { return None;
// }
// let mut iter_ptr = thread_node.children()[0];
// while threads.thread_nodes()[&iter_ptr].message().is_none() {
// if
// threads.thread_nodes()[&iter_ptr].children().is_empty() {
// return None;
// }
// iter_ptr =
// threads.thread_nodes()[&iter_ptr].children()[0]; }
// threads.thread_nodes()[&iter_ptr].message()
// }) {
// h
// } else {
// continue;
// };

// let row_data = FolderConversationRowData::new(&"Subject
// placeholder"); unsafe {
// (*row_data.as_ptr()).get_impl().subject.replace(Some(
//
// threads.thread_nodes()[&threads.thread_ref(thread).root()]
// .message()
// .as_ref()
// .map(|m|
// envelopes.read().unwrap()[m].subject().to_string())
// .unwrap_or_else(|| "None".to_string()),
// ));
// }

// self.threads_model.append(&row_data)
}
Loading