Skip to content

Commit 856ee6e

Browse files
committed
Add log output type setting: plain TextCtrl or colored StyledTextCtrl
1 parent 8bff6bc commit 856ee6e

4 files changed

Lines changed: 136 additions & 32 deletions

File tree

src/logview.rs

Lines changed: 105 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,86 @@ impl LogEntry {
2727
}
2828
}
2929

30-
#[allow(dead_code)]
30+
#[derive(Clone, Copy)]
31+
pub enum LogTextCtrl {
32+
Styled(StyledTextCtrl),
33+
Plain(TextCtrl),
34+
}
35+
36+
impl LogTextCtrl {
37+
pub fn append_text(&self, text: &str) {
38+
match self {
39+
LogTextCtrl::Styled(ctrl) => ctrl.append_text(text),
40+
LogTextCtrl::Plain(ctrl) => ctrl.append_text(text),
41+
}
42+
}
43+
44+
pub fn clear_all(&self) {
45+
match self {
46+
LogTextCtrl::Styled(ctrl) => ctrl.clear_all(),
47+
LogTextCtrl::Plain(ctrl) => ctrl.clear(),
48+
}
49+
}
50+
51+
pub fn get_end_position(&self) -> i64 {
52+
match self {
53+
LogTextCtrl::Styled(ctrl) => ctrl.get_length() as i64,
54+
LogTextCtrl::Plain(ctrl) => ctrl.get_last_position(),
55+
}
56+
}
57+
58+
#[allow(dead_code)]
59+
pub fn set_selection_mode_typed(&self, mode: SelectionMode) {
60+
if let LogTextCtrl::Styled(ctrl) = self {
61+
ctrl.set_selection_mode_typed(mode);
62+
}
63+
}
64+
65+
pub fn start_styling(&self, start: i32) {
66+
if let LogTextCtrl::Styled(ctrl) = self {
67+
ctrl.start_styling(start);
68+
}
69+
}
70+
71+
pub fn set_styling(&self, length: i32, style: i32) {
72+
if let LogTextCtrl::Styled(ctrl) = self {
73+
ctrl.set_styling(length, style);
74+
}
75+
}
76+
77+
pub fn goto_end(&self) {
78+
match self {
79+
LogTextCtrl::Styled(ctrl) => {
80+
let end = ctrl.get_length();
81+
ctrl.goto_pos(end);
82+
ctrl.ensure_caret_visible();
83+
}
84+
LogTextCtrl::Plain(ctrl) => {
85+
ctrl.set_insertion_point_end();
86+
}
87+
}
88+
}
89+
90+
pub fn scroll_to_end(&self) {
91+
match self {
92+
LogTextCtrl::Styled(ctrl) => ctrl.scroll_to_end(),
93+
LogTextCtrl::Plain(ctrl) => ctrl.scroll_to_end(),
94+
}
95+
}
96+
}
97+
3198
pub struct LogViewPanel {
3299
pub panel: Panel,
33-
pub text_ctrl: StyledTextCtrl,
100+
pub text_ctrl: LogTextCtrl,
101+
}
102+
103+
impl LogTextCtrl {
104+
pub fn add_to_sizer(&self, sizer: &BoxSizer, proportion: i32, flag: SizerFlag, border: i32) {
105+
match self {
106+
LogTextCtrl::Styled(ctrl) => sizer.add(ctrl, proportion, flag, border),
107+
LogTextCtrl::Plain(ctrl) => sizer.add(ctrl, proportion, flag, border),
108+
};
109+
}
34110
}
35111

