Skip to content

fix: Memory Display Error Issue#184

Merged
deepin-bot[bot] merged 1 commit intolinuxdeepin:masterfrom
JWWTSL:master
Nov 27, 2025
Merged

fix: Memory Display Error Issue#184
deepin-bot[bot] merged 1 commit intolinuxdeepin:masterfrom
JWWTSL:master

Conversation

@JWWTSL
Copy link
Contributor

@JWWTSL JWWTSL commented Nov 27, 2025

log: When the decimal separator is set to a comma and the grouping separator is set to a period, 11.111,04 is displayed in memory as 11,11104.

Summary by Sourcery

Fix incorrect handling of locale-specific decimal and grouping separators when formatting and displaying memory values.

Bug Fixes:

  • Preserve fractional parts correctly when formatting numbers by distinguishing between decimal and grouping symbols based on system locale.
  • Normalize input expressions using system decimal and grouping symbols instead of blindly stripping commas, preventing misinterpretation of localized numbers in memory evaluation.
  • Reformat memory list labels from underlying numeric quantities rather than manipulating already-localized text, avoiding accidental removal of locale decimal separators.

@sourcery-ai
Copy link

sourcery-ai bot commented Nov 27, 2025

Reviewer's Guide

Fixes locale-dependent number formatting and memory display by correctly handling system decimal/grouping separators, avoiding blind removal of commas, and reformatting memory entries from underlying numeric values instead of already-localized text.

Sequence diagram for localized memory evaluation and display

sequenceDiagram
    actor User
    participant InputEdit
    participant Settings
    participant Evaluator
    participant MemoryStore
    participant MemoryWidget
    participant Utils
    participant DMath

    User->>InputEdit: type 11.111,04
    User->>InputEdit: press_memory_recall_or_store

    InputEdit->>Settings: getSystemDecimalSymbol()
    Settings-->>InputEdit: decSym
    InputEdit->>Settings: getSystemDigitGroupingSymbol()
    Settings-->>InputEdit: grpSym

    Note over InputEdit: Replace locale-specific operators, then use decSym/grpSym to normalize expression

    InputEdit->>InputEdit: replace(decSym, placeholder)
    InputEdit->>InputEdit: replace(grpSym, "")
    InputEdit->>InputEdit: replace(placeholder, ".")

    InputEdit->>Evaluator: setExpression(normalized_expression)
    InputEdit->>Evaluator: evalUpdateAns()
    Evaluator-->>InputEdit: Quantity result
    InputEdit->>MemoryStore: save Quantity result

    User->>MemoryWidget: open_memory_view_or_change_separators

    MemoryWidget->>MemoryStore: getList()
    MemoryStore-->>MemoryWidget: list_of_Quantity

    alt programmer_mode
        MemoryWidget->>Utils: formatThousandsSeparatorsPro(programmerResult, base)
        Utils-->>MemoryWidget: formatted_string
    else normal_mode
        MemoryWidget->>DMath: format(Quantity, General_Precision)
        DMath-->>MemoryWidget: result_string
        MemoryWidget->>Utils: formatThousandsSeparators(result_string)
        Utils-->>MemoryWidget: formatted_string
    end

    MemoryWidget->>MemoryWidget: setitemwordwrap(formatted_string, index)
    MemoryWidget->>MemoryWidget: setTextLabel(formatted_string)

    MemoryWidget-->>User: correctly_localized_memory_display
Loading

Updated class diagram for locale-aware formatting and memory display

classDiagram
    class Settings {
        +static Settings instance()
        +QString getSystemDecimalSymbol()
        +QString getSystemDigitGroupingSymbol()
        +bool getSystemDigitGrouping()
        +int programmerBase
    }

    class DSettingsAlt {
        +static DSettingsAlt instance()
        +int getSeparate()
    }

    class Utils {
        +static QString formatThousandsSeparators(QString str)
        +static QString formatThousandsSeparatorsPro(QString str, int base)
    }

    class InputEdit {
        -Evaluator m_evaluator
        +QPair~bool, Quantity~ getMemoryAnswer()
    }

    class MemoryWidget {
        -int m_calculatormode
        -QListWidget m_listwidget
        -MemoryPublic m_memorypublic
        -int m_precision
        +void resetLabelBySeparator()
    }

    class DMath {
        +static QString format(Quantity value, Quantity_Format format)
    }

    class Quantity {
        <<value_object>>
        +Format Format
    }

    Settings <.. InputEdit : uses
    Settings <.. Utils : uses
    DSettingsAlt <.. Utils : uses
    Utils <.. MemoryWidget : uses
    DMath <.. MemoryWidget : uses
    Quantity <.. InputEdit : returns
    Quantity <.. MemoryWidget : uses
    MemoryWidget <.. MemoryPublic : has
    InputEdit <.. Evaluator : has
