summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRémi Verschelde <remi@verschelde.fr>2021-07-13 10:46:30 +0200
committerGitHub <noreply@github.com>2021-07-13 10:46:30 +0200
commit51b0aed4b722407c02c7685cefe6b403df7e2806 (patch)
treeba85d9534032fe8bf7fbc81305664d53de19dc49
parentf2ad067509865f524497175fca600300dae8b7d8 (diff)
parent43f9699a2605bdad9a21eab64247bcee2e7c6f77 (diff)
Merge pull request #48252 from Calinou/improve-2d-editor-zoom
-rw-r--r--editor/editor_zoom_widget.cpp86
-rw-r--r--editor/editor_zoom_widget.h2
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp34
3 files changed, 93 insertions, 29 deletions
diff --git a/editor/editor_zoom_widget.cpp b/editor/editor_zoom_widget.cpp
index f9be829493..44751a480a 100644
--- a/editor/editor_zoom_widget.cpp
+++ b/editor/editor_zoom_widget.cpp
@@ -51,17 +51,17 @@ void EditorZoomWidget::_update_zoom_label() {
}
void EditorZoomWidget::_button_zoom_minus() {
- set_zoom_by_increments(-6);
+ set_zoom_by_increments(-6, Input::get_singleton()->is_key_pressed(KEY_ALT));
emit_signal("zoom_changed", zoom);
}
void EditorZoomWidget::_button_zoom_reset() {
- set_zoom(1.0);
+ set_zoom(1.0 * MAX(1, EDSCALE));
emit_signal("zoom_changed", zoom);
}
void EditorZoomWidget::_button_zoom_plus() {
- set_zoom_by_increments(6);
+ set_zoom_by_increments(6, Input::get_singleton()->is_key_pressed(KEY_ALT));
emit_signal("zoom_changed", zoom);
}
@@ -76,31 +76,69 @@ void EditorZoomWidget::set_zoom(float p_zoom) {
}
}
-void EditorZoomWidget::set_zoom_by_increments(int p_increment_count) {
- // Base increment factor defined as the twelveth root of two.
- // This allow a smooth geometric evolution of the zoom, with the advantage of
- // visiting all integer power of two scale factors.
- // note: this is analogous to the 'semitones' interval in the music world
- // In order to avoid numerical imprecisions, we compute and edit a zoom index
- // with the following relation: zoom = 2 ^ (index / 12)
-
- if (zoom < CMP_EPSILON || p_increment_count == 0) {
- return;
- }
+void EditorZoomWidget::set_zoom_by_increments(int p_increment_count, bool p_integer_only) {
+ // Remove editor scale from the index computation.
+ const float zoom_noscale = zoom / MAX(1, EDSCALE);
+
+ if (p_integer_only) {
+ // Only visit integer scaling factors above 100%, and fractions with an integer denominator below 100%
+ // (1/2 = 50%, 1/3 = 33.33%, 1/4 = 25%, …).
+ // This is useful when working on pixel art projects to avoid distortion.
+ // This algorithm is designed to handle fractional start zoom values correctly
+ // (e.g. 190% will zoom up to 200% and down to 100%).
+ if (zoom_noscale + p_increment_count * 0.001 >= 1.0 - CMP_EPSILON) {
+ // New zoom is certain to be above 100%.
+ if (p_increment_count >= 1) {
+ // Zooming.
+ set_zoom(Math::floor(zoom_noscale + p_increment_count) * MAX(1, EDSCALE));
+ } else {
+ // Dezooming.
+ set_zoom(Math::ceil(zoom_noscale + p_increment_count) * MAX(1, EDSCALE));
+ }
+ } else {
+ if (p_increment_count >= 1) {
+ // Zooming. Convert the current zoom into a denominator.
+ float new_zoom = 1.0 / Math::ceil(1.0 / zoom_noscale - p_increment_count);
+ if (Math::is_equal_approx(zoom_noscale, new_zoom)) {
+ // New zoom is identical to the old zoom, so try again.
+ // This can happen due to floating-point precision issues.
+ new_zoom = 1.0 / Math::ceil(1.0 / zoom_noscale - p_increment_count - 1);
+ }
+ set_zoom(new_zoom * MAX(1, EDSCALE));
+ } else {
+ // Dezooming. Convert the current zoom into a denominator.
+ float new_zoom = 1.0 / Math::floor(1.0 / zoom_noscale - p_increment_count);
+ if (Math::is_equal_approx(zoom_noscale, new_zoom)) {
+ // New zoom is identical to the old zoom, so try again.
+ // This can happen due to floating-point precision issues.
+ new_zoom = 1.0 / Math::floor(1.0 / zoom_noscale - p_increment_count + 1);
+ }
+ set_zoom(new_zoom * MAX(1, EDSCALE));
+ }
+ }
+ } else {
+ // Base increment factor defined as the twelveth root of two.
+ // This allow a smooth geometric evolution of the zoom, with the advantage of
+ // visiting all integer power of two scale factors.
+ // note: this is analogous to the 'semitones' interval in the music world
+ // In order to avoid numerical imprecisions, we compute and edit a zoom index
+ // with the following relation: zoom = 2 ^ (index / 12)
- // Remove Editor scale from the index computation
- float zoom_noscale = zoom / MAX(1, EDSCALE);
+ if (zoom < CMP_EPSILON || p_increment_count == 0) {
+ return;
+ }
- // zoom = 2**(index/12) => log2(zoom) = index/12
- float closest_zoom_index = Math::round(Math::log(zoom_noscale) * 12.f / Math::log(2.f));
+ // zoom = 2**(index/12) => log2(zoom) = index/12
+ float closest_zoom_index = Math::round(Math::log(zoom_noscale) * 12.f / Math::log(2.f));
- float new_zoom_index = closest_zoom_index + p_increment_count;
- float new_zoom = Math::pow(2.f, new_zoom_index / 12.f);
+ float new_zoom_index = closest_zoom_index + p_increment_count;
+ float new_zoom = Math::pow(2.f, new_zoom_index / 12.f);
- // Restore Editor scale transformation
- new_zoom *= MAX(1, EDSCALE);
+ // Restore Editor scale transformation
+ new_zoom *= MAX(1, EDSCALE);
- set_zoom(new_zoom);
+ set_zoom(new_zoom);
+ }
}
void EditorZoomWidget::_notification(int p_what) {
@@ -118,7 +156,7 @@ void EditorZoomWidget::_notification(int p_what) {
void EditorZoomWidget::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_zoom", "zoom"), &EditorZoomWidget::set_zoom);
ClassDB::bind_method(D_METHOD("get_zoom"), &EditorZoomWidget::get_zoom);
- ClassDB::bind_method(D_METHOD("set_zoom_by_increments", "increment"), &EditorZoomWidget::set_zoom_by_increments);
+ ClassDB::bind_method(D_METHOD("set_zoom_by_increments", "increment", "integer_only"), &EditorZoomWidget::set_zoom_by_increments);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "zoom"), "set_zoom", "get_zoom");
diff --git a/editor/editor_zoom_widget.h b/editor/editor_zoom_widget.h
index 4e95018e52..c35faeefe7 100644
--- a/editor/editor_zoom_widget.h
+++ b/editor/editor_zoom_widget.h
@@ -56,7 +56,7 @@ public:
float get_zoom();
void set_zoom(float p_zoom);
- void set_zoom_by_increments(int p_increment_count);
+ void set_zoom_by_increments(int p_increment_count, bool p_integer_only = false);
};
#endif // EDITOR_ZOOM_WIDGET_H
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index 7282475ddf..94cd8ce17d 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -1149,8 +1149,9 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event, bo
view_offset.y += int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom * b->get_factor();
update_viewport();
} else {
- zoom_widget->set_zoom_by_increments(-1);
- if (b->get_factor() != 1.f) {
+ zoom_widget->set_zoom_by_increments(-1, Input::get_singleton()->is_key_pressed(KEY_ALT));
+ if (!Math::is_equal_approx(b->get_factor(), 1.0f)) {
+ // Handle high-precision (analog) scrolling.
zoom_widget->set_zoom(zoom * ((zoom_widget->get_zoom() / zoom - 1.f) * b->get_factor() + 1.f));
}
_zoom_on_position(zoom_widget->get_zoom(), b->get_position());
@@ -1164,8 +1165,9 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event, bo
view_offset.y -= int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom * b->get_factor();
update_viewport();
} else {
- zoom_widget->set_zoom_by_increments(1);
- if (b->get_factor() != 1.f) {
+ zoom_widget->set_zoom_by_increments(1, Input::get_singleton()->is_key_pressed(KEY_ALT));
+ if (!Math::is_equal_approx(b->get_factor(), 1.0f)) {
+ // Handle high-precision (analog) scrolling.
zoom_widget->set_zoom(zoom * ((zoom_widget->get_zoom() / zoom - 1.f) * b->get_factor() + 1.f));
}
_zoom_on_position(zoom_widget->get_zoom(), b->get_position());
@@ -1194,6 +1196,20 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event, bo
Ref<InputEventKey> k = p_event;
if (k.is_valid()) {
+ if (k->is_pressed()) {
+ if (ED_GET_SHORTCUT("canvas_item_editor/zoom_100_percent")->is_shortcut(p_event)) {
+ _update_zoom(1.0 * MAX(1, EDSCALE));
+ } else if (ED_GET_SHORTCUT("canvas_item_editor/zoom_200_percent")->is_shortcut(p_event)) {
+ _update_zoom(2.0 * MAX(1, EDSCALE));
+ } else if (ED_GET_SHORTCUT("canvas_item_editor/zoom_400_percent")->is_shortcut(p_event)) {
+ _update_zoom(4.0 * MAX(1, EDSCALE));
+ } else if (ED_GET_SHORTCUT("canvas_item_editor/zoom_800_percent")->is_shortcut(p_event)) {
+ _update_zoom(8.0 * MAX(1, EDSCALE));
+ } else if (ED_GET_SHORTCUT("canvas_item_editor/zoom_1600_percent")->is_shortcut(p_event)) {
+ _update_zoom(16.0 * MAX(1, EDSCALE));
+ }
+ }
+
bool is_pan_key = pan_view_shortcut.is_valid() && pan_view_shortcut->is_shortcut(p_event);
if (is_pan_key && (EditorSettings::get_singleton()->get("editors/2d/simple_panning") || drag_type != DRAG_NONE)) {
@@ -5610,6 +5626,16 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
skeleton_menu->get_popup()->set_item_checked(skeleton_menu->get_popup()->get_item_index(SKELETON_SHOW_BONES), true);
singleton = this;
+ // To ensure that scripts can parse the list of shortcuts correctly, we have to define
+ // those shortcuts one by one.
+ // Resetting zoom to 100% is a duplicate shortcut of `canvas_item_editor/reset_zoom`,
+ // but it ensures both 1 and Ctrl + 0 can be used to reset zoom.
+ ED_SHORTCUT("canvas_item_editor/zoom_100_percent", TTR("Zoom To 100%"), KEY_1);
+ ED_SHORTCUT("canvas_item_editor/zoom_200_percent", TTR("Zoom To 200%"), KEY_2);
+ ED_SHORTCUT("canvas_item_editor/zoom_400_percent", TTR("Zoom To 400%"), KEY_3);
+ ED_SHORTCUT("canvas_item_editor/zoom_800_percent", TTR("Zoom To 800%"), KEY_4);
+ ED_SHORTCUT("canvas_item_editor/zoom_1600_percent", TTR("Zoom To 1600%"), KEY_5);
+
set_process_unhandled_key_input(true);
// Update the menus' checkboxes