36112
impl LogViewPanel {
@@ -62,39 +138,50 @@ impl LogViewPanel {
62138
ctrl.style_set_size(STYLE_TRACE, 10);
63139
}
64140

65-
pub fn new(parent: &Panel) -> Self {
141+
pub fn new(parent: &Panel, use_color: bool) -> Self {
66142
let panel = Panel::builder(parent).build();
67143
let sizer = BoxSizer::builder(Orientation::Vertical).build();
68-
let text_ctrl = StyledTextCtrl::builder(&panel).with_size(Size::new(-1, 200)).build();
69-
text_ctrl.set_min_size(Size::new(-1, 200));
70-
text_ctrl.set_selection_mode_typed(SelectionMode::Stream);
71-
Self::init_log_styles(&text_ctrl);
72-
sizer.add(&text_ctrl, 1, SizerFlag::Expand | SizerFlag::All, crate::settings::WIDGET_MARGIN);
144+
let text_ctrl = if use_color {
145+
let ctrl = StyledTextCtrl::builder(&panel).with_size(Size::new(-1, 200)).build();
146+
ctrl.set_min_size(Size::new(-1, 200));
147+
ctrl.set_selection_mode_typed(SelectionMode::Stream);
148+
Self::init_log_styles(&ctrl);
149+
LogTextCtrl::Styled(ctrl)
150+
} else {
151+
let ctrl = TextCtrl::builder(&panel)
152+
.with_size(Size::new(-1, 200))
153+
.with_style(TextCtrlStyle::MultiLine | TextCtrlStyle::ReadOnly)
154+
.build();
155+
ctrl.set_min_size(Size::new(-1, 200));
156+
LogTextCtrl::Plain(ctrl)
157+
};
158+
159+
text_ctrl.add_to_sizer(&sizer, 1, SizerFlag::Expand | SizerFlag::All, crate::settings::WIDGET_MARGIN);
73160
panel.set_sizer(sizer, true);
74161
Self { panel, text_ctrl }
75162
}
76163
}
77164

78-
// UI-thread local storage for the log StyledTextCtrl. This avoids Send/Sync issues
165+
// UI-thread local storage for the log control. This avoids Send/Sync issues
79166
// by ensuring the control is only ever accessed on the UI thread.
80167
thread_local! {
81-
pub static LOG_TEXT_CTRL: RefCell<Option<StyledTextCtrl>> = const { RefCell::new(None) };
168+
pub static LOG_TEXT_CTRL: RefCell<Option<LogTextCtrl>> = const { RefCell::new(None) };
82169
// A UI-thread log ring buffer; we render from here instead of reading back from the control
83170
pub static LOG_RING: RefCell<VecDeque<LogEntry>> = const { RefCell::new(VecDeque::new()) };
84171
}
85172

86-
fn append_text_entry(ctrl: &StyledTextCtrl, entry: &LogEntry) {
87-
let start = ctrl.get_length();
173+
fn append_text_entry(ctrl: &LogTextCtrl, entry: &LogEntry) {
174+
let start = ctrl.get_end_position();
88175
ctrl.append_text(&entry.text);
89-
let end = ctrl.get_length();
90-
let length = end.saturating_sub(start);
176+
let end = ctrl.get_end_position();
177+
let length = end.saturating_sub(start) as i32;
91178
if length > 0 {
92-
ctrl.start_styling(start);
179+
ctrl.start_styling(start as i32);
93180
ctrl.set_styling(length, entry.style());
94181
}
95182
}
96183

97-
fn rebuild_log_text(ctrl: &StyledTextCtrl, ring: &VecDeque<LogEntry>) {
184+
fn rebuild_log_text(ctrl: &LogTextCtrl, ring: &VecDeque<LogEntry>) {
98185
ctrl.clear_all();
99186
for entry in ring.iter() {
100187
append_text_entry(ctrl, entry);
@@ -131,12 +218,9 @@ pub fn ui_append_logs(lines: Vec<(log::Level, String)>, max_lines: usize, auto_s
131218
}
132219
}
133220

134-
let end = ctrl.get_length();
135-
ctrl.goto_pos(end);
136-
ctrl.ensure_caret_visible();
221+
ctrl.goto_end();
137222
if auto_scroll {
138-
let last_line = ctrl.get_line_count().saturating_sub(1);
139-
ctrl.scroll_to_line(last_line);
223+
ctrl.scroll_to_end();
140224
}
141225
}
142226
});

src/main.rs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -160,11 +160,7 @@ fn main() -> std::io::Result<()> {
160160
// a channel. the UI thread will poll the receiver via the shared UI
161161
// timer and perform the actual raise operation, which avoids moving the
162162
// `Frame` across thread boundaries.
163-
let activation_rx = if let Some(listener) = activation_listener {
164-
Some(crate::single_instance::spawn_activation_listener(listener))
165-
} else {
166-
None
167-
};
163+
let activation_rx = activation_listener.map(crate::single_instance::spawn_activation_listener);
168164

169165
let icon_bitmap = create_bitmap_from_memory(MAIN_ICON, Some((48, 48))).unwrap();
170166
frame.set_icon(&icon_bitmap);
@@ -354,10 +350,10 @@ fn main() -> std::io::Result<()> {
354350
if shutting_down_for_status.load(Ordering::Acquire) {
355351
return;
356352
}
357-
if let Some(act_rx) = &activation_rx {
358-
if act_rx.try_recv().is_ok() {
359-
restore_main_window(&frame_for_refresh_ui);
360-
}
353+
if let Some(act_rx) = &activation_rx
354+
&& act_rx.try_recv().is_ok()
355+
{
356+
restore_main_window(&frame_for_refresh_ui);
361357
}
362358

363359
if core::is_overtls_running()
@@ -642,7 +638,13 @@ fn main() -> std::io::Result<()> {
642638
notebook.add_page(&subscriptions_panel, "Subscriptions", false, None);
643639

644640
// Integrate LogView module (bottom pane)
645-
let logview_panel = logview::LogViewPanel::new(&main_panel);
641+
let log_color_output = cfg_clone
642+
.lock()
643+
.ok()
644+
.and_then(|c| c.logging.clone())
645+
.and_then(|ls| ls.log_color_output)
646+
.unwrap_or_default();
647+
let logview_panel = logview::LogViewPanel::new(&main_panel, log_color_output);
646648
// Register the TextCtrl in UI-thread-local storage for callbacks
647649
logview::LOG_TEXT_CTRL.with(|cell| {
648650
*cell.borrow_mut() = Some(logview_panel.text_ctrl);

src/settings.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,9 @@ pub struct LoggingSettings {
215215

216216
#[serde(skip_serializing_if = "Option::is_none")]
217217
pub log_auto_scroll: Option<bool>, // log auto scroll
218+
219+
#[serde(skip_serializing_if = "Option::is_none")]
220+
pub log_color_output: Option<bool>, // colored log output
218221
}
219222

220223
impl Default for LoggingSettings {
@@ -228,6 +231,7 @@ impl Default for LoggingSettings {
228231
overtls_log_level: Some("Debug".to_string()),
229232
tun2proxy_log_level: Some("Debug".to_string()),
230233
log_auto_scroll: Some(true),
234+
log_color_output: None,
231235
}
232236
}
233237
}
@@ -401,5 +405,6 @@ impl LoggingSettings {
401405
&& self.overtls_log_level == other.overtls_log_level
402406
&& self.tun2proxy_log_level == other.tun2proxy_log_level
403407
&& self.global_log_level == other.global_log_level
408+
&& self.log_color_output == other.log_color_output
404409
}
405410
}

src/settings_dlg.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -600,7 +600,17 @@ fn create_logging_tab(parent: &dyn WxWidget, cfg: &Arc<Mutex<Config>>) -> (Panel
600600
.with_label("Log Auto Scroll")
601601
.build();
602602

603-
let grid = FlexGridSizer::builder(8, 2).with_vgap(10).with_hgap(16).build();
603+
let color_output_label = StaticText::builder(&panel)
604+
.with_label(" ")
605+
.with_style(StaticTextStyle::AlignRight)
606+
.with_size(label_size)
607+
.build();
608+
let color_output_checkbox = CheckBox::builder(&panel)
609+
.with_value(logging_settings.log_color_output.unwrap_or_default())
610+
.with_label("Colored log output")
611+
.build();
612+
613+
let grid = FlexGridSizer::builder(9, 2).with_vgap(10).with_hgap(16).build();
604614
grid.add(&global_label, 0, SizerFlag::AlignRight | SizerFlag::AlignCenterVertical, 0);
605615
grid.add(&global_choice, 0, SizerFlag::Expand, 0);
606616
grid.add(&rustls_label, 0, SizerFlag::AlignRight | SizerFlag::AlignCenterVertical, 0);
@@ -617,6 +627,8 @@ fn create_logging_tab(parent: &dyn WxWidget, cfg: &Arc<Mutex<Config>>) -> (Panel
617627
grid.add(&tun2proxy_choice, 0, SizerFlag::Expand, 0);
618628
grid.add(&auto_scroll_label, 0, SizerFlag::AlignRight | SizerFlag::AlignCenterVertical, 0);
619629
grid.add(&auto_scroll_checkbox, 0, SizerFlag::AlignLeft | SizerFlag::AlignCenterVertical, 0);
630+
grid.add(&color_output_label, 0, SizerFlag::AlignRight | SizerFlag::AlignCenterVertical, 0);
631+
grid.add(&color_output_checkbox, 0, SizerFlag::AlignLeft | SizerFlag::AlignCenterVertical, 0);
620632

621633
let sizer = BoxSizer::builder(Orientation::Vertical).build();
622634
sizer.add_sizer(&grid, 0, SizerFlag::Expand | SizerFlag::All, 16);
@@ -634,6 +646,7 @@ fn create_logging_tab(parent: &dyn WxWidget, cfg: &Arc<Mutex<Config>>) -> (Panel
634646
overtls_log_level: overtls_choice.get_selection().and_then(|i| log_levels.get(i as usize).cloned()),
635647
tun2proxy_log_level: tun2proxy_choice.get_selection().and_then(|i| log_levels.get(i as usize).cloned()),
636648
log_auto_scroll: if auto_scroll_checkbox.get_value() { Some(true) } else { None },
649+
log_color_output: if color_output_checkbox.get_value() { Some(true) } else { None },
637650
}
638651
};
639652

0 commit comments

Comments
 (0)