1use std::ops::Deref;
2use std::rc::Rc;
3use std::cell::RefCell;
4
5use gtk;
6use gtk::prelude::*;
7
8use glib;
9use glib::signal;
10
11use pango;
12
13use neovim_lib::{NeovimApi, NeovimApiAsync};
14use neovim_lib::neovim_api::Tabpage;
15
16use crate::nvim;
17use crate::nvim::ErrorReport;
18
19struct State {
20 data: Vec<Tabpage>,
21 selected: Option<Tabpage>,
22 nvim: Option<Rc<nvim::NeovimClient>>,
23}
24
25impl State {
26 pub fn new() -> Self {
27 State {
28 data: Vec::new(),
29 selected: None,
30 nvim: None,
31 }
32 }
33
34 fn switch_page(&self, idx: u32) {
35 let target = &self.data[idx as usize];
36 if Some(target) != self.selected.as_ref() {
37 if let Some(mut nvim) = self.nvim.as_ref().unwrap().nvim() {
38 nvim.set_current_tabpage(target).report_err();
39 }
40 }
41 }
42
43 fn close_tab(&self, idx: u32) {
44 if let Some(mut nvim) = self.nvim.as_ref().unwrap().nvim() {
45 nvim.command_async(&format!(":tabc {}", idx + 1))
46 .cb(|r| r.report_err())
47 .call();
48 }
49 }
50}
51
52pub struct Tabline {
53 tabs: gtk::Notebook,
54 state: Rc<RefCell<State>>,
55 switch_handler_id: glib::SignalHandlerId,
56}
57
58impl Tabline {
59 pub fn new() -> Self {
60 let tabs = gtk::Notebook::new();
61
62 tabs.set_can_focus(false);
63 tabs.set_scrollable(true);
64 tabs.set_show_border(false);
65 tabs.set_border_width(0);
66 tabs.set_hexpand(true);
67 tabs.hide();
68
69 let state = Rc::new(RefCell::new(State::new()));
70
71 let state_ref = state.clone();
72 let switch_handler_id =
73 tabs.connect_switch_page(move |_, _, idx| state_ref.borrow().switch_page(idx));
74
75 Tabline {
76 tabs,
77 state,
78 switch_handler_id,
79 }
80 }
81
82 fn update_state(
83 &self,
84 nvim: &Rc<nvim::NeovimClient>,
85 selected: &Tabpage,
86 tabs: &[(Tabpage, Option<String>)],
87 ) {
88 let mut state = self.state.borrow_mut();
89
90 if state.nvim.is_none() {
91 state.nvim = Some(nvim.clone());
92 }
93
94 state.selected = Some(selected.clone());
95
96 state.data = tabs.iter().map(|item| item.0.clone()).collect();
97 }
98
99 pub fn update_tabs(
100 &self,
101 nvim: &Rc<nvim::NeovimClient>,
102 selected: &Tabpage,
103 tabs: &[(Tabpage, Option<String>)],
104 ) {
105 if tabs.len() <= 1 {
106 self.tabs.hide();
107 return;
108 } else {
109 self.tabs.show();
110 }
111
112 self.update_state(nvim, selected, tabs);
113
114
115 signal::signal_handler_block(&self.tabs, &self.switch_handler_id);
116
117 let count = self.tabs.get_n_pages() as usize;
118 if count < tabs.len() {
119 for _ in count..tabs.len() {
120 let empty = gtk::Box::new(gtk::Orientation::Vertical, 0);
121 empty.show_all();
122 let title = gtk::Label::new(None);
123 title.set_ellipsize(pango::EllipsizeMode::Middle);
124 title.set_width_chars(25);
125 let close_btn = gtk::Button::new_from_icon_name(
126 Some("window-close-symbolic"),
127 gtk::IconSize::Menu,
128 );
129 close_btn.set_relief(gtk::ReliefStyle::None);
130 close_btn.get_style_context().add_class("small-button");
131 close_btn.set_focus_on_click(false);
132 let label_box = gtk::Box::new(gtk::Orientation::Horizontal, 0);
133 label_box.pack_start(&title, true, false, 0);
134 label_box.pack_start(&close_btn, false, false, 0);
135 title.show();
136 close_btn.show();
137 self.tabs.append_page(&empty, Some(&label_box));
138 self.tabs.set_child_tab_expand(&empty, true);
139
140 let tabs = self.tabs.clone();
141 let state_ref = Rc::clone(&self.state);
142 close_btn.connect_clicked(move |btn| {
143 let current_label = btn
144 .get_parent().unwrap();
145 for i in 0..tabs.get_n_pages() {
146 let page = tabs.get_nth_page(Some(i)).unwrap();
147 let label = tabs.get_tab_label(&page).unwrap();
148 if label == current_label {
149 state_ref.borrow().close_tab(i);
150 }
151 }
152 });
153 }
154 } else if count > tabs.len() {
155 for _ in tabs.len()..count {
156 self.tabs.remove_page(None);
157 }
158 }
159
160 for (idx, tab) in tabs.iter().enumerate() {
161 let tab_child = self.tabs.get_nth_page(Some(idx as u32));
162 let tab_label = self.tabs
163 .get_tab_label(&tab_child.unwrap())
164 .unwrap()
165 .downcast::<gtk::Box>()
166 .unwrap()
167 .get_children()
168 .into_iter()
169 .next()
170 .unwrap()
171 .downcast::<gtk::Label>()
172 .unwrap();
173 tab_label.set_text(tab.1.as_ref().unwrap_or(&"??".to_owned()));
174
175 if *selected == tab.0 {
176 self.tabs.set_current_page(Some(idx as u32));
177 }
178 }
179
180 signal::signal_handler_unblock(&self.tabs, &self.switch_handler_id);
181 }
182}
183
184impl Deref for Tabline {
185 type Target = gtk::Notebook;
186
187 fn deref(&self) -> >k::Notebook {
188 &self.tabs
189 }
190}