1use std::sync::Weak;
2use std::sync::atomic::AtomicBool;
3
4use fuzzy::{StringMatch, StringMatchCandidate};
5use gpui::{AppContext, ClickEvent, DismissEvent, Entity, EventEmitter, Focusable, WeakEntity};
6use language::Buffer;
7use picker::{Picker, PickerDelegate};
8use ui::{
9 Button, ButtonCommon, Context, Label, LabelSize, ListItem, Render, Styled, Tooltip, Window,
10 div, rems, v_flex,
11};
12use ui::{Clickable, ParentElement};
13use util::ResultExt;
14use workspace::{ItemHandle, ModalView, StatusItemView, Workspace};
15
16pub enum Encoding {
17 Utf8(WeakEntity<Workspace>),
18}
19
20impl Encoding {
21 pub fn as_str(&self) -> &str {
22 match &self {
23 Encoding::Utf8(_) => "UTF-8",
24 }
25 }
26}
27
28impl EncodingSaveOrReopenSelector {
29 pub fn new(window: &mut Window, cx: &mut Context<EncodingSaveOrReopenSelector>) -> Self {
30 let delegate = EncodingSaveOrReopenDelegate::new(cx.entity().downgrade());
31
32 let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx));
33
34 Self { picker }
35 }
36
37 pub fn toggle(workspace: &mut Workspace, window: &mut Window, cx: &mut Context<Workspace>) {
38 workspace.toggle_modal(window, cx, |window, cx| {
39 EncodingSaveOrReopenSelector::new(window, cx)
40 });
41 }
42}
43
44pub struct EncodingSaveOrReopenSelector {
45 picker: Entity<Picker<EncodingSaveOrReopenDelegate>>,
46}
47
48impl Focusable for EncodingSaveOrReopenSelector {
49 fn focus_handle(&self, cx: &ui::App) -> gpui::FocusHandle {
50 self.picker.focus_handle(cx)
51 }
52}
53
54impl Render for EncodingSaveOrReopenSelector {
55 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl ui::IntoElement {
56 v_flex().w(rems(34.0)).child(self.picker.clone())
57 }
58}
59
60impl ModalView for EncodingSaveOrReopenSelector {}
61
62impl EventEmitter<DismissEvent> for EncodingSaveOrReopenSelector {}
63
64pub struct EncodingSaveOrReopenDelegate {
65 encoding_selector: WeakEntity<EncodingSaveOrReopenSelector>,
66 current_selection: usize,
67 matches: Vec<StringMatch>,
68 pub actions: Vec<StringMatchCandidate>,
69}
70
71impl EncodingSaveOrReopenDelegate {
72 pub fn new(selector: WeakEntity<EncodingSaveOrReopenSelector>) -> Self {
73 Self {
74 encoding_selector: selector,
75 current_selection: 0,
76 matches: Vec::new(),
77 actions: vec![
78 StringMatchCandidate::new(0, "Save with encoding"),
79 StringMatchCandidate::new(1, "Reopen with encoding"),
80 ],
81 }
82 }
83
84 pub fn get_actions(&self) -> (&str, &str) {
85 (&self.actions[0].string, &self.actions[1].string)
86 }
87}
88
89impl PickerDelegate for EncodingSaveOrReopenDelegate {
90 type ListItem = ListItem;
91
92 fn match_count(&self) -> usize {
93 self.matches.len()
94 }
95
96 fn selected_index(&self) -> usize {
97 self.current_selection
98 }
99
100 fn set_selected_index(
101 &mut self,
102 ix: usize,
103 _window: &mut Window,
104 _cx: &mut Context<Picker<Self>>,
105 ) {
106 self.current_selection = ix;
107 }
108
109 fn placeholder_text(&self, _window: &mut Window, _cx: &mut ui::App) -> std::sync::Arc<str> {
110 "Select an action...".into()
111 }
112
113 fn update_matches(
114 &mut self,
115 query: String,
116 window: &mut Window,
117 cx: &mut Context<Picker<Self>>,
118 ) -> gpui::Task<()> {
119 let executor = cx.background_executor().clone();
120 let actions = self.actions.clone();
121
122 cx.spawn_in(window, async move |this, cx| {
123 let matches = if query.is_empty() {
124 actions
125 .into_iter()
126 .enumerate()
127 .map(|(index, value)| StringMatch {
128 candidate_id: index,
129 score: 0.0,
130 positions: vec![],
131 string: value.string,
132 })
133 .collect::<Vec<StringMatch>>()
134 } else {
135 fuzzy::match_strings(
136 &actions,
137 &query,
138 false,
139 false,
140 2,
141 &AtomicBool::new(false),
142 executor,
143 )
144 .await
145 };
146
147 this.update(cx, |picker, cx| {
148 let delegate = &mut picker.delegate;
149 delegate.current_selection = matches.len().saturating_sub(1);
150 delegate.matches = matches;
151 cx.notify();
152 })
153 .log_err();
154 })
155 }
156
157 fn confirm(&mut self, secondary: bool, window: &mut Window, cx: &mut Context<Picker<Self>>) {}
158
159 fn dismissed(&mut self, _window: &mut Window, cx: &mut Context<Picker<Self>>) {
160 self.encoding_selector
161 .update(cx, |_, cx| cx.emit(DismissEvent))
162 .log_err();
163 }
164
165 fn render_match(
166 &self,
167 ix: usize,
168 selected: bool,
169 window: &mut Window,
170 cx: &mut Context<Picker<Self>>,
171 ) -> Option<Self::ListItem> {
172 Some(ListItem::new(ix).child(Label::new(&self.matches[ix].string)))
173 }
174}
175
176fn get_current_encoding() -> &'static str {
177 "UTF-8"
178}
179
180impl Render for Encoding {
181 fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl ui::IntoElement {
182 let encoding_indicator = div();
183
184 encoding_indicator.child(
185 Button::new("encoding", get_current_encoding())
186 .label_size(LabelSize::Small)
187 .tooltip(Tooltip::text("Select Encoding"))
188 .on_click(cx.listener(|encoding, _: &ClickEvent, window, cx| {
189 if let Some(workspace) = match encoding {
190 Encoding::Utf8(workspace) => workspace.upgrade(),
191 } {
192 workspace.update(cx, |workspace, cx| {
193 EncodingSaveOrReopenSelector::toggle(workspace, window, cx)
194 })
195 } else {
196 }
197 })),
198 )
199 }
200}
201
202impl StatusItemView for Encoding {
203 fn set_active_pane_item(
204 &mut self,
205 _active_pane_item: Option<&dyn ItemHandle>,
206 _window: &mut Window,
207 _cx: &mut Context<Self>,
208 ) {
209 }
210}