use super::types::{LinuxHandleKind, LinuxPlatform};
#[cfg(all(target_os = "linux", feature = "gtk-native"))]
use crate::core::MutexExt;
use crate::platform::{DropEvent, WidgetTriggerEvent, WidgetTriggerKind};
#[cfg(all(target_os = "linux", feature = "gtk-native"))]
use gtk::prelude::*;
impl LinuxPlatform {
pub(crate) fn show_widget_impl(&self, widget_id: u64) {
self.state.set_visible(widget_id, true);
#[cfg(all(target_os = "linux", feature = "gtk-native"))]
{
let native = self.native.lock_guard();
if let Some(window) = native.windows.get(&widget_id) {
window.show_all();
return;
}
if let Some(widget) = native.widgets.get(&widget_id) {
widget.show();
}
}
}
pub(crate) fn hide_widget_impl(&self, widget_id: u64) {
self.state.set_visible(widget_id, false);
#[cfg(all(target_os = "linux", feature = "gtk-native"))]
{
let native = self.native.lock_guard();
if let Some(window) = native.windows.get(&widget_id) {
window.hide();
return;
}
if let Some(widget) = native.widgets.get(&widget_id) {
widget.hide();
}
}
}
pub(crate) fn set_widget_geometry_impl(
&self,
widget_id: u64,
x: i32,
y: i32,
width: u32,
height: u32,
) {
self.state.set_geometry(widget_id, x, y, width, height);
#[cfg(all(target_os = "linux", feature = "gtk-native"))]
{
let parent_id = {
let menus = self.menus.lock().expect("linux menu lock poisoned");
menus.widget_parent.get(&widget_id).copied()
};
let native = self.native.lock_guard();
if let Some(window) = native.windows.get(&widget_id) {
window.move_(x, y);
window.resize(width as i32, height as i32);
return;
}
if let Some(widget) = native.widgets.get(&widget_id) {
widget.set_size_request(width as i32, height as i32);
if let Some(parent_id) = parent_id {
if let Some(container) = native.content_fixed.get(&parent_id) {
container.move_(widget, x, y);
}
}
}
}
}
pub(crate) fn set_widget_text_impl(&self, widget_id: u64, text: &str) {
if !self.state.set_text(widget_id, text) {
return;
}
#[cfg(all(target_os = "linux", feature = "gtk-native"))]
{
let native = self.native.lock_guard();
if let Some(window) = native.windows.get(&widget_id) {
window.set_title(text);
} else if let Some(widget) = native.widgets.get(&widget_id) {
if let Ok(menu_item) = widget.clone().downcast::<gtk::MenuItem>() {
menu_item.set_label(text);
} else if let Ok(spin) = widget.clone().downcast::<gtk::SpinButton>() {
if let Ok(value) = text.trim().parse::<f64>() {
spin.set_value(value);
}
} else if let Ok(check) = widget.clone().downcast::<gtk::CheckButton>() {
check.set_label(text);
} else if let Ok(radio) = widget.clone().downcast::<gtk::RadioButton>() {
radio.set_label(text);
} else if let Ok(button) = widget.clone().downcast::<gtk::Button>() {
button.set_label(text);
} else if let Ok(entry) = widget.clone().downcast::<gtk::Entry>() {
entry.set_text(text);
} else if let Ok(label) = widget.clone().downcast::<gtk::Label>() {
label.set_text(text);
} else if let Ok(frame) = widget.clone().downcast::<gtk::Frame>() {
frame.set_label(Some(text));
} else if let Ok(progress) = widget.clone().downcast::<gtk::ProgressBar>() {
if let Ok(value) = text.trim().parse::<f64>() {
progress.set_fraction(value.clamp(0.0, 1.0));
} else {
progress.set_show_text(true);
progress.set_text(Some(text));
}
}
}
}
if matches!(self.kind_of(widget_id), Some(LinuxHandleKind::LineEdit)) {
self.menus
.lock()
.expect("linux menu lock poisoned")
.pending_widget_events
.push_back(WidgetTriggerEvent { widget_id, kind: WidgetTriggerKind::ValueChanged });
}
}
pub(crate) fn get_widget_text_impl(&self, widget_id: u64) -> String {
self.state.text(widget_id)
}
pub(crate) fn set_widget_enabled_impl(&self, widget_id: u64, enabled: bool) {
self.state.set_enabled(widget_id, enabled);
#[cfg(all(target_os = "linux", feature = "gtk-native"))]
{
let native = self.native.lock_guard();
if let Some(widget) = native.widgets.get(&widget_id) {
widget.set_sensitive(enabled);
}
}
}
pub(crate) fn is_widget_enabled_impl(&self, widget_id: u64) -> bool {
#[cfg(all(target_os = "linux", feature = "gtk-native"))]
{
let native = self.native.lock_guard();
if let Some(widget) = native.widgets.get(&widget_id) {
return widget.is_sensitive();
}
}
self.state.enabled(widget_id)
}
pub(crate) fn set_widget_visible_impl(&self, widget_id: u64, visible: bool) {
self.state.set_visible(widget_id, visible);
if visible {
self.show_widget_impl(widget_id);
} else {
self.hide_widget_impl(widget_id);
}
}
pub(crate) fn is_widget_visible_impl(&self, widget_id: u64) -> bool {
self.state.visible(widget_id)
}
pub(crate) fn kind_accepts_numeric_value(kind: LinuxHandleKind) -> bool {
matches!(
kind,
LinuxHandleKind::Slider
| LinuxHandleKind::ProgressBar
| LinuxHandleKind::SpinBox
| LinuxHandleKind::DoubleSpinBox
| LinuxHandleKind::ScrollBar
| LinuxHandleKind::ActivityIndicator
| LinuxHandleKind::ProgressDialog
)
}
pub(crate) fn set_widget_value_impl(&self, widget_id: u64, value: f64) -> bool {
let Some(kind) = self.kind_of(widget_id) else {
return false;
};
if !Self::kind_accepts_numeric_value(kind) {
return false;
}
self.state.set_value(widget_id, value);
#[cfg(all(target_os = "linux", feature = "gtk-native"))]
{
let native = self.native.lock_guard();
if let Some(widget) = native.widgets.get(&widget_id) {
if let Ok(scale) = widget.clone().downcast::<gtk::Scale>() {
scale.set_value(value);
} else if let Ok(spin) = widget.clone().downcast::<gtk::SpinButton>() {
spin.set_value(value);
} else if let Ok(progress) = widget.clone().downcast::<gtk::ProgressBar>() {
let (min, max) = self.state.range(widget_id).unwrap_or((0.0, 100.0));
let span = (max - min).abs().max(f64::EPSILON);
progress.set_fraction(((value - min) / span).clamp(0.0, 1.0));
} else if let Ok(adjustment) = widget.clone().downcast::<gtk::Scrollbar>() {
adjustment.adjustment().set_value(value);
}
}
}
true
}
pub(crate) fn widget_value_impl(&self, widget_id: u64) -> Option<f64> {
let kind = self.kind_of(widget_id)?;
if !Self::kind_accepts_numeric_value(kind) {
return None;
}
#[cfg(all(target_os = "linux", feature = "gtk-native"))]
{
let native = self.native.lock_guard();
if let Some(widget) = native.widgets.get(&widget_id) {
if let Ok(scale) = widget.clone().downcast::<gtk::Scale>() {
return Some(scale.value());
}
if let Ok(spin) = widget.clone().downcast::<gtk::SpinButton>() {
return Some(spin.value());
}
}
}
self.state.value(widget_id)
}
pub(crate) fn set_widget_range_impl(&self, widget_id: u64, min: f64, max: f64) -> bool {
let Some(kind) = self.kind_of(widget_id) else {
return false;
};
if !matches!(kind, LinuxHandleKind::Slider | LinuxHandleKind::SpinBox) {
return false;
}
self.state.set_range(widget_id, min, max);
#[cfg(all(target_os = "linux", feature = "gtk-native"))]
{
let native = self.native.lock_guard();
if let Some(widget) = native.widgets.get(&widget_id) {
if let Ok(scale) = widget.clone().downcast::<gtk::Scale>() {
let adjustment = scale.adjustment();
adjustment.set_lower(min);
adjustment.set_upper(max);
} else if let Ok(spin) = widget.clone().downcast::<gtk::SpinButton>() {
let adjustment = spin.adjustment();
adjustment.set_lower(min);
adjustment.set_upper(max);
}
}
}
true
}
pub(crate) fn widget_range_impl(&self, widget_id: u64) -> Option<(f64, f64)> {
let kind = self.kind_of(widget_id)?;
if !matches!(kind, LinuxHandleKind::Slider | LinuxHandleKind::SpinBox) {
return None;
}
#[cfg(all(target_os = "linux", feature = "gtk-native"))]
{
let native = self.native.lock_guard();
if let Some(widget) = native.widgets.get(&widget_id) {
if let Ok(scale) = widget.clone().downcast::<gtk::Scale>() {
let adjustment = scale.adjustment();
return Some((adjustment.lower(), adjustment.upper()));
}
if let Ok(spin) = widget.clone().downcast::<gtk::SpinButton>() {
let adjustment = spin.adjustment();
return Some((adjustment.lower(), adjustment.upper()));
}
}
}
self.state.range(widget_id)
}
pub(crate) fn set_widget_selected_index_impl(
&self,
widget_id: u64,
index: Option<usize>,
) -> bool {
let Some(kind) = self.kind_of(widget_id) else {
return false;
};
match (kind, index) {
(LinuxHandleKind::ComboBox, Some(selected)) => {
crate::platform::Platform::combo_box_set_current_index(self, widget_id, selected)
}
(LinuxHandleKind::ListBox, Some(selected)) => {
crate::platform::Platform::list_box_set_current_index(self, widget_id, selected)
}
(LinuxHandleKind::ComboBox | LinuxHandleKind::ListBox, None) => {
false
}
_ => false,
}
}
pub(crate) fn widget_selected_index_impl(&self, widget_id: u64) -> Option<usize> {
match self.kind_of(widget_id)? {
LinuxHandleKind::ComboBox => {
crate::platform::Platform::combo_box_current_index(self, widget_id)
}
LinuxHandleKind::ListBox => {
crate::platform::Platform::list_box_current_index(self, widget_id)
}
_ => None,
}
}
pub(crate) fn set_widget_checked_impl(&self, widget_id: u64, checked: bool) -> bool {
let Some(kind) = self.kind_of(widget_id) else {
return false;
};
if !matches!(
kind,
LinuxHandleKind::CheckBox
| LinuxHandleKind::RadioButton
| LinuxHandleKind::ToggleButton
) {
return false;
}
self.state.set_checked(widget_id, checked);
#[cfg(all(target_os = "linux", feature = "gtk-native"))]
{
let native = self.native.lock_guard();
if let Some(widget) = native.widgets.get(&widget_id) {
if let Ok(check) = widget.clone().downcast::<gtk::ToggleButton>() {
check.set_active(checked);
}
}
}
true
}
pub(crate) fn is_widget_checked_impl(&self, widget_id: u64) -> Option<bool> {
let kind = self.kind_of(widget_id)?;
if !matches!(
kind,
LinuxHandleKind::CheckBox
| LinuxHandleKind::RadioButton
| LinuxHandleKind::ToggleButton
) {
return None;
}
#[cfg(all(target_os = "linux", feature = "gtk-native"))]
{
let native = self.native.lock_guard();
if let Some(widget) = native.widgets.get(&widget_id) {
if let Ok(check) = widget.clone().downcast::<gtk::ToggleButton>() {
return Some(check.is_active());
}
}
}
self.state.checked(widget_id)
}
pub(crate) fn set_widget_step_impl(&self, widget_id: u64, step: f64) -> bool {
let Some(kind) = self.kind_of(widget_id) else {
return false;
};
if !matches!(kind, LinuxHandleKind::Slider | LinuxHandleKind::SpinBox) {
return false;
}
self.state.set_step(widget_id, step);
#[cfg(all(target_os = "linux", feature = "gtk-native"))]
{
let native = self.native.lock_guard();
if let Some(widget) = native.widgets.get(&widget_id) {
if let Ok(scale) = widget.clone().downcast::<gtk::Scale>() {
let adjustment = scale.adjustment();
adjustment.set_step_increment(step);
} else if let Ok(spin) = widget.clone().downcast::<gtk::SpinButton>() {
let adjustment = spin.adjustment();
adjustment.set_step_increment(step);
}
}
}
true
}
pub(crate) fn widget_step_impl(&self, widget_id: u64) -> Option<f64> {
let kind = self.kind_of(widget_id)?;
if !matches!(kind, LinuxHandleKind::Slider | LinuxHandleKind::SpinBox) {
return None;
}
#[cfg(all(target_os = "linux", feature = "gtk-native"))]
{
let native = self.native.lock_guard();
if let Some(widget) = native.widgets.get(&widget_id) {
if let Ok(scale) = widget.clone().downcast::<gtk::Scale>() {
return Some(scale.adjustment().step_increment());
}
if let Ok(spin) = widget.clone().downcast::<gtk::SpinButton>() {
return Some(spin.adjustment().step_increment());
}
}
}
self.state.step(widget_id)
}
pub(crate) fn set_widget_indeterminate_impl(
&self,
widget_id: u64,
indeterminate: bool,
) -> bool {
let Some(kind) = self.kind_of(widget_id) else {
return false;
};
if !matches!(kind, LinuxHandleKind::ProgressBar | LinuxHandleKind::ActivityIndicator) {
return false;
}
self.state.set_indeterminate(widget_id, indeterminate);
#[cfg(all(target_os = "linux", feature = "gtk-native"))]
{
let native = self.native.lock_guard();
if let Some(widget) = native.widgets.get(&widget_id) {
if let Ok(progress) = widget.clone().downcast::<gtk::ProgressBar>() {
if indeterminate {
progress.pulse();
} else {
progress.set_fraction(0.0);
}
}
}
}
true
}
pub(crate) fn is_widget_indeterminate_impl(&self, widget_id: u64) -> Option<bool> {
let kind = self.kind_of(widget_id)?;
if !matches!(kind, LinuxHandleKind::ProgressBar | LinuxHandleKind::ActivityIndicator) {
return None;
}
self.state.indeterminate(widget_id)
}
pub(crate) fn set_widget_read_only_impl(&self, widget_id: u64, read_only: bool) -> bool {
let Some(kind) = self.kind_of(widget_id) else {
return false;
};
if !matches!(kind, LinuxHandleKind::LineEdit) {
return false;
}
self.state.set_read_only(widget_id, read_only);
#[cfg(all(target_os = "linux", feature = "gtk-native"))]
{
let native = self.native.lock_guard();
if let Some(widget) = native.widgets.get(&widget_id) {
if let Ok(entry) = widget.clone().downcast::<gtk::Entry>() {
entry.set_editable(!read_only);
}
}
}
true
}
pub(crate) fn is_widget_read_only_impl(&self, widget_id: u64) -> Option<bool> {
let kind = self.kind_of(widget_id)?;
if !matches!(kind, LinuxHandleKind::LineEdit) {
return None;
}
self.state.read_only(widget_id)
}
pub(crate) fn set_widget_max_length_impl(&self, widget_id: u64, max_length: u32) -> bool {
let Some(kind) = self.kind_of(widget_id) else {
return false;
};
if !matches!(kind, LinuxHandleKind::LineEdit) {
return false;
}
self.state.set_max_length(widget_id, max_length);
#[cfg(all(target_os = "linux", feature = "gtk-native"))]
{
let native = self.native.lock_guard();
if let Some(widget) = native.widgets.get(&widget_id) {
if let Ok(entry) = widget.clone().downcast::<gtk::Entry>() {
let limit = if max_length >= i32::MAX as u32 { -1 } else { max_length as i32 };
entry.set_max_length(limit);
}
}
}
true
}
pub(crate) fn widget_max_length_impl(&self, widget_id: u64) -> Option<u32> {
let kind = self.kind_of(widget_id)?;
if !matches!(kind, LinuxHandleKind::LineEdit) {
return None;
}
#[cfg(all(target_os = "linux", feature = "gtk-native"))]
{
let native = self.native.lock_guard();
if let Some(widget) = native.widgets.get(&widget_id) {
if let Ok(entry) = widget.clone().downcast::<gtk::Entry>() {
let limit = entry.max_length();
return Some(if limit < 0 { u32::MAX } else { limit as u32 });
}
}
if native.windows.contains_key(&widget_id) {
return self.state.max_length(widget_id);
}
}
self.state.max_length(widget_id)
}
pub(crate) fn set_window_state_impl(
&self,
widget_id: u64,
flag: crate::platform::WindowStateFlag,
on: bool,
) -> bool {
use crate::platform::WindowStateFlag;
let Some(kind) = self.kind_of(widget_id) else {
return false;
};
if !matches!(kind, LinuxHandleKind::Window) {
return false;
}
self.state.set_window_state(widget_id, flag, on);
#[cfg(all(target_os = "linux", feature = "gtk-native"))]
{
let native = self.native.lock_guard();
if let Some(window) = native.windows.get(&widget_id) {
match flag {
WindowStateFlag::Maximized => {
if on {
window.maximize();
} else {
window.unmaximize();
}
}
WindowStateFlag::Minimized => {
if on {
window.iconify();
} else {
window.deiconify();
}
}
WindowStateFlag::Fullscreen => {
if on {
window.fullscreen();
} else {
window.unfullscreen();
}
}
WindowStateFlag::Resizable => window.set_resizable(on),
WindowStateFlag::Decorated => window.set_decorated(on),
}
}
}
true
}
pub(crate) fn is_window_in_state_impl(
&self,
widget_id: u64,
flag: crate::platform::WindowStateFlag,
) -> Option<bool> {
use crate::platform::WindowStateFlag;
let kind = self.kind_of(widget_id)?;
if !matches!(kind, LinuxHandleKind::Window) {
return None;
}
#[cfg(all(target_os = "linux", feature = "gtk-native"))]
{
let native = self.native.lock_guard();
if let Some(window) = native.windows.get(&widget_id) {
let value = match flag {
WindowStateFlag::Maximized => window.is_maximized(),
WindowStateFlag::Minimized => return self.state.window_state(widget_id, flag),
WindowStateFlag::Fullscreen => return self.state.window_state(widget_id, flag),
WindowStateFlag::Resizable => window.is_resizable(),
WindowStateFlag::Decorated => window.is_decorated(),
};
return Some(value);
}
}
self.state.window_state(widget_id, flag)
}
pub(crate) fn set_window_min_size_impl(&self, widget_id: u64, width: u32, height: u32) -> bool {
let Some(kind) = self.kind_of(widget_id) else {
return false;
};
if !matches!(kind, LinuxHandleKind::Window) {
return false;
}
self.state.set_window_min_size(widget_id, width, height);
#[cfg(all(target_os = "linux", feature = "gtk-native"))]
{
use gdk::WindowHints;
let native = self.native.lock_guard();
if let Some(window) = native.windows.get(&widget_id) {
let hints = gdk::Geometry::new(
width as i32,
height as i32,
0,
0,
0,
0,
0,
0,
0.0,
0.0,
gdk::Gravity::NorthWest,
);
window.set_geometry_hints(
None::<>k::Widget>,
Some(&hints),
WindowHints::MIN_SIZE,
);
}
}
true
}
pub(crate) fn window_min_size_impl(&self, widget_id: u64) -> Option<(u32, u32)> {
let kind = self.kind_of(widget_id)?;
if !matches!(kind, LinuxHandleKind::Window) {
return None;
}
self.state.window_min_size(widget_id)
}
pub(crate) fn set_window_icon_impl(&self, widget_id: u64, path: &str) -> bool {
let Some(kind) = self.kind_of(widget_id) else {
return false;
};
if !matches!(kind, LinuxHandleKind::Window) {
return false;
}
#[cfg(all(target_os = "linux", feature = "gtk-native"))]
{
let native = self.native.lock_guard();
if let Some(window) = native.windows.get(&widget_id) {
if let Err(error) = window.set_icon_from_file(std::path::Path::new(path)) {
log::warn!("[rust_widgets][linux] set_window_icon: '{path}' rejected: {error}");
return false;
}
}
}
self.state.set_window_icon(widget_id, path);
true
}
pub(crate) fn window_icon_impl(&self, widget_id: u64) -> Option<String> {
let kind = self.kind_of(widget_id)?;
if !matches!(kind, LinuxHandleKind::Window) {
return None;
}
self.state.window_icon(widget_id)
}
pub(crate) fn set_widget_selection_impl(&self, widget_id: u64, start: u32, end: u32) -> bool {
let Some(kind) = self.kind_of(widget_id) else {
return false;
};
if !matches!(kind, LinuxHandleKind::LineEdit) {
return false;
}
self.state.set_selection(widget_id, start, end);
#[cfg(all(target_os = "linux", feature = "gtk-native"))]
{
let native = self.native.lock_guard();
if let Some(widget) = native.widgets.get(&widget_id) {
if let Ok(entry) = widget.clone().downcast::<gtk::Entry>() {
let start_i = start as i32;
let end_i = if end as i32 >= 0 { end as i32 } else { -1 };
entry.select_region(start_i, end_i);
}
}
}
true
}
pub(crate) fn widget_selection_impl(&self, widget_id: u64) -> Option<(u32, u32)> {
let kind = self.kind_of(widget_id)?;
if !matches!(kind, LinuxHandleKind::LineEdit) {
return None;
}
#[cfg(all(target_os = "linux", feature = "gtk-native"))]
{
let native = self.native.lock_guard();
if let Some(widget) = native.widgets.get(&widget_id) {
if let Ok(entry) = widget.clone().downcast::<gtk::Entry>() {
return entry.selection_bounds().map(|(s, e)| (s as u32, e as u32));
}
}
}
self.state.selection(widget_id)
}
pub(crate) fn set_widget_placeholder_impl(&self, widget_id: u64, text: &str) -> bool {
let Some(kind) = self.kind_of(widget_id) else {
return false;
};
if !matches!(kind, LinuxHandleKind::LineEdit) {
return false;
}
self.state.set_placeholder(widget_id, text);
#[cfg(all(target_os = "linux", feature = "gtk-native"))]
{
let native = self.native.lock_guard();
if let Some(widget) = native.widgets.get(&widget_id) {
if let Ok(entry) = widget.clone().downcast::<gtk::Entry>() {
if text.is_empty() {
entry.set_placeholder_text(None);
} else {
entry.set_placeholder_text(Some(text));
}
}
}
}
true
}
pub(crate) fn widget_placeholder_impl(&self, widget_id: u64) -> Option<String> {
let kind = self.kind_of(widget_id)?;
if !matches!(kind, LinuxHandleKind::LineEdit) {
return None;
}
#[cfg(all(target_os = "linux", feature = "gtk-native"))]
{
let native = self.native.lock_guard();
if let Some(widget) = native.widgets.get(&widget_id) {
if let Ok(entry) = widget.clone().downcast::<gtk::Entry>() {
return self.state.placeholder(widget_id);
}
}
}
self.state.placeholder(widget_id)
}
pub(crate) fn set_widget_echo_mode_impl(
&self,
widget_id: u64,
mode: crate::platform::EchoMode,
) -> bool {
use crate::platform::EchoMode;
let Some(kind) = self.kind_of(widget_id) else {
return false;
};
if !matches!(kind, LinuxHandleKind::LineEdit) {
return false;
}
self.state.set_echo_mode(widget_id, mode);
#[cfg(all(target_os = "linux", feature = "gtk-native"))]
{
let native = self.native.lock_guard();
if let Some(widget) = native.widgets.get(&widget_id) {
if let Ok(entry) = widget.clone().downcast::<gtk::Entry>() {
match mode {
EchoMode::Normal => entry.set_visibility(true),
EchoMode::Password => entry.set_visibility(false),
EchoMode::NoEcho => {
log::warn!(
"[rust_widgets][linux] set_widget_echo_mode: NoEcho has no GTK \
equivalent; refusing"
);
return false;
}
}
}
}
}
true
}
pub(crate) fn widget_echo_mode_impl(
&self,
widget_id: u64,
) -> Option<crate::platform::EchoMode> {
let kind = self.kind_of(widget_id)?;
if !matches!(kind, LinuxHandleKind::LineEdit) {
return None;
}
self.state.echo_mode(widget_id)
}
pub(crate) fn set_slider_orientation_impl(
&self,
widget_id: u64,
orientation: crate::core::Orientation,
) -> bool {
use crate::core::Orientation;
let Some(kind) = self.kind_of(widget_id) else {
return false;
};
if !matches!(kind, LinuxHandleKind::Slider) {
return false;
}
self.state.set_orientation(widget_id, orientation);
#[cfg(all(target_os = "linux", feature = "gtk-native"))]
{
let native = self.native.lock_guard();
if let Some(widget) = native.widgets.get(&widget_id) {
if let Ok(scale) = widget.clone().downcast::<gtk::Scale>() {
let gtk_orientation = if orientation == Orientation::Vertical {
gtk::Orientation::Vertical
} else {
gtk::Orientation::Horizontal
};
scale.set_orientation(gtk_orientation);
}
}
}
true
}
pub(crate) fn slider_orientation_impl(
&self,
widget_id: u64,
) -> Option<crate::core::Orientation> {
use crate::core::Orientation;
let kind = self.kind_of(widget_id)?;
if !matches!(kind, LinuxHandleKind::Slider) {
return None;
}
#[cfg(all(target_os = "linux", feature = "gtk-native"))]
{
let native = self.native.lock_guard();
if let Some(widget) = native.widgets.get(&widget_id) {
if let Ok(scale) = widget.clone().downcast::<gtk::Scale>() {
return Some(if scale.orientation() == gtk::Orientation::Vertical {
Orientation::Vertical
} else {
Orientation::Horizontal
});
}
}
}
self.state.orientation(widget_id)
}
pub(crate) fn set_widget_tristate_impl(&self, widget_id: u64, enabled: bool) -> bool {
let Some(kind) = self.kind_of(widget_id) else {
return false;
};
if !matches!(
kind,
LinuxHandleKind::CheckBox
| LinuxHandleKind::RadioButton
| LinuxHandleKind::ToggleButton
) {
return false;
}
self.state.set_tristate(widget_id, enabled);
#[cfg(all(target_os = "linux", feature = "gtk-native"))]
{
let native = self.native.lock_guard();
if let Some(widget) = native.widgets.get(&widget_id) {
if let Ok(toggle) = widget.clone().downcast::<gtk::ToggleButton>() {
if enabled {
toggle.set_inconsistent(true);
} else {
toggle.set_inconsistent(false);
}
}
}
}
true
}
pub(crate) fn is_widget_tristate_impl(&self, widget_id: u64) -> Option<bool> {
let kind = self.kind_of(widget_id)?;
if !matches!(
kind,
LinuxHandleKind::CheckBox
| LinuxHandleKind::RadioButton
| LinuxHandleKind::ToggleButton
) {
return None;
}
#[cfg(all(target_os = "linux", feature = "gtk-native"))]
{
let native = self.native.lock_guard();
if let Some(widget) = native.widgets.get(&widget_id) {
if let Ok(toggle) = widget.clone().downcast::<gtk::ToggleButton>() {
return Some(toggle.is_inconsistent());
}
}
}
self.state.tristate(widget_id)
}
pub(crate) fn set_widget_group_impl(&self, widget_id: u64, group: &str) -> bool {
let Some(kind) = self.kind_of(widget_id) else {
return false;
};
if !matches!(kind, LinuxHandleKind::RadioButton) {
return false;
}
self.state.set_group(widget_id, group);
true
}
pub(crate) fn widget_group_impl(&self, widget_id: u64) -> Option<String> {
let kind = self.kind_of(widget_id)?;
if !matches!(kind, LinuxHandleKind::RadioButton) {
return None;
}
self.state.group(widget_id)
}
pub(crate) fn set_widget_scroll_position_impl(&self, widget_id: u64, x: i32, y: i32) -> bool {
let Some(kind) = self.kind_of(widget_id) else {
return false;
};
if !matches!(kind, LinuxHandleKind::ScrollArea) {
return false;
}
self.state.set_scroll(widget_id, x, y);
#[cfg(all(target_os = "linux", feature = "gtk-native"))]
{
let native = self.native.lock_guard();
if let Some(widget) = native.widgets.get(&widget_id) {
if let Ok(scrollable) = widget.clone().downcast::<gtk::ScrolledWindow>() {
scrollable.hadjustment().set_value(f64::from(x));
scrollable.vadjustment().set_value(f64::from(y));
}
}
}
true
}
pub(crate) fn widget_scroll_position_impl(&self, widget_id: u64) -> Option<(i32, i32)> {
let kind = self.kind_of(widget_id)?;
if !matches!(kind, LinuxHandleKind::ScrollArea) {
return None;
}
#[cfg(all(target_os = "linux", feature = "gtk-native"))]
{
let native = self.native.lock_guard();
if let Some(widget) = native.widgets.get(&widget_id) {
if let Ok(scrollable) = widget.clone().downcast::<gtk::ScrolledWindow>() {
let x = scrollable.hadjustment().value() as i32;
let y = scrollable.vadjustment().value() as i32;
return Some((x, y));
}
}
}
self.state.scroll(widget_id)
}
pub(crate) fn set_widget_ime_enabled_impl(&self, widget_id: u64, enabled: bool) -> bool {
self.state.set_ime_enabled(widget_id, enabled)
}
pub(crate) fn is_widget_ime_enabled_impl(&self, widget_id: u64) -> bool {
self.state.ime_enabled(widget_id)
}
pub(crate) fn set_widget_accessibility_name_impl(&self, widget_id: u64, name: &str) -> bool {
if !self.state.set_accessibility_name(widget_id, name) {
return false;
}
#[cfg(target_os = "linux")]
if let Some(bridge) = crate::platform::Platform::accessibility_bridge(self) {
bridge.set_accessibility_name(widget_id, name);
bridge.notify_name_changed(widget_id);
}
true
}
pub(crate) fn get_widget_accessibility_name_impl(&self, widget_id: u64) -> String {
#[cfg(target_os = "linux")]
if let Some(bridge) = crate::platform::Platform::accessibility_bridge(self) {
if let Some(name) = bridge.accessibility_name(widget_id) {
return name;
}
}
self.state.accessibility_name(widget_id)
}
pub(crate) fn set_clipboard_text_impl(&self, text: &str) -> bool {
let stored = self.state.set_clipboard_text(text);
#[cfg(all(target_os = "linux", feature = "gtk-native"))]
{
if let Some(clipboard) = gtk_clipboard() {
clipboard.set_text(text);
clipboard.store();
return true;
}
}
stored
}
pub(crate) fn get_clipboard_text_impl(&self) -> String {
#[cfg(all(target_os = "linux", feature = "gtk-native"))]
{
if let Some(clipboard) = gtk_clipboard() {
if let Some(text) = clipboard.wait_for_text() {
let text = text.to_string();
self.state.set_clipboard_text(&text);
return text;
}
}
}
self.state.clipboard_text()
}
pub(crate) fn begin_drag_impl(
&self,
source_widget_id: u64,
mime: &str,
payload: &[u8],
) -> bool {
self.state.begin_drag(source_widget_id, mime, payload)
}
pub(crate) fn poll_drop_event_impl(&self) -> Option<DropEvent> {
self.state.pop_drop_event()
}
pub(crate) fn inject_drop_event_impl(&self, event: DropEvent) -> bool {
self.state.inject_drop_event(event)
}
pub(crate) fn create_message_box_impl(
&self,
parent: u64,
title: &str,
text: &str,
x: i32,
y: i32,
width: u32,
height: u32,
) -> u64 {
let id = self.insert_widget(
LinuxHandleKind::MessageBox,
&format!("{}: {}", title, text),
x,
y,
width,
height,
);
self.menus.lock().expect("linux menu lock poisoned").widget_parent.insert(id, parent);
#[cfg(all(target_os = "linux", feature = "gtk-native"))]
{
let parent_window = self.native_parent_window(parent);
let dialog = gtk::MessageDialog::new(
parent_window.as_ref(),
gtk::DialogFlags::MODAL,
gtk::MessageType::Info,
gtk::ButtonsType::Ok,
text,
);
dialog.set_title(title);
dialog.set_default_size(width as i32, height as i32);
dialog.connect_response(|dialog, _| {
dialog.close();
});
let mut native = self.native.lock_guard();
native.widgets.insert(id, dialog.clone().upcast::<gtk::Widget>());
native.dialogs.insert(id, dialog.upcast::<gtk::Dialog>());
}
id
}
pub(crate) fn create_file_dialog_impl(
&self,
parent: u64,
x: i32,
y: i32,
width: u32,
height: u32,
) -> u64 {
let id = self.insert_widget(LinuxHandleKind::FileDialog, "FileDialog", x, y, width, height);
self.menus.lock().expect("linux menu lock poisoned").widget_parent.insert(id, parent);
#[cfg(all(target_os = "linux", feature = "gtk-native"))]
{
let parent_window = self.native_parent_window(parent);
let dialog = gtk::FileChooserDialog::new(
Some("File"),
parent_window.as_ref(),
gtk::FileChooserAction::Open,
);
dialog.add_buttons(&[
("Cancel", gtk::ResponseType::Cancel),
("Open", gtk::ResponseType::Accept),
]);
dialog.set_default_size(width as i32, height as i32);
let mut native = self.native.lock_guard();
native.widgets.insert(id, dialog.clone().upcast::<gtk::Widget>());
native.dialogs.insert(id, dialog.upcast::<gtk::Dialog>());
}
id
}
pub(crate) fn create_color_dialog_impl(
&self,
parent: u64,
x: i32,
y: i32,
width: u32,
height: u32,
) -> u64 {
let id =
self.insert_widget(LinuxHandleKind::ColorDialog, "ColorDialog", x, y, width, height);
self.menus.lock().expect("linux menu lock poisoned").widget_parent.insert(id, parent);
#[cfg(all(target_os = "linux", feature = "gtk-native"))]
{
let parent_window = self.native_parent_window(parent);
let dialog = gtk::ColorChooserDialog::new(Some("Color"), parent_window.as_ref());
dialog.set_default_size(width as i32, height as i32);
let mut native = self.native.lock_guard();
native.widgets.insert(id, dialog.clone().upcast::<gtk::Widget>());
native.color_choosers.insert(id, dialog.clone().upcast::<gtk::ColorChooser>());
native.dialogs.insert(id, dialog.upcast::<gtk::Dialog>());
}
id
}
pub(crate) fn create_font_dialog_impl(
&self,
parent: u64,
x: i32,
y: i32,
width: u32,
height: u32,
) -> u64 {
let id = self.insert_widget(LinuxHandleKind::FontDialog, "FontDialog", x, y, width, height);
self.menus.lock().expect("linux menu lock poisoned").widget_parent.insert(id, parent);
#[cfg(all(target_os = "linux", feature = "gtk-native"))]
{
let parent_window = self.native_parent_window(parent);
let dialog = gtk::FontChooserDialog::new(Some("Font"), parent_window.as_ref());
dialog.set_default_size(width as i32, height as i32);
let mut native = self.native.lock_guard();
native.widgets.insert(id, dialog.clone().upcast::<gtk::Widget>());
native.font_choosers.insert(id, dialog.clone().upcast::<gtk::FontChooser>());
native.dialogs.insert(id, dialog.upcast::<gtk::Dialog>());
}
id
}
#[cfg(all(target_os = "linux", feature = "gtk-native"))]
pub(crate) fn native_parent_window(&self, parent: u64) -> Option<gtk::Window> {
let native = self.native.lock_guard();
native.windows.get(&parent).cloned()
}
}
#[cfg(all(target_os = "linux", feature = "gtk-native"))]
fn gtk_clipboard() -> Option<gtk::Clipboard> {
if !gtk::is_initialized_main_thread() {
return None;
}
let display = gtk::gdk::Display::default()?;
gtk::Clipboard::default(&display)
}