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

Adds a hook for intercepting close requests. #1118

Merged
merged 2 commits into from
Aug 17, 2020
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
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ You can find its changes [documented below](#060---2020-06-01).
- Re-export `druid_shell::Scalable` under `druid` namespace. ([#1075] by [@ForLoveOfCats])
- `TextBox` now supports ctrl and shift hotkeys. ([#1076] by [@vkahl])
- Added selection text color to textbox. ([#1093] by [@sysint64])
- Close requests from the shell can now be intercepted ([#1118] by [@jneem])

### Changed

Expand Down Expand Up @@ -45,7 +46,7 @@ You can find its changes [documented below](#060---2020-06-01).
- `ViewSwitcher` now skips the update after switching widgets. ([#1113] by [@finnerale])
- Key and KeyOrValue derive Clone ([#1119] by [@rjwittams])
- Allow submit_command from the layout method in Widgets ([#1119] by [@rjwittams])
- Allow derivation of lenses for generic types ([#1120]) by [@rjwittams])
- Allow derivation of lenses for generic types ([#1120]) by [@rjwittams])

### Visual

Expand Down Expand Up @@ -382,6 +383,7 @@ Last release without a changelog :(
[#1093]: https://github.com/linebender/druid/pull/1093
[#1100]: https://github.com/linebender/druid/pull/1100
[#1103]: https://github.com/linebender/druid/pull/1103
[#1118]: https://github.com/linebender/druid/pull/1118
[#1119]: https://github.com/linebender/druid/pull/1119
[#1120]: https://github.com/linebender/druid/pull/1120

Expand Down
4 changes: 4 additions & 0 deletions druid-shell/examples/invalidate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ impl WinHandler for InvalidateTest {
}
}

fn request_close(&mut self) {
self.handle.close();
}

fn destroy(&mut self) {
Application::global().quit()
}
Expand Down
4 changes: 4 additions & 0 deletions druid-shell/examples/perftest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,10 @@ impl WinHandler for PerfTest {
self.size = size;
}

fn request_close(&mut self) {
self.handle.close();
}

fn destroy(&mut self) {
Application::global().quit()
}
Expand Down
89 changes: 89 additions & 0 deletions druid-shell/examples/quit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Copyright 2020 The Druid Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::any::Any;

use druid_shell::kurbo::{Line, Rect, Size};
use druid_shell::piet::{Color, RenderContext};

use druid_shell::{Application, HotKey, Menu, SysMods, WinHandler, WindowBuilder, WindowHandle};

const BG_COLOR: Color = Color::rgb8(0x27, 0x28, 0x22);
const FG_COLOR: Color = Color::rgb8(0xf0, 0xf0, 0xea);

#[derive(Default)]
struct QuitState {
quit_count: u32,
size: Size,
handle: WindowHandle,
}

impl WinHandler for QuitState {
fn connect(&mut self, handle: &WindowHandle) {
self.handle = handle.clone();
}

fn paint(&mut self, piet: &mut piet_common::Piet, _: Rect) -> bool {
let rect = self.size.to_rect();
piet.fill(rect, &BG_COLOR);
piet.stroke(Line::new((10.0, 50.0), (90.0, 90.0)), &FG_COLOR, 1.0);
false
}

fn size(&mut self, size: Size) {
self.size = size;
}

fn request_close(&mut self) {
self.quit_count += 1;
if self.quit_count >= 5 {
self.handle.close();
} else {
log::info!("Don't wanna quit");
}
}

fn destroy(&mut self) {
Application::global().quit()
}

fn as_any(&mut self) -> &mut dyn Any {
self
}
}

fn main() {
simple_logger::init().expect("Failed to init simple logger");
let app = Application::new().unwrap();
let mut file_menu = Menu::new();
file_menu.add_item(
0x100,
"E&xit",
Some(&HotKey::new(SysMods::Cmd, "q")),
true,
false,
);
let mut menubar = Menu::new();
menubar.add_dropdown(Menu::new(), "Application", true);

let mut builder = WindowBuilder::new(app.clone());
builder.set_handler(Box::new(QuitState::default()));
builder.set_title("Quit example");
builder.set_menu(menubar);

let window = builder.build().unwrap();
window.show();

app.run(None);
}
4 changes: 4 additions & 0 deletions druid-shell/examples/shello.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ impl WinHandler for HelloState {
self.size = size;
}

fn request_close(&mut self) {
self.handle.close();
}

fn destroy(&mut self) {
Application::global().quit()
}
Expand Down
20 changes: 18 additions & 2 deletions druid-shell/src/platform/gtk/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ use crate::piet::{Piet, RenderContext};
use crate::common_util::IdleCallback;
use crate::dialog::{FileDialogOptions, FileDialogType, FileInfo};
use crate::error::Error as ShellError;
use crate::keyboard::{KbKey, KeyState, KeyEvent, Modifiers};
use crate::keyboard::{KbKey, KeyEvent, KeyState, Modifiers};
use crate::mouse::{Cursor, MouseButton, MouseButtons, MouseEvent};
use crate::scale::{Scale, Scalable, ScaledArea};
use crate::scale::{Scalable, Scale, ScaledArea};
use crate::window::{IdleToken, Text, TimerToken, WinHandler};

use super::application::Application;
Expand Down Expand Up @@ -116,6 +116,9 @@ pub(crate) struct WindowState {
window: ApplicationWindow,
scale: Cell<Scale>,
area: Cell<ScaledArea>,
/// Used to determine whether to honor close requests from the system: we inhibit them unless
/// this is true, and this gets set to true when our client requests a close.
closing: Cell<bool>,
drawing_area: DrawingArea,
pub(crate) handler: RefCell<Box<dyn WinHandler>>,
idle_queue: Arc<Mutex<Vec<IdleKind>>>,
Expand Down Expand Up @@ -198,6 +201,7 @@ impl WindowBuilder {
window,
scale: Cell::new(scale),
area: Cell::new(area),
closing: Cell::new(false),
drawing_area,
handler: RefCell::new(handler),
idle_queue: Arc::new(Mutex::new(vec![])),
Expand Down Expand Up @@ -509,6 +513,17 @@ impl WindowBuilder {
Inhibit(true)
}));

win_state
.window
.connect_delete_event(clone!(handle => move |_widget, _ev| {
if let Some(state) = handle.state.upgrade() {
state.handler.borrow_mut().request_close();
Inhibit(!state.closing.get())
} else {
Inhibit(false)
}
}));

win_state
.drawing_area
.connect_destroy(clone!(handle => move |_widget| {
Expand Down Expand Up @@ -556,6 +571,7 @@ impl WindowHandle {
/// Close the window.
pub fn close(&self) {
if let Some(state) = self.state.upgrade() {
state.closing.set(true);
state.window.close();
}
}
Expand Down
10 changes: 10 additions & 0 deletions druid-shell/src/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,16 @@ pub trait WinHandler {
#[allow(unused_variables)]
fn got_focus(&mut self) {}

/// Called when the shell requests to close the window, for example because the user clicked
/// the little "X" in the titlebar.
///
/// If you want to actually close the window in response to this request, call
/// [`WindowHandle::close`]. If you don't implement this method, clicking the titlebar "X" will
/// have no effect.
///
/// [`WindowHandle::close`]: struct.WindowHandle.html#tymethod.close
fn request_close(&mut self) {}

/// Called when the window is being destroyed. Note that this happens
/// earlier in the sequence than drop (at WM_DESTROY, while the latter is
/// WM_NCDESTROY).
Expand Down
34 changes: 25 additions & 9 deletions druid/src/win_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -306,23 +306,26 @@ impl<T: Data> Inner<T> {
}
}

fn dispatch_cmd(&mut self, target: Target, cmd: Command) {
/// Returns `true` if the command was handled.
fn dispatch_cmd(&mut self, target: Target, cmd: Command) -> bool {
if !self.delegate_cmd(target, &cmd) {
return;
return true;
}

match target {
Target::Window(id) => {
// first handle special window-level events
if cmd.is(sys_cmd::SET_MENU) {
return self.set_menu(id, &cmd);
self.set_menu(id, &cmd);
return true;
}
if cmd.is(sys_cmd::SHOW_CONTEXT_MENU) {
return self.show_context_menu(id, &cmd);
self.show_context_menu(id, &cmd);
return true;
}
if let Some(w) = self.windows.get_mut(id) {
let event = Event::Command(cmd);
w.event(&mut self.command_queue, event, &mut self.data, &self.env);
return w.event(&mut self.command_queue, event, &mut self.data, &self.env);
}
}
// in this case we send it to every window that might contain
Expand All @@ -332,19 +335,20 @@ impl<T: Data> Inner<T> {
let event =
Event::Internal(InternalEvent::TargetedCommand(id.into(), cmd.clone()));
if w.event(&mut self.command_queue, event, &mut self.data, &self.env) {
break;
return true;
}
}
}
Target::Global => {
for w in self.windows.iter_mut() {
let event = Event::Command(cmd.clone());
if w.event(&mut self.command_queue, event, &mut self.data, &self.env) {
break;
return true;
}
}
}
}
false
}

fn do_window_event(&mut self, source_id: WindowId, event: Event) -> bool {
Expand Down Expand Up @@ -552,7 +556,11 @@ impl<T: Data> AppState<T> {
// FIXME: we need to be able to open a file without a window handle
T::Window(id) if cmd.is(sys_cmd::SHOW_OPEN_PANEL) => self.show_open_panel(cmd, id),
T::Window(id) if cmd.is(sys_cmd::SHOW_SAVE_PANEL) => self.show_save_panel(cmd, id),
T::Window(id) if cmd.is(sys_cmd::CLOSE_WINDOW) => self.request_close_window(id),
T::Window(id) if cmd.is(sys_cmd::CLOSE_WINDOW) => {
if !self.inner.borrow_mut().dispatch_cmd(target, cmd) {
self.request_close_window(id);
}
}
T::Window(id) if cmd.is(sys_cmd::SHOW_WINDOW) => self.show_window(id),
T::Window(id) if cmd.is(sys_cmd::PASTE) => self.do_paste(id),
_ if cmd.is(sys_cmd::CLOSE_WINDOW) => {
Expand All @@ -561,7 +569,9 @@ impl<T: Data> AppState<T> {
_ if cmd.is(sys_cmd::SHOW_WINDOW) => {
log::warn!("SHOW_WINDOW command must target a window.")
}
_ => self.inner.borrow_mut().dispatch_cmd(target, cmd),
_ => {
self.inner.borrow_mut().dispatch_cmd(target, cmd);
}
}
}

Expand Down Expand Up @@ -737,6 +747,12 @@ impl<T: Data> WinHandler for DruidHandler<T> {
self
}

fn request_close(&mut self) {
self.app_state
.handle_cmd(self.window_id.into(), sys_cmd::CLOSE_WINDOW.into());
self.app_state.inner.borrow_mut().do_update();
}

fn destroy(&mut self) {
self.app_state.remove_window(self.window_id);
}
Expand Down