From acf2f4280c8e0194f9a64f36000abb7cd944e415 Mon Sep 17 00:00:00 2001 From: Kelly Yang <124ykl@gmail.com> Date: Sun, 8 Mar 2026 09:11:19 -0700 Subject: [PATCH] fix(maskeditor): make brush size slider logarithmic (#8097) (#9534) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary fix #8097. This PR shifts the Mask Editor Brush Size slider from a linear scale to a logarithmic (exponential) scale. Previously, the linear 1-250 range heavily clumped the usable, small "fine-detail" brush sizes (e.g., 1px to 20px) into the very first 10% of the slider, making it extremely difficult to select precise sizes with the mouse. This update borrows UX paradigms from other standard image editors like Photoshop and GIMP, which map their scale entry widgets on an exponential curve. ## GIMP Source By inspecting the official **GIMP** source code under `libgimpwidgets/gimpscaleentry.c`, we can see this exact mathematical relationship being utilized when the logarithmic property is marked TRUE on a brush radius adjustment widget: ``` // Mapping visual slider to internal value value = gtk_adjustment_get_lower(...) + exp(t); // Mapping internal value to visual slider t = log (value - gtk_adjustment_get_lower(...) + 0.1); ``` https://github.com/user-attachments/assets/6d59ff12-f623-42cc-a52b-84147e9bb90b ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-9534-fix-maskeditor-make-brush-size-slider-logarithmic-8097-31c6d73d365081118508e8363e0c5312) by [Unito](https://www.unito.io) --- .../maskeditor/BrushSettingsPanel.vue | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/components/maskeditor/BrushSettingsPanel.vue b/src/components/maskeditor/BrushSettingsPanel.vue index 4778d2cff3..37a78b11bd 100644 --- a/src/components/maskeditor/BrushSettingsPanel.vue +++ b/src/components/maskeditor/BrushSettingsPanel.vue @@ -72,12 +72,12 @@ /> @@ -182,6 +182,26 @@ const brushSize = computed({ set: (value: number) => store.setBrushSize(value) }) +const rawSliderValue = ref(null) + +const brushSizeSliderValue = computed({ + get: () => { + if (rawSliderValue.value !== null) { + const cachedSize = Math.round(Math.pow(250, rawSliderValue.value)) + if (cachedSize === brushSize.value) { + return rawSliderValue.value + } + } + + return Math.log(brushSize.value) / Math.log(250) + }, + set: (value: number) => { + rawSliderValue.value = value + const size = Math.round(Math.pow(250, value)) + store.setBrushSize(size) + } +}) + const brushOpacity = computed({ get: () => store.brushSettings.opacity, set: (value: number) => store.setBrushOpacity(value)