Skip to content

Commit

Permalink
textview: Allow zooming the text (#21)
Browse files Browse the repository at this point in the history
This allows the user to change the text size via keyboard, scrolling,
zoom gesture and from the primary menu.
  • Loading branch information
jsparber authored Jan 17, 2025
1 parent 1ac4aba commit 259a2df
Show file tree
Hide file tree
Showing 8 changed files with 290 additions and 33 deletions.
2 changes: 2 additions & 0 deletions aardvark-app/src/aardvark.gresource.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
<gresources>
<gresource prefix="/org/p2panda/aardvark">
<file preprocess="xml-stripblanks">window.ui</file>
<file preprocess="xml-stripblanks">components/zoom_level_selector.ui</file>
<file preprocess="xml-stripblanks">gtk/help-overlay.ui</file>
<file compressed="true" alias="style.css">style.css</file>
</gresource>
</gresources>

3 changes: 3 additions & 0 deletions aardvark-app/src/components/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
mod zoom_level_selector;

pub use self::zoom_level_selector::ZoomLevelSelector;
81 changes: 81 additions & 0 deletions aardvark-app/src/components/zoom_level_selector.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/* zoom_level_selector.rs
*
* Copyright 2025 Julian Sparber <[email protected]>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
use std::cell::Cell;

use adw::subclass::prelude::*;
use gtk::prelude::*;
use gtk::glib;
use sourceview::*;

mod imp {
use super::*;

#[derive(Debug, Default, glib::Properties, gtk::CompositeTemplate)]
#[properties(wrapper_type = super::ZoomLevelSelector)]
#[template(resource = "/org/p2panda/aardvark/components/zoom_level_selector.ui")]
pub struct ZoomLevelSelector {
#[template_child]
pub button: TemplateChild<gtk::Button>,
#[property(get, set)]
zoom_level: Cell<f64>,
}

#[glib::object_subclass]
impl ObjectSubclass for ZoomLevelSelector {
const NAME: &'static str = "ZoomLevelSelector";
type Type = super::ZoomLevelSelector;
type ParentType = gtk::Box;

fn class_init(klass: &mut Self::Class) {
klass.bind_template();
}

fn instance_init(obj: &glib::subclass::InitializingObject<Self>) {
obj.init_template();
}
}

#[glib::derived_properties]
impl ObjectImpl for ZoomLevelSelector {
fn constructed(&self) {
self.parent_constructed();
self.obj()
.bind_property("zoom_level", &*self.button, "label")
.sync_create()
.transform_to(|_, zoom_level: f64| Some(format!("{:.0}%", zoom_level * 100.0)))
.build();
}
}

impl WidgetImpl for ZoomLevelSelector {}
impl BoxImpl for ZoomLevelSelector {}
}

glib::wrapper! {
pub struct ZoomLevelSelector(ObjectSubclass<imp::ZoomLevelSelector>)
@extends gtk::Widget, gtk::Box;
}

impl ZoomLevelSelector {
pub fn new() -> Self {
glib::Object::builder()
.build()
}
}
57 changes: 57 additions & 0 deletions aardvark-app/src/components/zoom_level_selector.ui
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk" version="4.0"/>
<template class="ZoomLevelSelector" parent="GtkBox">
<property name="hexpand">True</property>
<child>
<object class="GtkBox">
<property name="spacing">12</property>
<property name="margin-start">18</property>
<property name="margin-end">18</property>
<child>
<object class="GtkButton">
<property name="icon-name">zoom-out-symbolic</property>
<!-- Translators: Button tooltip-->
<property name="tooltip-text" translatable="yes">Zoom Out</property>
<property name="action-name">window.zoom-out</property>
<accessibility>
<!-- Translators: Button -->
<property name="label" translatable="yes">Zoom Out</property>
</accessibility>
<style>
<class name="circular"/>
<class name="flat"/>
</style>
</object>
</child>
<child>
<object class="GtkButton" id="button">
<property name="hexpand">True</property>
<property name="action-name">window.zoom-one</property>
<style>
<class name="circular"/>
<class name="flat"/>
</style>
</object>
</child>
<child>
<object class="GtkButton">
<property name="icon-name">zoom-in-symbolic</property>
<!-- Translators: Button tooltip-->
<property name="tooltip-text" translatable="yes">Zoom In</property>
<property name="action-name">window.zoom-in</property>
<accessibility>
<!-- Translators: Button -->
<property name="label" translatable="yes">Zoom In</property>
</accessibility>
<style>
<class name="circular"/>
<class name="flat"/>
</style>
</object>
</child>
</object>
</child>
</template>
</interface>

1 change: 1 addition & 0 deletions aardvark-app/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

mod application;
mod config;
mod components;
mod document;
mod textbuffer;
mod window;
Expand Down
4 changes: 0 additions & 4 deletions aardvark-app/src/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,3 @@
.user-counter {
font-weight: bold;
}

.editor {
font-size: 24px;
}
109 changes: 106 additions & 3 deletions aardvark-app/src/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,23 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/

use std::cell;

use adw::prelude::AdwDialogExt;
use adw::subclass::prelude::*;
use gtk::prelude::*;
use gtk::{gio, glib};
use gtk::{gio, glib, gdk};
use sourceview::*;

use crate::AardvarkTextBuffer;
use crate::{AardvarkTextBuffer, components::ZoomLevelSelector};

const BASE_TEXT_FONT_SIZE: f64 = 24.0;

mod imp {
use super::*;

#[derive(Debug, Default, gtk::CompositeTemplate)]
#[derive(Debug, Default, glib::Properties, gtk::CompositeTemplate)]
#[properties(wrapper_type = super::AardvarkWindow)]
#[template(resource = "/org/p2panda/aardvark/window.ui")]
pub struct AardvarkWindow {
// Template widgets
Expand All @@ -41,6 +46,12 @@ mod imp {
pub open_document_dialog: TemplateChild<adw::Dialog>,
#[template_child]
pub toast_overlay: TemplateChild<adw::ToastOverlay>,
pub css_provider: gtk::CssProvider,
pub font_size: cell::Cell<f64>,
#[property(get, set = Self::set_font_scale, default = 0.0)]
pub font_scale: cell::Cell<f64>,
#[property(get, default = 1.0)]
pub zoom_level: cell::Cell<f64>,
}

#[glib::object_subclass]
Expand All @@ -50,21 +61,99 @@ mod imp {
type ParentType = adw::ApplicationWindow;

fn class_init(klass: &mut Self::Class) {
ZoomLevelSelector::static_type();

klass.bind_template();

klass.install_action("window.zoom-in", None, |window, _, _| {
window.set_font_scale(window.font_scale() + 1.0);
});
klass.install_action("window.zoom-out", None, |window, _, _| {
window.set_font_scale(window.font_scale() - 1.0);
});
klass.install_action("window.zoom-one", None, |window, _, _| {
window.set_font_scale(0.0);
});

klass.add_binding_action(gdk::Key::plus,
gdk::ModifierType::CONTROL_MASK,
"window.zoom-in");
klass.add_binding_action(gdk::Key::KP_Add,
gdk::ModifierType::CONTROL_MASK,
"window.zoom-in");
klass.add_binding_action(gdk::Key::minus,
gdk::ModifierType::CONTROL_MASK,
"window.zoom-out");
// gnome-text-editor uses this as well: probably to make it
// nicer for the US keyboard layout
klass.add_binding_action(gdk::Key::equal,
gdk::ModifierType::CONTROL_MASK,
"window.zoom-out");
klass.add_binding_action(gdk::Key::KP_Subtract,
gdk::ModifierType::CONTROL_MASK,
"window.zoom-out");
klass.add_binding_action(gdk::Key::_0,
gdk::ModifierType::CONTROL_MASK,
"window.zoom-one");
klass.add_binding_action(gdk::Key::KP_0,
gdk::ModifierType::CONTROL_MASK,
"window.zoom-one");
}

fn instance_init(obj: &glib::subclass::InitializingObject<Self>) {
obj.init_template();
}
}

#[glib::derived_properties]
impl ObjectImpl for AardvarkWindow {
fn constructed(&self) {
self.parent_constructed();

let buffer = AardvarkTextBuffer::new();
self.text_view.set_buffer(Some(&buffer));

self.font_size.set(BASE_TEXT_FONT_SIZE);
self.obj().set_font_scale(0.0);
gtk::style_context_add_provider_for_display (
&self.obj().display(),
&self.css_provider,
gtk::STYLE_PROVIDER_PRIORITY_APPLICATION);

let scroll_controller = gtk::EventControllerScroll::new(gtk::EventControllerScrollFlags::VERTICAL);
scroll_controller.set_propagation_phase(gtk::PropagationPhase::Capture);
let window = self.obj().clone();
scroll_controller.connect_scroll(move |scroll, _dx, dy| {
if scroll.current_event_state().contains(gdk::ModifierType::CONTROL_MASK) {
if dy < 0.0 {
window.set_font_scale(window.font_scale() + 1.0);
} else {
window.set_font_scale(window.font_scale() - 1.0);
}
glib::Propagation::Stop
} else {
glib::Propagation::Proceed
}
});
self.obj().add_controller(scroll_controller);

let zoom_gesture = gtk::GestureZoom::new();
let window = self.obj().clone();
let prev_delta = std::cell::Cell::new(0.0);
zoom_gesture.connect_scale_changed(move |_, delta| {
if prev_delta.get() == delta {
return;
}

if prev_delta.get() < delta {
window.set_font_scale(window.font_scale() + delta);
} else {
window.set_font_scale(window.font_scale() - delta);
}
prev_delta.set(delta);
});
self.obj().add_controller(zoom_gesture);

let window = self.obj().clone();
let dialog = self.open_document_dialog.clone();
self.open_document_button.connect_clicked(move |_| {
Expand All @@ -73,6 +162,20 @@ mod imp {
}
}

impl AardvarkWindow {
fn set_font_scale(&self, value: f64) {
let font_size = self.font_size.get();

self.font_scale.set(value);

let size = (font_size + self.obj().font_scale()).max(1.0);
self.zoom_level.set(size / font_size);
self.obj().notify_zoom_level();
self.css_provider.load_from_string(&format!( ".sourceview {{ font-size: {size}px; }}"));
self.obj().action_set_enabled("window.zoom-out", size > 1.0);
}
}

impl WidgetImpl for AardvarkWindow {}
impl WindowImpl for AardvarkWindow {}
impl ApplicationWindowImpl for AardvarkWindow {}
Expand Down
Loading

0 comments on commit 259a2df

Please sign in to comment.