{"id":50426428,"url":"https://github.com/mbaumsti/vclrotatededit","last_synced_at":"2026-05-31T11:01:10.600Z","repository":{"id":360687935,"uuid":"1249090327","full_name":"mbaumsti/VclRotatedEdit","owner":"mbaumsti","description":"Single-line VCL edit control for Delphi that supports horizontal, vertical and arbitrary-angle text editing, with caret, selection, clipboard, validation, VCL styles and design-time resizing.","archived":false,"fork":false,"pushed_at":"2026-05-27T12:08:06.000Z","size":156,"stargazers_count":1,"open_issues_count":1,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-05-27T14:09:00.502Z","etag":null,"topics":["component","custom-control","design-time","edit-control","object-pascal","oriented","pascal","rotated-text","text-editing","vcl","vcl-styles","vertical-text","windows"],"latest_commit_sha":null,"homepage":"","language":"Pascal","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mbaumsti.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-25T10:43:37.000Z","updated_at":"2026-05-27T12:06:30.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/mbaumsti/VclRotatedEdit","commit_stats":null,"previous_names":["mbaumsti/vclrotatededit"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/mbaumsti/VclRotatedEdit","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mbaumsti%2FVclRotatedEdit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mbaumsti%2FVclRotatedEdit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mbaumsti%2FVclRotatedEdit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mbaumsti%2FVclRotatedEdit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mbaumsti","download_url":"https://codeload.github.com/mbaumsti/VclRotatedEdit/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mbaumsti%2FVclRotatedEdit/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33728391,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-31T02:00:06.040Z","response_time":95,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["component","custom-control","design-time","edit-control","object-pascal","oriented","pascal","rotated-text","text-editing","vcl","vcl-styles","vertical-text","windows"],"created_at":"2026-05-31T11:01:09.808Z","updated_at":"2026-05-31T11:01:10.591Z","avatar_url":"https://github.com/mbaumsti.png","language":"Pascal","funding_links":[],"categories":[],"sub_categories":[],"readme":"# VclRotatedEdit\n\n`VclRotatedEdit` is a single-line VCL edit control that can render and edit text horizontally, vertically, or at an arbitrary angle while keeping normal edit-control behavior such as caret movement, selection, clipboard operations, validation, VCL styles and design-time resizing.\n\nCopyright (c) 2026 Marc BAUMSTIMLER  \nRepository: https://github.com/mbaumsti/VclRotatedEdit  \nLicense: see the `LICENSE` file.\n\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"docs/images/TRotatedEdit.png\" alt=\"TRotatedEdit orientation examples\" /\u003e\n\u003c/p\u003e\n\n---\n\n## Purpose\n\nA standard `TEdit` is axis-aligned. `TRotatedEdit` keeps the text-editing model canonical and horizontal internally, then projects the edit surface to the requested angle for rendering, hit-testing, caret drawing and selection.\n\nThe component is designed for VCL applications that need editable labels, compact rotated fields, vertical editing surfaces, or custom-angle text input without switching to a fully custom editor framework.\n\n---\n\n## Main features\n\n- Single-line editable text control.\n- Horizontal, vertical-down, vertical-up and custom-angle orientations.\n- Arbitrary angle rendering through the `Angle` property.\n- Stable logical dimensions through `LogicalLength` and `LogicalThickness`.\n- Native-like `AutoSize` support for the logical thickness of a single-line edit surface.\n- Caret, selection and mouse hit-testing aligned with the rotated text flow.\n- Clipboard and selection support: copy, cut, paste, select all, selected-text replacement and clear selection.\n- `ReadOnly`, `MaxLength`, `CharCase`, `NumbersOnly`, `TextHint` and alignment support.\n- `RenderBackend` property for selecting the rendering backend. `rebDirect2D` is the default renderer and uses Direct2D / DirectWrite when native resources are available; `rebGDI` keeps the historical GDI renderer available for compatibility and fallback.\n- VCL style-aware rendering through `PaletteMode` and `StyleElements`.\n- Style-derived border metrics so the editable area follows the active VCL style instead of assuming a fixed one-pixel border.\n- Design-time selection markers for shaped/rotated controls.\n- Protected design-time resizing: one drag changes either logical length or logical thickness, never both.\n- Protection against invalid design-time resize rectangles.\n- Design-time drag repaint protection over container controls such as `TGroupBox`.\n- GDI fallback renderer corrected for arbitrary-angle text rendering without rotating ClearType-rasterized text bitmaps.\n- Rendering backend boundary prepared so a future Direct2D/DirectWrite backend can own both rendering and text metrics.\n- Event set close to what developers expect from an edit control, plus specific editing events.\n\n---\n\n## Important concepts\n\n### Physical bounds\n\n`Left`, `Top`, `Width` and `Height` are the normal VCL rectangular bounds. They describe the external Windows control rectangle.\n\n### Logical size\n\n`LogicalLength` and `LogicalThickness` describe the editable surface before rotation.\n\n- `LogicalLength`: dimension along the text flow axis.\n- `LogicalThickness`: dimension perpendicular to the text flow axis.\n\nFor a rotated control, `Width` and `Height` are not the editable dimensions. They are only the external box that contains the rotated logical edit surface.\n\n### AutoSize and logical thickness\n\n`AutoSize` follows the naming convention of `TEdit`, but it acts on the logical edit surface.\n\n- When `AutoSize = True`, the component updates `LogicalThickness` to a native-like single-line edit height for the current font, style and border.\n- When `AutoSize = False`, the user may set a smaller or larger `LogicalThickness`.\n- If the logical thickness is smaller than the native-like value, the normal top margin is preserved and the lower part is clipped, matching the behavior expected from a single-line edit control.\n- If the logical thickness is larger than the native-like value, the text band is visually re-centered inside the enlarged editable area.\n\nText vertical placement is intentionally based on the visible `W` glyph band for better visual centering, while caret, selection and hit-testing remain aligned through the same layout result.\n\n### Orientation and angle\n\n`Orientation` is a convenience property for common modes:\n\n- `reoHorizontal`\n- `reoVerticalDown`\n- `reoVerticalUp`\n- `reoCustomAngle`\n\n`Angle` is the effective rotation angle in degrees. When `Orientation = reoCustomAngle`, `Angle` is directly controlled by the user.\n\nValidated convention:\n\n- `reoVerticalDown` means visual text flow from top to bottom and maps to `270°`.\n- `reoVerticalUp` means visual text flow from bottom to top and maps to `90°`.\n\nThis convention must remain centralized in the geometry unit so rendering, caret, selection, hit-testing and layout stay coherent.\n\n---\n\n## Design-time behavior\n\nThe component intentionally separates the external VCL rectangle from the internal editable surface.\n\nRules:\n\n- Loading a DFM must preserve the streamed external bounds.\n- Changing `LogicalLength` or `LogicalThickness` recalculates the external bounds.\n- Changing `Angle` or `Orientation` recalculates the external bounds while keeping the external center stable.\n- During a design-time resize drag, only one logical axis changes:\n  - either `LogicalLength`,\n  - or `LogicalThickness`,\n  - never both.\n- If a design-time resize would produce an invalid or too-small rectangle, the component restores the last valid geometry.\n- During design-time dragging, the control avoids repainting the full parent surface into its own background, so containers such as `TGroupBox` do not leak their border or caption into the rotated edit background.\n\nThese rules prevent the component from jumping or drifting when switching between form view and text DFM view.\n\n---\n\n## Style support\n\n`PaletteMode` selects how rendering colors are resolved:\n\n- `repmStyle`: use the resolved VCL style palette when possible.\n- `repmCustom`: use `Color`, `Font.Color` and `BorderColor`.\n\nAt runtime, the component keeps the style-resolution behavior that matches application styles. At design time, it uses the parent/control style context so the owner-drawn surface follows the style shown by the form designer.\n\n`StyleElements` is honored:\n\n- `seClient`: style controls the edit background.\n- `seFont`: style controls the text color.\n- `seBorder`: style controls the border color.\n\n---\n\n## Public properties\n\nCommon edit-style properties:\n\n- `Text`\n- `ReadOnly`\n- `MaxLength`\n- `Alignment`\n- `CharCase`\n- `TextHint`\n- `NumbersOnly`\n- `BorderStyle`\n- `BorderColor`\n- `TextPaddingStart`\n- `TextPaddingEnd`\n\nRotated-edit specific properties:\n\n- `Orientation`\n- `Angle`\n- `LogicalLength`\n- `LogicalThickness`\n- `AutoSize`: automatically maintains a native-like logical thickness for the current font/style.\n- `AutoSizeBounds`\n- `PaletteMode`\n- `ShowDesignMarkers`\n\nRuntime selection state:\n\n- `CaretIndex`\n- `SelStart`\n- `SelLength`\n- `SelText`: selected text. Assigning this property replaces the current selection or inserts text at the caret position.\n- `ScrollOffset`\n\n`Cursor` is intentionally hidden from the Object Inspector because the component uses an orientation-aware custom hover cursor. The inherited runtime property still exists as part of `TControl`, but it is not a meaningful design-time setting for `TRotatedEdit`.\n\n---\n\n## Public methods\n\nCommon edit-style methods:\n\n- `Clear`: clears the whole text.\n- `ClearSelection`: removes the current selection without using the clipboard.\n- `SelectAll`: selects the whole text.\n- `CopyToClipboard`: copies the current selection to the clipboard.\n- `CutToClipboard`: cuts the current selection to the clipboard when the control is not read-only.\n- `PasteFromClipboard`: inserts clipboard text at the caret position or replaces the current selection.\n- `AutoSizeToLogicalBounds`: recalculates the external bounds from the current logical size and angle.\n\n---\n\n## Events\n\nStandard-style events include:\n\n- `OnChange`\n- `OnClick`\n- `OnContextPopup`\n- `OnDblClick`\n- `OnEnter`\n- `OnExit`\n- `OnKeyDown`\n- `OnKeyPress`\n- `OnKeyUp`\n- `OnMouseDown`\n- `OnMouseEnter`\n- `OnMouseLeave`\n- `OnMouseMove`\n- `OnMouseUp`\n- `OnMouseWheel`\n- `OnMouseWheelDown`\n- `OnMouseWheelUp`\n\nSpecific events:\n\n- `OnSelectionChange`: fired when `CaretIndex`, `SelStart` or `SelLength` changes.\n- `OnEditingStart`: fired once when the first accepted user text mutation starts an editing session.\n- `OnCanChange`: allows a normalized candidate text value to be accepted or rejected before it becomes `Text`.\n- `OnValidate`: validates the final value when editing is completed.\n- `OnEditingDone`: reports why editing ended.\n\n---\n\n## Basic usage\n\n```pascal\nuses\n    VclRotatedEdit,\n    VclRotatedEdit_Types;\n\nprocedure TForm1.FormCreate(Sender: TObject);\nbegin\n    RotatedEdit1.Text := 'Vertical text';\n    RotatedEdit1.Orientation := reoVerticalDown;\n    RotatedEdit1.LogicalLength := 160;\n    RotatedEdit1.LogicalThickness := 28;\nend;\n```\n\nCustom angle:\n\n```pascal\nprocedure TForm1.FormCreate(Sender: TObject);\nbegin\n    RotatedEdit1.Orientation := reoCustomAngle;\n    RotatedEdit1.Angle := 340;\n    RotatedEdit1.Text := 'Custom angle';\nend;\n```\n\nImmediate change filtering:\n\n```pascal\nprocedure TForm1.RotatedEdit1CanChange(\n    Sender: TObject;\n    const AOldText, ANewText: string;\n    var ACanChange: Boolean);\nbegin\n    ACanChange := Pos('!', ANewText) = 0;\nend;\n```\n\nSelected-text replacement:\n\n```pascal\nprocedure TForm1.ReplaceSelectionButtonClick(Sender: TObject);\nbegin\n    RotatedEdit1.SelText := 'replacement';\nend;\n```\n\nClear the current selection without using the clipboard:\n\n```pascal\nprocedure TForm1.ClearSelectionButtonClick(Sender: TObject);\nbegin\n    RotatedEdit1.ClearSelection;\nend;\n```\n\n---\n\n## Source layout\n\n- `VclRotatedEdit.pas`: public component facade.\n- `VclRotatedEdit_Core.pas`: component state, published API, input handling and design-time resize policy.\n- `VclRotatedEdit_Types.pas`: shared types and event signatures.\n- `VclRotatedEdit_Geometry.pas`: projection and angle helpers.\n- `VclRotatedEdit_Layout.pas`: canonical text/caret/selection layout.\n- `VclRotatedEdit_Render.pas`: historical GDI owner-drawn rendering pipeline.\n- `VclRotatedEdit_RenderBackend.pas`: common rendering and text-metric backend contract plus backend factory.\n- `VclRotatedEdit_RenderBackend_GDI.pas`: historical GDI backend implementation.\n- `VclRotatedEdit_RenderBackend_Direct2D.pas`: Direct2D/DirectWrite backend implementation with a private GDI fallback for native-resource or drawing failures.\n- `VclRotatedEdit_Style.pas`: style, color and border-metric resolution.\n- `VclRotatedEdit_EditEngine.pas`: pure text mutation engine.\n- `VclRotatedEdit_Caret.pas`: caret blink controller.\n- `VclRotatedEdit_Clipboard.pas`: clipboard helpers.\n- `VclRotatedEdit_Design.pas`: design-time selection support.\n- `VclRotatedEdit_Reg.pas`: design-time registration.\n\n---\n\n## PasDoc\n\nThe source files contain unit headers and declaration comments intended to be consumable by PasDoc.\n\nThe repository includes PasDoc helper files at the root level:\n\n- `pasdoc-complete.cfg`: configuration file for the complete API documentation;\n- `pasdoc-sources-complete.txt`: explicit list of source units to document;\n- `pasdoc.txt`: short notes and command examples.\n\nThe PasDoc introduction text is stored in:\n\n```text\ndocs/api-complete-introduction.txt\n```\n\nTypical command from the repository root:\n\n```text\npasdoc @pasdoc-complete.cfg\n```\n\nThe generated API documentation is written to:\n\n```text\ndocs/api-complete\n```\n\nThe configuration intentionally documents only the public component sources from `Src`. It does not include:\n\n- Delphi history folders such as `__history`;\n- backup/copy files such as `* - Copie.pas`;\n- demo sources;\n- generated binary files from `Lib`.\n\nIf your PasDoc version does not support `@` response files, use the fallback command documented in `pasdoc.txt`.\n\n---\n\n## Current development state\n\nThe source tree currently contains two rendering backends:\n\n- `rebDirect2D`: the default Direct2D / DirectWrite renderer. It draws in the final oriented coordinate system and avoids projecting an already-rasterized straight bitmap.\n- `rebGDI`: the historical GDI renderer, kept as an explicit compatibility backend and as the fallback path used when Direct2D cannot be used.\n\nThe Direct2D backend owns the native Direct2D factory, DirectWrite factory and DC render target. It draws the background, frame, selection, text and caret without using a straight bitmap that is rotated afterwards. This rule is intentional: rendering directly in the final coordinate system avoids the edge artefacts caused by projecting already-rasterized GDI bitmaps.\n\nThe Direct2D renderer is the recommended/default path for normal use. The GDI renderer remains useful for compatibility, troubleshooting and as a safety fallback. The Direct2D backend delegates to it when native resources are unavailable or when a Direct2D operation fails.\n\nThe demo event monitor stores event counters in memory and coalesces visual grid refreshes to avoid flicker while keeping all event counts exact.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmbaumsti%2Fvclrotatededit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmbaumsti%2Fvclrotatededit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmbaumsti%2Fvclrotatededit/lists"}