Loading

File-Level Changes

Change Details Files
Improve thousands separator formatting to respect system decimal symbol and avoid corrupting fractional parts.
  • Remove debug logging from formatThousandsSeparators.
  • Detect decimal point using system decimal symbol first, then fall back to '.'.
  • Split integer and fractional parts using the detected decimal symbol and preserve the fractional substring untouched.
  • Clean only the integer part of any existing grouping symbols, including '.', ',', full-width comma, spaces, and the system grouping symbol before re-grouping.
src/utils.cpp
Normalize user expressions using system decimal and grouping symbols instead of deleting commas outright.
  • Stop globally removing commas from the input expression string.
  • Fetch system decimal and grouping symbols from Settings.
  • Temporarily replace the system decimal symbol with a placeholder, strip grouping symbols, then convert the placeholder back to '.' for evaluation.
src/widgets/inputedit.cpp
Rebuild memory display strings from stored quantities with proper localization instead of mutating displayed text.
  • Replace logic that removed commas from the existing textLabel then re-formatted it.
  • For each memory item, recompute the display string from the underlying Quantity (or programmer result) and run it through the appropriate thousands separator formatter.
  • Apply word wrapping to the newly formatted string before setting it on the MemoryItemWidget label.
src/views/memorywidget.cpp

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey there - I've reviewed your changes - here's some feedback:

  • In InputEdit::getMemoryAnswer, consider avoiding the magic placeholder character QChar(0x1D) by defining a named constant (or using a less obscure sentinel) and adding a brief note about why it's guaranteed not to appear in user input, to reduce the risk of accidental collisions or future regressions.
  • In MemoryWidget::resetLabelBySeparator, you repeatedly call Settings::instance() and recompute formatting settings inside the loop; caching the relevant settings (and possibly the base) before the loop would simplify the code and avoid redundant lookups.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `InputEdit::getMemoryAnswer`, consider avoiding the magic placeholder character `QChar(0x1D)` by defining a named constant (or using a less obscure sentinel) and adding a brief note about why it's guaranteed not to appear in user input, to reduce the risk of accidental collisions or future regressions.
- In `MemoryWidget::resetLabelBySeparator`, you repeatedly call `Settings::instance()` and recompute formatting settings inside the loop; caching the relevant settings (and possibly the base) before the loop would simplify the code and avoid redundant lookups.

## Individual Comments

### Comment 1
<location> `src/utils.cpp:105-115` </location>
<code_context>
+            fractional = input.mid(decIdx + 1);
+            integerPart = input.left(decIdx);
+        }
+        // 仅清理整数部分中可能已存在的分组符号('.'、','、全角','、空格、系统分组符),小数部分完全保持原样
+        const QChar fullWidthComma(0xFF0C);
+        if (!grpSym.isEmpty())
+            integerPart.remove(grpSym);
+        integerPart.remove(QLatin1Char('.'));
+        integerPart.remove(QLatin1Char(','));
+        integerPart.remove(fullWidthComma);
+        integerPart.remove(QLatin1Char(' '));
+    }
+    
</code_context>

<issue_to_address>
**suggestion:** Consider broadening grouping cleanup to handle other common spacing/grouping characters.

Currently this only removes '.', ',', full-width comma, plain space, and the system grouping symbol. In some locales, pasted numbers may include non‑breaking space (U+00A0), thin space (U+2009), narrow no‑break space, etc. as group separators.

Consider either:
- Normalizing all Unicode spaces in the integer part to plain spaces, then removing them, or
- Explicitly removing a small set of additional common grouping characters (NBSP, thin space, narrow no‑break space).

This will make parsing more robust for copy/paste and mixed‑locale inputs.

```suggestion
        }
+        // 仅清理整数部分中可能已存在的分组符号('.'、','、全角','、各种空格、系统分组符),小数部分完全保持原样
+        const QChar fullWidthComma(0xFF0C);
+        const QChar nonBreakingSpace(0x00A0);      // NBSP
+        const QChar thinSpace(0x2009);            // THIN SPACE
+        const QChar narrowNoBreakSpace(0x202F);   // NARROW NO-BREAK SPACE
+
+        if (!grpSym.isEmpty())
+            integerPart.remove(grpSym);
+        integerPart.remove(QLatin1Char('.'));
+        integerPart.remove(QLatin1Char(','));
+        integerPart.remove(fullWidthComma);
+        integerPart.remove(QLatin1Char(' '));
+        integerPart.remove(nonBreakingSpace);
+        integerPart.remove(thinSpace);
+        integerPart.remove(narrowNoBreakSpace);
+    }
+    
```
</issue_to_address>

