Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix caret cursor #212

Merged
merged 10 commits into from
Dec 17, 2024
Merged

fix caret cursor #212

merged 10 commits into from
Dec 17, 2024

Conversation

tomlm
Copy link
Collaborator

@tomlm tomlm commented Dec 16, 2024

#179 Caret is not being drawn by caret control.
For some reason the Line template wasn't being drawn. It never called into DrawingContext. I think it may be because we were drawing a line which had Width and Height but no start point or end point. I verified that Avalonia GUI ignores Lines with just width/height, so this is avalonia behavior.
I changed CaretControl template to output a Rectangle of width/1,height/1 and now it calls, gets rendered correctly.

I changed CaretControl template to output a Rectangle and everything works.
Copy link
Contributor

coderabbitai bot commented Dec 16, 2024

Caution

Review failed

The head commit changed during the review from fb93cf4 to 0c17c13.

📝 Walkthrough
📝 Walkthrough

Walkthrough

This pull request introduces comprehensive changes to the Consolonia framework's caret and drawing functionality. The modifications span multiple files and focus on enhancing caret style management and rendering. A new CaretStyle enumeration is introduced, defining six different caret styles including blinking and steady variants for bar, block, and underline cursors.

The changes include updates to the DrawingContextImpl class to handle the new MoveConsoleCaretToPositionBrush, modifications to the PixelBuffer and RenderTarget classes to support caret style configuration, and additions to the Esc class with new ANSI escape sequences for cursor appearance. The CaretControl and related components have been updated to support the new caret styling mechanism.

Key modifications involve adding a CaretStyle property to various classes, introducing new drawing methods to handle caret positioning, and expanding the framework's ability to control cursor appearance across different contexts. The changes provide more flexible and granular control over caret rendering in the Consolonia framework.

Possibly related PRs

Suggested labels

bug, low priority


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/Consolonia.Core/Drawing/DrawingContextImpl.cs (1)

471-478: Consider refactoring duplicate caret positioning logic.

The implementation for handling MoveConsoleCaretToPositionBrush is duplicated between DrawRectangle and DrawLineInternal. Consider extracting this common logic into a private method.

Apply this diff to reduce code duplication:

+private void DrawCaretPosition(Point position)
+{
+    Point head = position.Transform(Transform);
+    CurrentClip.ExecuteWithClipping(head,
+        () => { _pixelBuffer.Set((PixelBufferCoordinate)head, pixel => pixel.Blend(new Pixel(true))); });
+}

 case MoveConsoleCaretToPositionBrush:
 {
-    Point head = r.TopLeft.Transform(Transform);
-    CurrentClip.ExecuteWithClipping(head,
-        () =>
-        {
-            _pixelBuffer.Set((PixelBufferCoordinate)head, pixel => pixel.Blend(new Pixel(true)));
-        });
+    DrawCaretPosition(r.TopLeft);
     return;
 }

 if (pen.Brush is MoveConsoleCaretToPositionBrush)
 {
-    Point head = line.PStart.Transform(Transform);
-    CurrentClip.ExecuteWithClipping(head,
-        () => { _pixelBuffer.Set((PixelBufferCoordinate)head, pixel => pixel.Blend(new Pixel(true))); });
+    DrawCaretPosition(line.PStart);
     return;
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1034058 and aaf951a.

📒 Files selected for processing (1)
  • src/Consolonia.Core/Drawing/DrawingContextImpl.cs (2 hunks)
🔇 Additional comments (1)
src/Consolonia.Core/Drawing/DrawingContextImpl.cs (1)

264-273: Verify the intentional use of only TopLeft for caret positioning.

The implementation uses only the TopLeft point of the rectangle for caret positioning, ignoring the width and height. While this appears intentional since we only want to move the caret to a single position, please confirm this is the desired behavior.

Run the following script to verify similar implementations:

✅ Verification successful

Based on the search results, I can now generate the final response as I have sufficient evidence about caret positioning behavior across the codebase:

The single-point caret positioning is the correct implementation

The codebase consistently treats the caret as a single-point entity:

  • TextBoxCaret draws the caret using a single position point (Position)
  • IConsole interface and its implementations (DummyConsole, InputLessDefaultNetConsole) handle caret as a single coordinate
  • The caret rendering in RenderTarget also treats it as a single position
  • The CaretControl template uses MoveConsoleCaretToPositionBrush with a single point

Therefore, using only the TopLeft point of the rectangle for caret positioning is the correct approach, as the caret is consistently implemented as a single-point cursor throughout the codebase.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if other caret-related code also uses single-point positioning
# Look for caret-related patterns in the codebase
rg -A 5 "(?i)(caret|cursor).*(position|move|draw)"

Length of output: 21353

@tomlm tomlm marked this pull request as ready for review December 16, 2024 19:15
@tomlm tomlm requested a review from jinek December 16, 2024 19:15
@tomlm tomlm enabled auto-merge (squash) December 16, 2024 19:19
@jinek
Copy link
Owner

jinek commented Dec 16, 2024

Caret does not show up in ComboBoxItem. Behavior observed when opening ComboBox.

Repository owner deleted a comment from goe5mu Dec 16, 2024
@tomlm
Copy link
Collaborator Author

tomlm commented Dec 17, 2024

Hmmm....I can't seem to figure out how to get comboboxItem to show focus.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (4)
src/Consolonia.Core/Drawing/MoveConsoleCaretToPositionBrush.cs (2)

13-15: Fix typo in documentation comment

The word "cursor" is misspelled as "curosr" in the summary.

-        /// style of curosr
+        /// style of cursor

13-16: Consider enhancing XML documentation

While the property addition looks good, consider adding more detailed XML documentation to better describe the property's purpose and default value.

         /// <summary>
-        /// style of cursor
+        /// Gets or sets the cursor style for the caret.
+        /// Default value is CursorStyle.BlinkingBar.
         /// </summary>
src/Consolonia.Core/Drawing/PixelBufferImplementation/PixelBuffer.cs (1)

35-35: Add XML documentation and consider input validation

The property addition looks good but could benefit from the following improvements:

  1. Add XML documentation to describe the property's purpose and default value
  2. Consider adding validation in the setter to ensure only valid cursor styles are assigned
+        /// <summary>
+        /// Gets or sets the cursor style for the pixel buffer.
+        /// Default value is CursorStyle.BlinkingBar.
+        /// </summary>
         public CursorStyle CursorStyle { get; set; } = CursorStyle.BlinkingBar;

Consider adding validation:

-        public CursorStyle CursorStyle { get; set; } = CursorStyle.BlinkingBar;
+        private CursorStyle _cursorStyle = CursorStyle.BlinkingBar;
+        public CursorStyle CursorStyle
+        {
+            get => _cursorStyle;
+            set
+            {
+                if (!Enum.IsDefined(typeof(CursorStyle), value))
+                    throw new ArgumentException($"Invalid cursor style: {value}", nameof(value));
+                _cursorStyle = value;
+            }
+        }
src/Consolonia.Core/Drawing/DrawingContextImpl.cs (1)

Line range hint 760-841: Enhanced text rendering with proper character handling.

The implementation properly handles:

  • Tab characters with consistent spacing
  • Wide characters with proper pixel management
  • Empty pixels and overlapping scenarios

However, consider extracting the tab size to a constant at the class level for better maintainability.

+    private const int TabSize = 8;
     private void DrawStringInternal(IBrush foreground, string text, IGlyphTypeface typeface, Point origin = new())
     {
         // ... existing code ...
         switch (glyph)
         {
             case "\t":
                 {
-                    const int tabSize = 8;
-                    var consolePixel = new Pixel(new SimpleSymbol(' '), foregroundColor);
+                    var consolePixel = new Pixel(new SimpleSymbol(' '), foregroundColor);
-                    for (int j = 0; j < tabSize; j++)
+                    for (int j = 0; j < TabSize; j++)
                     {
                         // ... existing code ...
                     }
-                    currentXPosition += tabSize - 1;
+                    currentXPosition += TabSize - 1;
                 }
                 break;
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between aaf951a and 4b97ba4.

📒 Files selected for processing (7)
  • src/Consolonia.Core/Drawing/CursorStyle.cs (1 hunks)
  • src/Consolonia.Core/Drawing/DrawingContextImpl.cs (7 hunks)
  • src/Consolonia.Core/Drawing/MoveConsoleCaretToPositionBrush.cs (1 hunks)
  • src/Consolonia.Core/Drawing/PixelBufferImplementation/PixelBuffer.cs (1 hunks)
  • src/Consolonia.Core/Drawing/RenderTarget.cs (2 hunks)
  • src/Consolonia.Core/Infrastructure/InputLessDefaultNetConsole.cs (0 hunks)
  • src/Consolonia.Core/Text/Esc.cs (1 hunks)
💤 Files with no reviewable changes (1)
  • src/Consolonia.Core/Infrastructure/InputLessDefaultNetConsole.cs
✅ Files skipped from review due to trivial changes (1)
  • src/Consolonia.Core/Drawing/CursorStyle.cs
🔇 Additional comments (7)
src/Consolonia.Core/Text/Esc.cs (1)

34-40: LGTM! Well-organized cursor shape constants

The new cursor shape constants are correctly implemented using standard VT100 escape sequences and follow the existing code organization pattern.

src/Consolonia.Core/Drawing/RenderTarget.cs (3)

13-14: LGTM: Required imports for text and JSON handling.

The added imports support the new cursor style functionality.


146-156: LGTM: Comprehensive cursor style handling.

The switch expression properly maps each cursor style to its corresponding escape sequence, ensuring proper cursor rendering. The implementation is exhaustive and handles all cursor style cases.


162-162: LGTM: Proper cursor visibility management.

Correctly hides the cursor when no caret position is detected.

src/Consolonia.Core/Drawing/DrawingContextImpl.cs (3)

99-129: Improved bitmap drawing with better quad pixel handling.

The bitmap drawing logic has been enhanced with:

  • Clearer variable naming and comments
  • Proper handling of quad pixels
  • Improved color blending

264-274: LGTM: Proper caret positioning with cursor style support.

The implementation correctly:

  • Transforms the position
  • Sets the cursor style
  • Handles clipping
  • Updates the pixel buffer

472-479: LGTM: Consistent cursor style handling in line drawing.

The implementation properly handles the MoveConsoleCaretToPositionBrush in line drawing context, maintaining consistency with the rectangle drawing implementation.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/Consolonia.Core/Drawing/RenderTarget.cs (1)

145-154: Consider enhancing cursor style handling.

The switch expression effectively maps cursor styles to escape sequences. However, consider these improvements:

  1. Extract the cursor style mapping to a separate method for better separation of concerns.
  2. Add XML documentation explaining the different cursor styles.
  3. Consider providing a default style instead of throwing for unknown values.

Here's a suggested refactor:

+ /// <summary>
+ /// Maps cursor style to the corresponding escape sequence.
+ /// </summary>
+ private string GetCursorStyleEscapeSequence(CursorStyle style) =>
+     style switch
+     {
+         CursorStyle.BlinkingBar => Esc.BlinkingBarCursor,
+         CursorStyle.SteadyBar => Esc.SteadyBarCursor,
+         CursorStyle.BlinkingBlock => Esc.BlinkingBlockCursor,
+         CursorStyle.SteadyBlock => Esc.SteadyBlockCursor,
+         CursorStyle.BlinkingUnderline => Esc.BlinkingUnderlineCursor,
+         CursorStyle.SteadyUnderline => Esc.SteadyUnderlineCursor,
+         _ => Esc.BlinkingBarCursor // Default to blinking bar
+     };

- _console.WriteText(pixelBuffer.CursorStyle switch
- {
-     CursorStyle.BlinkingBar => Esc.BlinkingBarCursor,
-     CursorStyle.SteadyBar => Esc.SteadyBarCursor,
-     CursorStyle.BlinkingBlock => Esc.BlinkingBlockCursor,
-     CursorStyle.SteadyBlock => Esc.SteadyBlockCursor,
-     CursorStyle.BlinkingUnderline => Esc.BlinkingUnderlineCursor,
-     CursorStyle.SteadyUnderline => Esc.SteadyUnderlineCursor,
-     _ => throw new ArgumentOutOfRangeException()
- });
+ _console.WriteText(GetCursorStyleEscapeSequence(pixelBuffer.CursorStyle));
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4b97ba4 and 6bebdd4.

📒 Files selected for processing (2)
  • src/Consolonia.Core/Drawing/RenderTarget.cs (2 hunks)
  • src/Consolonia.Core/Infrastructure/InputLessDefaultNetConsole.cs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/Consolonia.Core/Infrastructure/InputLessDefaultNetConsole.cs
🔇 Additional comments (2)
src/Consolonia.Core/Drawing/RenderTarget.cs (2)

13-13: LGTM!

The added import for Consolonia.Core.Text is necessary for accessing the Esc class containing cursor style escape sequences.


155-155: Verify cursor visibility behavior across different terminal emulators.

The cursor visibility commands are properly synchronized with the CaretVisible property. However, it's important to verify that these escape sequences work consistently across different terminal emulators.

Also applies to: 161-161

✅ Verification successful

Based on the search results, I can now generate the final response:

Cursor visibility handling is implemented consistently across the codebase

The codebase shows a well-structured and consistent implementation of cursor visibility:

  • Cursor commands are properly defined as ANSI escape sequences in Esc.cs
  • The visibility state is tracked via CaretVisible property in the IConsole interface
  • All console implementations (DummyConsole, InputLessDefaultNetConsole, UnitTestConsole) handle the property
  • The RenderTarget class correctly pairs ShowCursor/HideCursor commands with CaretVisible state changes

No terminal-specific handling was found, which is good as these are standard ANSI escape sequences supported by modern terminal emulators.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Search for cursor visibility-related code to ensure consistent handling
# Test: Look for other places where cursor visibility is managed
rg -A 2 "CaretVisible|ShowCursor|HideCursor"

# Test: Look for terminal-specific handling
ast-grep --pattern 'if ($TERM) { $$$ }'

Length of output: 2861

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/Consolonia.Core/Drawing/DrawingContextImpl.cs (1)

264-274: Consider combining the transform and coordinate cast operations.

The implementation correctly handles the caret positioning, but we can slightly optimize the coordinate transformation:

-                        Point head = r.TopLeft.Transform(Transform);
-                        _pixelBuffer.CaretStyle = moveBrush.CaretStyle;
-                        CurrentClip.ExecuteWithClipping(head,
-                            () =>
-                            {
-                                _pixelBuffer.Set((PixelBufferCoordinate)head, pixel => pixel.Blend(new Pixel(true)));
-                            });
+                        var head = (PixelBufferCoordinate)r.TopLeft.Transform(Transform);
+                        _pixelBuffer.CaretStyle = moveBrush.CaretStyle;
+                        CurrentClip.ExecuteWithClipping(head,
+                            () => _pixelBuffer.Set(head, pixel => pixel.Blend(new Pixel(true))));
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6bebdd4 and adb5995.

📒 Files selected for processing (9)
  • src/Consolonia.Core/Drawing/CaretStyle.cs (1 hunks)
  • src/Consolonia.Core/Drawing/DrawingContextImpl.cs (2 hunks)
  • src/Consolonia.Core/Drawing/MoveConsoleCaretToPositionBrush.cs (1 hunks)
  • src/Consolonia.Core/Drawing/PixelBufferImplementation/PixelBuffer.cs (1 hunks)
  • src/Consolonia.Core/Drawing/RenderTarget.cs (2 hunks)
  • src/Consolonia.Core/Infrastructure/InputLessDefaultNetConsole.cs (1 hunks)
  • src/Consolonia.Themes/Templates/Controls/CaretControl.axaml (1 hunks)
  • src/Consolonia.Themes/Templates/Controls/ComboBoxItem.axaml (1 hunks)
  • src/Consolonia.Themes/Templates/Controls/Helpers/CaretControl.cs (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/Consolonia.Core/Drawing/PixelBufferImplementation/PixelBuffer.cs
  • src/Consolonia.Core/Infrastructure/InputLessDefaultNetConsole.cs
  • src/Consolonia.Themes/Templates/Controls/CaretControl.axaml
  • src/Consolonia.Core/Drawing/RenderTarget.cs
🔇 Additional comments (9)
src/Consolonia.Core/Drawing/MoveConsoleCaretToPositionBrush.cs (3)

6-6: Inheritance change enables use of Avalonia styled properties

By inheriting from AvaloniaObject, the class can now define and use StyledPropertys, which is necessary for CaretStyleProperty.


8-9: CaretStyleProperty is correctly registered

The CaretStyleProperty is properly registered using AvaloniaProperty.Register, which is the correct approach for defining styled properties in Avalonia.


14-18: CaretStyle property is properly implemented

The CaretStyle property uses the CaretStyleProperty as its backing store with appropriate getter and setter methods, following Avalonia's property pattern.

src/Consolonia.Core/Drawing/CaretStyle.cs (1)

3-11: CaretStyle enumeration is well-defined

The CaretStyle enum provides clear and descriptive values for different caret styles, facilitating easy extension and maintenance.

src/Consolonia.Themes/Templates/Controls/Helpers/CaretControl.cs (3)

4-4: Added necessary using directive for Consolonia.Core.Drawing

Including using Consolonia.Core.Drawing; allows access to the CaretStyle enum within CaretControl.


13-14: CaretStyleProperty is correctly registered

The CaretStyleProperty is properly registered using AvaloniaProperty.Register, enabling styled property functionality.


22-26: CaretStyle property is properly implemented

The CaretStyle property correctly wraps CaretStyleProperty with getter and setter methods, adhering to Avalonia's conventions.

src/Consolonia.Themes/Templates/Controls/ComboBoxItem.axaml (1)

16-17: Applied CaretStyle to CaretControl in ComboBoxItem template

Setting CaretStyle="BlinkingBlock" enhances the caret visibility within ComboBoxItem, addressing the issue where the caret did not appear when the ComboBox is opened.

src/Consolonia.Core/Drawing/DrawingContextImpl.cs (1)

264-274: Implementation successfully fixes caret rendering.

The changes effectively address the caret rendering issue by:

  1. Using a pixel-based approach instead of lines
  2. Properly handling coordinate transformations
  3. Ensuring consistent caret rendering across different drawing operations

Also applies to: 472-479

@tomlm
Copy link
Collaborator Author

tomlm commented Dec 17, 2024

I figured it out. I need to draw the movebrush AFTER the content is drawn, not before, otherwise the content resets the MoveBrush pixel

Copy link
Owner

@jinek jinek left a comment

Choose a reason for hiding this comment

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

Oh, so zOrder was initial issue. Makes sense.

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.

2 participants