diff --git a/data/style.scss b/data/style.scss index 96428717..9e3c1812 100644 --- a/data/style.scss +++ b/data/style.scss @@ -63,7 +63,7 @@ $LCD_FG_COLOR: #272863bb; 0 0 0 1px #0006, inset 0 1px 0 #fff4, inset 0 -1px 0 #fff2, - 0 12px 24px #545e5473; + 0 12px 24px #aec3ae73; color: $LCD_FG_COLOR; padding-top: 8px; diff --git a/data/ui/date_view.blp b/data/ui/date_view.blp index 08ff00f9..32877b84 100644 --- a/data/ui/date_view.blp +++ b/data/ui/date_view.blp @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: 2026 Subhadeep Jasu using Gtk 4.0; -using Adw 1; template $PebblesDateView: $PebblesView { selected_view: bind date_stack.visible-child-name; diff --git a/src/core/graphing_calculator.py b/src/core/graphing_calculator.py index 208f86bc..299575b3 100644 --- a/src/core/graphing_calculator.py +++ b/src/core/graphing_calculator.py @@ -68,7 +68,8 @@ def set_plot_params_and_plot(self, payload): 'yMax': payload.get_y_max(), 'xScaling': payload.get_x_scaling(), 'yScaling': payload.get_y_scaling(), - 'darkMode': payload.get_dark_mode() + 'darkMode': payload.get_dark_mode(), + 'fidelity': payload.get_fidelity_mode() } self.start_plotting() @@ -99,64 +100,74 @@ def start_plotting(self): def _plot(self, path=''): try: with self.plot_lock: - steps = [6, 1] + fidelity_mode = self.plot_params['fidelity'] + step_size = 1 if fidelity_mode or path != '' else 6 palette = Pebbles.get_palette(self.plot_params['darkMode']) dpi = self.plot_params['dpi'] width = self.plot_params['width'] height = self.plot_params['height'] plt.tight_layout(pad=4 / dpi) - for step_size in steps: - if self.cancel_event.is_set(): - return - - fig, ax = self._setup_plot(width, height, dpi, step_size) - x_values = self._generate_plot_x_values( - self.plot_params['xScaling'], - width, - step_size - ) - - if self.plot_params['yScaling'] == 1: - x_values = x_values[x_values != 0] - - t_values = np.linspace( - 0, 2*np.pi, width // (step_size ** 2) - ) - for pro in self.calculators: - pro['calc'].set_substitute_value('A', - self.plot_params['a'], zero_limit=True) - pro['calc'].set_substitute_value('B', - self.plot_params['b'], zero_limit=True) - pro['calc'].set_substitute_value('C', - self.plot_params['c'], zero_limit=True) - pro['calc'].set_substitute_value('M', - self.plot_params['m'], zero_limit=True) - if pro['eq'].get_radial_coord_mode(): - self._plot_radial(ax, pro, palette, (t_values, step_size)) - else: - self._plot_cartesian( - ax, pro, palette, - ( - x_values, - step_size, - self.plot_params['xScaling'], - self.plot_params['yScaling'] - ) + if self.cancel_event.is_set(): + return + + fig, ax = self._setup_plot(width, height, dpi, step_size) + x_values = self._generate_plot_x_values( + self.plot_params['xScaling'], + width, + step_size + ) + + if self.plot_params['yScaling'] == 1: + x_values = x_values[x_values != 0] + + t_values = np.linspace( + 0, 2 * np.pi, width // (step_size ** 2) + ) + for pro in self.calculators: + pro['calc'].set_substitute_value('A', + self.plot_params['a'], zero_limit=True) + pro['calc'].set_substitute_value('B', + self.plot_params['b'], zero_limit=True) + pro['calc'].set_substitute_value('C', + self.plot_params['c'], zero_limit=True) + pro['calc'].set_substitute_value('M', + self.plot_params['m'], zero_limit=True) + if pro['eq'].get_radial_coord_mode(): + self._plot_radial(ax, pro, palette, (t_values, step_size)) + else: + self._plot_cartesian( + ax, pro, palette, + ( + x_values, + step_size, + self.plot_params['xScaling'], + self.plot_params['yScaling'] ) + ) - if step_size == 1: + if path == '': + if fidelity_mode: self._draw_legend(ax) self._configure_labels(ax) - - if path != '': - Utils.fig_save_path = path - Utils.plot_to_image(plt, fig, (width, height), dpi, step_size) + pixbuf_f = Utils.plot_to_pixbuf(plt, fig, (width, height), dpi, step_size) + ax.set_xticklabels([]) + ax.set_yticklabels([]) + ax.get_legend().remove() + pixbuf_i = Utils.plot_to_pixbuf(plt, fig, (width, height), dpi, step_size) + if self.on_plot_ready: + self.on_plot_ready(pixbuf_i, pixbuf_f, True) else: - pixbuf = Utils.plot_to_pixbuf(plt, fig, (width, height), dpi, step_size) - + ax.set_xticklabels([]) + ax.set_yticklabels([]) + pixbuf_f = Utils.plot_to_pixbuf(plt, fig, (width, height), dpi, step_size) if self.on_plot_ready: - self.on_plot_ready(pixbuf, True) + self.on_plot_ready(None, pixbuf_f, True) + else: + self._draw_legend(ax) + self._configure_labels(ax) + Utils.fig_save_path = path + Utils.plot_to_image(plt, fig, (width, height), dpi, step_size) except RuntimeError: pass diff --git a/src/models/GraphPayload.Model.vala b/src/models/GraphPayload.Model.vala index 04e7eb11..1e66face 100644 --- a/src/models/GraphPayload.Model.vala +++ b/src/models/GraphPayload.Model.vala @@ -19,9 +19,10 @@ namespace Pebbles { public int height { get; set; } public double dpi { get; set; } public bool dark_mode { get; set; } + public bool fidelity_mode { get; set; } public bool contains_equations () { - return equations != null; + return equations != null && equations.length > 0; } } } diff --git a/src/shell/MainWindow.vala b/src/shell/MainWindow.vala index 3d88b5fe..7f487c43 100644 --- a/src/shell/MainWindow.vala +++ b/src/shell/MainWindow.vala @@ -708,13 +708,6 @@ namespace Pebbles { } private void load_settings () { - // Check if the version is new - if (settings.version != Config.VERSION) { - settings.version = Config.VERSION; - - // Show some kind of What's New dialog? - } - if (settings.load_last_session) { switch (settings.global_angle_unit) { case DEG: @@ -893,8 +886,8 @@ namespace Pebbles { statistics_view.plot (figure, valid); } - protected void on_render_ready (Gdk.Pixbuf? figure, bool valid) { - graphing_view.render_graph (figure, valid); + protected void on_render_ready (Gdk.Pixbuf? figure_i, Gdk.Pixbuf? figure_f, bool valid) { + graphing_view.render_graph (figure_i, figure_f, valid); } protected void on_memory_change (string context, bool present) { diff --git a/src/shell/main_window.py b/src/shell/main_window.py index 0d3aa89a..081c3009 100644 --- a/src/shell/main_window.py +++ b/src/shell/main_window.py @@ -236,8 +236,8 @@ def _on_graph_export(self, _, path): self.graph_calc.export(path) - def _graph_ready_cb(self, pixbuf, valid): - self.on_render_ready(pixbuf, valid) + def _graph_ready_cb(self, pixbuf_i, pixbuf_f, valid): + self.on_render_ready(pixbuf_i, pixbuf_f, valid) def _memory_recall(self, _, context: str): diff --git a/src/shell/views/GraphingView.vala b/src/shell/views/GraphingView.vala index a3bdc24d..82140f03 100644 --- a/src/shell/views/GraphingView.vala +++ b/src/shell/views/GraphingView.vala @@ -84,8 +84,8 @@ namespace Pebbles { var_m_adjustment = new Gtk.Adjustment (0, -double.MAX, double.MAX, 0.01, 0.1, 0); } - public void render_graph (Gdk.Pixbuf? pixbuf, bool valid) { - viewport.show_graph (pixbuf, valid); + public void render_graph (Gdk.Pixbuf? pixbuf_i, Gdk.Pixbuf? pixbuf_f, bool valid) { + viewport.show_graph (pixbuf_i, pixbuf_f, valid); } public void send_shift_modifier (bool shifted) { diff --git a/src/shell/widgets/GraphViewport.vala b/src/shell/widgets/GraphViewport.vala index d0b35285..5193c3d6 100644 --- a/src/shell/widgets/GraphViewport.vala +++ b/src/shell/widgets/GraphViewport.vala @@ -101,7 +101,28 @@ namespace Pebbles { } } + private bool _dragging = false; + private bool dragging { + get { + return _dragging; + } + + set { + _dragging = value; + queue_render = true; + } + } + + private bool scrolling = false; + private Gdk.Pixbuf? figure; + private Gdk.Pixbuf? intermediate_figure; + private double last_pixbuf_zoom_x = 100.0; + private double last_pixbuf_zoom_y = 100.0; + private double last_pixbuf_pan_x = 0.0; + private double last_pixbuf_pan_y = 0.0; + private int last_pixbuf_width = 0; + private int last_pixbuf_height = 0; private bool valid_figure = true; private GlobalAngleUnit angle_unit; private bool queue_render = false; @@ -164,6 +185,11 @@ namespace Pebbles { propagation_phase = Gtk.PropagationPhase.CAPTURE, name = "drag-rotation-capture" }; + + pan_gesture.begin.connect (() => { + dragging = true; + }); + pan_gesture.drag_update.connect ((off_x, off_y) => { var vel_x = off_x - previous_x; previous_x = off_x; @@ -173,9 +199,11 @@ namespace Pebbles { previous_y = off_y; pan_y += vel_y / zoom_y; }); + pan_gesture.drag_end.connect (() => { previous_x = 0; previous_y = 0; + dragging = false; }); add_controller (pan_gesture); @@ -186,10 +214,15 @@ namespace Pebbles { zoom_gesture.begin.connect (() => { previous_sx = zoom_x; previous_sy = zoom_y; + dragging = true; }); zoom_gesture.scale_changed.connect ((off_s) => { zoom_x = previous_sx * off_s; zoom_y = previous_sy * off_s; + dragging = true; + }); + zoom_gesture.end.connect (() => { + dragging = false; }); add_controller (zoom_gesture); @@ -197,6 +230,8 @@ namespace Pebbles { propagation_phase = Gtk.PropagationPhase.CAPTURE }; main_scroll_gesture.scroll.connect ((dx, dy) => { + dragging = true; + scrolling = true; var modifier = main_scroll_gesture.get_current_event_state (); if ((modifier & Gdk.ModifierType.CONTROL_MASK) != 0) { var new_zoom_x = zoom_x - dy; @@ -211,6 +246,10 @@ namespace Pebbles { pan_y -= dy / dpi; } }); + main_scroll_gesture.scroll_end.connect (() => { + dragging = false; + scrolling = false; + }); add_controller (main_scroll_gesture); x_scroll_gesture = new Gtk.EventControllerScroll (Gtk.EventControllerScrollFlags.BOTH_AXES); @@ -222,8 +261,6 @@ namespace Pebbles { if (new_zoom_x > 0) { zoom_x = new_zoom_x; } - - }); y_scroll_gesture.scroll.connect ((dx, dy) => { @@ -270,14 +307,24 @@ namespace Pebbles { width = renderer.get_width (), height = renderer.get_height (), dpi = dpi, - dark_mode = gtk_settings.gtk_application_prefer_dark_theme + dark_mode = gtk_settings.gtk_application_prefer_dark_theme, + fidelity_mode = !dragging }; window.on_render_graph (payload); } - public void show_graph (Gdk.Pixbuf? figure, bool valid) { - this.figure = figure; + public void show_graph (Gdk.Pixbuf? figure_i, Gdk.Pixbuf? figure_f, bool valid) { + this.figure = figure_f; + if (figure_i != null) { + this.intermediate_figure = figure_i; + this.last_pixbuf_pan_x = pan_x; + this.last_pixbuf_pan_y = pan_y; + this.last_pixbuf_zoom_x = zoom_x; + this.last_pixbuf_zoom_y = zoom_y; + this.last_pixbuf_width = figure_i.get_width (); + this.last_pixbuf_height = figure_i.get_height (); + } valid_figure = valid; Idle.add_once (() => { renderer.queue_draw (); @@ -287,7 +334,103 @@ namespace Pebbles { } private void draw_figure (Gtk.DrawingArea area, Cairo.Context cr, int width, int height) { - if (figure != null) { + if ((dragging || scrolling) && intermediate_figure != null && figure != null) { // Intermediate Image + double degrees = Math.PI / 180.0; + double radius = 3.0; + cr.new_sub_path (); + cr.arc (width - radius, radius, radius, -90 * degrees, 0); + cr.arc (width - radius, height - radius, radius, 0, 90 * degrees); + cr.arc (radius, height - radius, radius, 90 * degrees, 180 * degrees); + cr.arc (radius, radius, radius, 180 * degrees, 270 * degrees); + cr.close_path (); + + cr.clip (); + + + int width_f = figure?.get_width (); + int width_i = intermediate_figure?.get_width (); + if (width_f > 0) { + double scale_x = (double) width / width_f; + + cr.save (); + cr.set_operator (Cairo.Operator.SOURCE); + cr.scale (scale_x, scale_x); + Gdk.cairo_set_source_pixbuf ( + cr, + figure, + 0, + 0 + ); + cr.paint (); + cr.restore (); + + if (width_i > 0) { + if (last_pixbuf_zoom_x > 0 && last_pixbuf_zoom_y > 0 && last_pixbuf_width > 0 && last_pixbuf_height > 0) { + double s_x = zoom_x / last_pixbuf_zoom_x; + double s_y = zoom_y / last_pixbuf_zoom_y; + + double img_w = (double) last_pixbuf_width * s_x; + double img_h = (double) last_pixbuf_height * s_y; + + double tx = (double) width / 2.0 + zoom_x * (last_pixbuf_pan_x - pan_x) - img_w / 2.0; + double ty = (double) height / 2.0 - zoom_y * (last_pixbuf_pan_y - pan_y) - img_h / 2.0; + + cr.save (); + if (gtk_settings.gtk_application_prefer_dark_theme) { + cr.set_source_rgb (0.227, 0.227, 0.227); + } else { + cr.set_source_rgb (1.0, 1.0, 1.0); + } + cr.rectangle (tx, ty, img_w, img_h); + cr.fill (); + cr.restore (); + + cr.save (); + cr.rectangle (tx, ty, img_w, img_h); + cr.clip (); + cr.translate (tx, ty); + cr.scale (s_x, s_y); + Gdk.cairo_set_source_pixbuf ( + cr, + intermediate_figure, + 0, + 0 + ); + cr.paint (); + cr.restore (); + } else { + double scale_xi = (double) width / width_i; + + double img_w = width; + double img_h = height; + double tx = 0.0; + double ty = 0.0; + + cr.save (); + if (gtk_settings.gtk_application_prefer_dark_theme) { + cr.set_source_rgb (0.227, 0.227, 0.227); + } else { + cr.set_source_rgb (1.0, 1.0, 1.0); + } + cr.rectangle (tx, ty, img_w, img_h); + cr.fill (); + cr.restore (); + + cr.save (); + cr.set_operator (Cairo.Operator.OVER); + cr.scale (scale_xi, scale_xi); + Gdk.cairo_set_source_pixbuf ( + cr, + intermediate_figure, + 0, + 0 + ); + cr.paint (); + cr.restore (); + } + } + } + } else if (figure != null) { double degrees = Math.PI / 180.0; double radius = 3.0; cr.new_sub_path (); @@ -304,6 +447,7 @@ namespace Pebbles { if (iwidth > 0) { double scale_x = (double) width / iwidth; + cr.save (); cr.set_operator (Cairo.Operator.SOURCE); cr.scale (scale_x, scale_x); Gdk.cairo_set_source_pixbuf ( @@ -313,6 +457,7 @@ namespace Pebbles { 0 ); cr.paint (); + cr.restore (); } } else if (!valid_figure) { // Draw a "No Symbol" (🛇) @@ -331,6 +476,12 @@ namespace Pebbles { cr.line_to (cx + radius * 0.7, cy + radius * 0.7); cr.stroke (); } + + if (scrolling) { + dragging = false; + scrolling = false; + renderer.queue_draw (); + } } private void draw_x_scale (Gtk.DrawingArea area, Cairo.Context cr, int width, int height) { @@ -444,8 +595,10 @@ namespace Pebbles { double y_center = (height - extents.width) / 2 - extents.x_bearing; double x_offset = width - extents.height - major_tick - 4; - cr.move_to (x_offset, y_center); + cr.save (); + cr.translate (x_offset, y_center); cr.rotate (Math.PI_2); + cr.move_to (0, 0); cr.show_text (label); cr.restore (); } diff --git a/src/shell/widgets/displays/ScientificDisplay.vala b/src/shell/widgets/displays/ScientificDisplay.vala index 8325c0e6..9ba0cf24 100644 --- a/src/shell/widgets/displays/ScientificDisplay.vala +++ b/src/shell/widgets/displays/ScientificDisplay.vala @@ -39,22 +39,33 @@ namespace Pebbles { construct { Idle.add (()=> { - Timeout.add (60, ()=> { - if (animation_frame < animation_frames.length) { - main_label.label = animation_frames[animation_frame]; - animation_frame++; - return true; - } + if (settings.version != Config.VERSION) { + settings.version = Config.VERSION; + + Timeout.add (60, ()=> { + if (animation_frame < animation_frames.length) { + main_label.label = animation_frames[animation_frame]; + animation_frame++; + return true; + } + + main_label.label = settings.last_output_scientific; + main_entry.text = settings.last_input_scientific; + main_entry.grab_focus_without_selecting (); + main_entry.set_position (-1); + history_display.visible = true; + history_display.add_css_class ("animate-in"); + return false; + }); + } else { main_label.label = settings.last_output_scientific; main_entry.text = settings.last_input_scientific; main_entry.grab_focus_without_selecting (); main_entry.set_position (-1); history_display.visible = true; history_display.add_css_class ("animate-in"); - - return false; - }); + } return false; }, Priority.LOW);