### Comment 2
<location> `src/widgets/inputedit.cpp:1138` </location>
<code_context>
+    const auto sys = Settings::instance();
+    const QString decSym = sys->getSystemDecimalSymbol();
+    const QString grpSym = sys->getSystemDigitGroupingSymbol();
+    const QString decimalPlaceholder = QString(QChar(0x1D));
+    
+    if (!decSym.isEmpty() && decSym != QLatin1String("."))
</code_context>

<issue_to_address>
**suggestion:** Using U+001D as a decimal placeholder risks clashing with existing content.

Using a control character as the placeholder could still collide with rare but valid input (e.g. pasted content that already contains U+001D), causing subtle data corruption.

To minimize this risk, prefer either:
- A private-use code point (e.g. U+E000), or
- A longer, clearly synthetic sentinel string (e.g. "__DEC_PLACEHOLDER__").

Both options greatly reduce the chance of overlapping with real user input.

```suggestion
    // Use a synthetic sentinel string instead of a control character to minimize
    // the risk of collisions with pasted or user-entered content.
    const QString decimalPlaceholder = QStringLiteral("__DEC_PLACEHOLDER__");
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@JWWTSL JWWTSL force-pushed the master branch 2 times, most recently from f59e11a to 29e06b9 Compare November 27, 2025 08:38
log: When the decimal separator is set to a comma and the grouping separator is set to a period, `11.111,04` is displayed in memory as `11,11104`.

bug: https://pms.uniontech.com/bug-view-342051.html
@deepin-ci-robot
Copy link

deepin pr auto review

我来对这段代码的修改进行审查,主要从语法逻辑、代码质量、性能和安全几个方面进行分析:

  1. 语法逻辑:
  • 代码修改主要解决了本地化数字格式化的问题,特别是小数点和千位分隔符的处理
  • 新增的逻辑考虑了系统小数符和千位分隔符的不同情况,处理更加严谨
  • 使用了更安全的占位符替换方式,避免了特殊字符冲突
  1. 代码质量:
  • 优点:
    • 代码注释详细,解释了修改的原因和目的
    • 使用了QLatin1String和QStringLiteral等Qt推荐的字符串处理方式
    • 代码结构清晰,逻辑分层合理
  • 建议:
    • 可以考虑将重复的占位符字符串定义为一个常量
    • 部分代码块可以抽取为独立函数,提高复用性
  1. 代码性能:
  • 使用QStringLiteral而不是从QChar构造字符串,性能更好
  • lastIndexOf比indexOf更适合查找最后一个分隔符
  • remove操作链式调用,减少了中间字符串的创建
  1. 代码安全:
  • 使用更安全的占位符字符串"DEC_PLACEHOLDER"替代了特殊字符
  • 增加了对系统小数符和千位分隔符的严格检查
  • 避免了直接删除逗号可能导致的数值解析错误

具体改进建议:

  1. 建议在类中定义一个常量成员:
static constexpr const char* DECIMAL_PLACEHOLDER = "__DEC_PLACEHOLDER__";
  1. 可以将分隔符清理逻辑抽取为独立函数:
QString cleanSeparators(const QString& input, const QString& decSym, const QString& grpSym) {
    QString result = input;
    const QChar fullWidthComma(0xFF0C);
    if (!grpSym.isEmpty())
        result.remove(grpSym);
    result.remove(QLatin1Char('.'));
    result.remove(QLatin1Char(','));
    result.remove(fullWidthComma);
    result.remove(QLatin1Char(' '));
    return result;
}
  1. 在formatExpression函数中,建议使用QStringBuilder优化字符串拼接:
using namespace QtLiterals;
expression = symbolComplement(expressionText())
           % QString::fromUtf8("").replace("+")
           % QString::fromUtf8("").replace("-")
           % QString::fromUtf8("×").replace("*")
           % QString::fromUtf8("÷").replace("/");
  1. 建议增加输入验证:
if (decSym == grpSym) {
    qWarning() << "Decimal symbol and grouping symbol cannot be the same";
    return {};
}

总体来说,这次修改显著提升了代码的健壮性和本地化支持,主要改进点合理且必要。代码质量良好,但仍有小幅优化空间。

@deepin-ci-robot
Copy link

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: JWWTSL, lzwind

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@JWWTSL
Copy link
Contributor Author

JWWTSL commented Nov 27, 2025

/forcemerge

@deepin-bot
Copy link
Contributor

deepin-bot bot commented Nov 27, 2025

This pr force merged! (status: unstable)

@deepin-bot deepin-bot bot merged commit 9922c58 into linuxdeepin:master Nov 27, 2025
15 of 18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants