├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ ├── custom.md
│ └── feature_request.md
├── dependabot.yml
└── workflows
│ ├── codeql-analysis.yml
│ └── gradle.yml
├── .gitignore
├── LICENSE
├── README.md
├── build.gradle
├── core.backend.lwjgl.nanovg
├── build.gradle
└── src
│ └── main
│ └── java
│ ├── com
│ └── spinyowl
│ │ └── spinygui
│ │ └── core
│ │ └── backend
│ │ └── renderer
│ │ └── lwjgl
│ │ └── nanovg
│ │ ├── NvgBorderRenderer.java
│ │ ├── NvgElementRenderer.java
│ │ ├── NvgRenderer.java
│ │ ├── NvgTextRenderer.java
│ │ └── util
│ │ ├── NvgColorUtil.java
│ │ ├── NvgRenderUtils.java
│ │ └── NvgShapes.java
│ └── module-info.java
├── core.backend
├── build.gradle
└── src
│ └── main
│ └── java
│ ├── com
│ └── spinyowl
│ │ └── spinygui
│ │ └── core
│ │ └── backend
│ │ └── renderer
│ │ └── Renderer.java
│ └── module-info.java
├── core
├── .gitignore
├── build.gradle
└── src
│ ├── main
│ ├── antlr
│ │ └── com
│ │ │ └── spinyowl
│ │ │ └── spinygui
│ │ │ └── core
│ │ │ └── parser
│ │ │ └── impl
│ │ │ └── css
│ │ │ └── antlr
│ │ │ └── CSS3.g4
│ ├── java
│ │ ├── com
│ │ │ └── spinyowl
│ │ │ │ └── spinygui
│ │ │ │ └── core
│ │ │ │ ├── Configuration.java
│ │ │ │ ├── animation
│ │ │ │ ├── Animation.java
│ │ │ │ ├── Animator.java
│ │ │ │ └── AnimatorImpl.java
│ │ │ │ ├── clipboard
│ │ │ │ └── Clipboard.java
│ │ │ │ ├── cursor
│ │ │ │ ├── Cursor.java
│ │ │ │ ├── CursorMod.java
│ │ │ │ └── CursorService.java
│ │ │ │ ├── event
│ │ │ │ ├── .gitkeep
│ │ │ │ ├── ChangePositionEvent.java
│ │ │ │ ├── ChangeSizeEvent.java
│ │ │ │ ├── ChangeTextEvent.java
│ │ │ │ ├── CharEvent.java
│ │ │ │ ├── CursorEnterEvent.java
│ │ │ │ ├── CursorExitEvent.java
│ │ │ │ ├── Event.java
│ │ │ │ ├── EventTarget.java
│ │ │ │ ├── FileDropEvent.java
│ │ │ │ ├── FocusInEvent.java
│ │ │ │ ├── FocusOutEvent.java
│ │ │ │ ├── KeyboardEvent.java
│ │ │ │ ├── MouseClickEvent.java
│ │ │ │ ├── MouseDragEvent.java
│ │ │ │ ├── ScrollEvent.java
│ │ │ │ ├── WindowCloseEvent.java
│ │ │ │ ├── WindowFocusEvent.java
│ │ │ │ ├── WindowIconifyEvent.java
│ │ │ │ ├── WindowPosEvent.java
│ │ │ │ ├── WindowRefreshEvent.java
│ │ │ │ ├── WindowSizeEvent.java
│ │ │ │ ├── listener
│ │ │ │ │ └── EventListener.java
│ │ │ │ └── processor
│ │ │ │ │ ├── DefaultEventProcessor.java
│ │ │ │ │ └── EventProcessor.java
│ │ │ │ ├── font
│ │ │ │ ├── Font.java
│ │ │ │ ├── FontSize.java
│ │ │ │ ├── FontStretch.java
│ │ │ │ ├── FontStyle.java
│ │ │ │ └── FontWeight.java
│ │ │ │ ├── image
│ │ │ │ └── Image.java
│ │ │ │ ├── input
│ │ │ │ ├── KeyAction.java
│ │ │ │ ├── KeyCode.java
│ │ │ │ ├── KeyMod.java
│ │ │ │ ├── Keyboard.java
│ │ │ │ ├── KeyboardKey.java
│ │ │ │ ├── KeyboardLayout.java
│ │ │ │ ├── MouseButton.java
│ │ │ │ ├── MouseService.java
│ │ │ │ ├── Shortcut.java
│ │ │ │ ├── ShortcutRegistry.java
│ │ │ │ └── impl
│ │ │ │ │ ├── KeyboardLayoutImpl.java
│ │ │ │ │ ├── MouseServiceImpl.java
│ │ │ │ │ └── ShortcutRegistryImpl.java
│ │ │ │ ├── layout
│ │ │ │ ├── .gitkeep
│ │ │ │ ├── ElementLayout.java
│ │ │ │ ├── Layout.java
│ │ │ │ ├── LayoutContext.java
│ │ │ │ ├── LayoutService.java
│ │ │ │ ├── TextLayout.java
│ │ │ │ └── impl
│ │ │ │ │ ├── BlockLayout.java
│ │ │ │ │ ├── FlexLayout.java
│ │ │ │ │ ├── LayoutServiceImpl.java
│ │ │ │ │ ├── LayoutServiceProvider.java
│ │ │ │ │ ├── LayoutUtils.java
│ │ │ │ │ ├── NoneLayout.java
│ │ │ │ │ └── TextLayoutImpl.java
│ │ │ │ ├── node
│ │ │ │ ├── Element.java
│ │ │ │ ├── EmptyElement.java
│ │ │ │ ├── Frame.java
│ │ │ │ ├── Node.java
│ │ │ │ ├── NodeBuilder.java
│ │ │ │ ├── Text.java
│ │ │ │ ├── intersection
│ │ │ │ │ ├── Intersection.java
│ │ │ │ │ ├── Intersections.java
│ │ │ │ │ └── RectangleIntersection.java
│ │ │ │ └── layout
│ │ │ │ │ ├── Box.java
│ │ │ │ │ ├── Edges.java
│ │ │ │ │ └── Rect.java
│ │ │ │ ├── parser
│ │ │ │ ├── NodeParser.java
│ │ │ │ ├── StyleSheetParser.java
│ │ │ │ └── impl
│ │ │ │ │ ├── DefaultNodeParser.java
│ │ │ │ │ ├── DefaultStyleSheetParser.java
│ │ │ │ │ ├── ParseException.java
│ │ │ │ │ ├── StyleSheetParserFactory.java
│ │ │ │ │ └── css
│ │ │ │ │ ├── antlr
│ │ │ │ │ ├── CSS3.interp
│ │ │ │ │ ├── CSS3.tokens
│ │ │ │ │ ├── CSS3BaseListener.java
│ │ │ │ │ ├── CSS3BaseVisitor.java
│ │ │ │ │ ├── CSS3Lexer.interp
│ │ │ │ │ ├── CSS3Lexer.java
│ │ │ │ │ ├── CSS3Lexer.tokens
│ │ │ │ │ ├── CSS3Listener.java
│ │ │ │ │ ├── CSS3Parser.java
│ │ │ │ │ └── CSS3Visitor.java
│ │ │ │ │ └── visitor
│ │ │ │ │ ├── AtRuleVisitor.java
│ │ │ │ │ ├── DeclarationListVisitor.java
│ │ │ │ │ ├── DeclarationVisitor.java
│ │ │ │ │ ├── PropertyValueVisitor.java
│ │ │ │ │ ├── RulesetVisitor.java
│ │ │ │ │ ├── SelectorGroupVisitor.java
│ │ │ │ │ ├── SelectorVisitor.java
│ │ │ │ │ └── StyleSheetVisitor.java
│ │ │ │ ├── style
│ │ │ │ ├── ResolvedStyle.java
│ │ │ │ ├── manager
│ │ │ │ │ ├── StyleManager.java
│ │ │ │ │ └── StyleManagerImpl.java
│ │ │ │ ├── stylesheet
│ │ │ │ │ ├── AtRule.java
│ │ │ │ │ ├── Declaration.java
│ │ │ │ │ ├── Properties.java
│ │ │ │ │ ├── PropertiesScanner.java
│ │ │ │ │ ├── Property.java
│ │ │ │ │ ├── PropertyProvider.java
│ │ │ │ │ ├── PropertyStore.java
│ │ │ │ │ ├── PropertyStoreProvider.java
│ │ │ │ │ ├── Ruleset.java
│ │ │ │ │ ├── Specificity.java
│ │ │ │ │ ├── StyleSheet.java
│ │ │ │ │ ├── Term.java
│ │ │ │ │ ├── annotation
│ │ │ │ │ │ └── Priority.java
│ │ │ │ │ ├── atrule
│ │ │ │ │ │ └── FontFaceRule.java
│ │ │ │ │ ├── impl
│ │ │ │ │ │ ├── DefaultPropertyStore.java
│ │ │ │ │ │ └── DefaultPropertyStoreProvider.java
│ │ │ │ │ ├── property
│ │ │ │ │ │ ├── BackgroundPropertyProvider.java
│ │ │ │ │ │ ├── BorderColorPropertyProvider.java
│ │ │ │ │ │ ├── BorderPropertyProvider.java
│ │ │ │ │ │ ├── BorderRadiusPropertyProvider.java
│ │ │ │ │ │ ├── BorderStylePropertyProvider.java
│ │ │ │ │ │ ├── BorderWidthPropertyProvider.java
│ │ │ │ │ │ ├── BoxShadowPropertyProvider.java
│ │ │ │ │ │ ├── ColorPropertyProvider.java
│ │ │ │ │ │ ├── DimensionPropertyProvider.java
│ │ │ │ │ │ ├── DisplayPropertyProvider.java
│ │ │ │ │ │ ├── FlexPropertyProvider.java
│ │ │ │ │ │ ├── FontPropertyProvider.java
│ │ │ │ │ │ ├── MarginPropertyProvider.java
│ │ │ │ │ │ ├── OpacityPropertyProvider.java
│ │ │ │ │ │ ├── PaddingPropertyProvider.java
│ │ │ │ │ │ ├── PointerEventsPropertyProvider.java
│ │ │ │ │ │ ├── PositionPropertyProvider.java
│ │ │ │ │ │ ├── PositioningPropertyProvider.java
│ │ │ │ │ │ ├── TabSizePropertyProvider.java
│ │ │ │ │ │ ├── WhiteSpacePropertyProvider.java
│ │ │ │ │ │ └── ZIndexPropertyProvider.java
│ │ │ │ │ ├── selector
│ │ │ │ │ │ ├── CombinatorSelector.java
│ │ │ │ │ │ ├── PseudoClassSelector.java
│ │ │ │ │ │ ├── PseudoElementSelector.java
│ │ │ │ │ │ ├── Selector.java
│ │ │ │ │ │ ├── combinator
│ │ │ │ │ │ │ ├── AdjacentSiblingSelector.java
│ │ │ │ │ │ │ ├── AndSelector.java
│ │ │ │ │ │ │ ├── ChildSelector.java
│ │ │ │ │ │ │ ├── DescendantSelector.java
│ │ │ │ │ │ │ └── GeneralSiblingSelector.java
│ │ │ │ │ │ ├── pseudoclass
│ │ │ │ │ │ │ └── HoverSelector.java
│ │ │ │ │ │ ├── pseudoelement
│ │ │ │ │ │ │ ├── AfterSelector.java
│ │ │ │ │ │ │ ├── BeforeSelector.java
│ │ │ │ │ │ │ └── ScrollbarSelector.java
│ │ │ │ │ │ └── simple
│ │ │ │ │ │ │ ├── AllSelector.java
│ │ │ │ │ │ │ ├── ClassAttributeSelector.java
│ │ │ │ │ │ │ ├── ElementSelector.java
│ │ │ │ │ │ │ └── IdAttributeSelector.java
│ │ │ │ │ ├── term
│ │ │ │ │ │ ├── TermColor.java
│ │ │ │ │ │ ├── TermFloat.java
│ │ │ │ │ │ ├── TermFunction.java
│ │ │ │ │ │ ├── TermIdent.java
│ │ │ │ │ │ ├── TermInteger.java
│ │ │ │ │ │ ├── TermLength.java
│ │ │ │ │ │ ├── TermList.java
│ │ │ │ │ │ ├── TermURI.java
│ │ │ │ │ │ └── TermUnit.java
│ │ │ │ │ └── util
│ │ │ │ │ │ └── StyleUtils.java
│ │ │ │ └── types
│ │ │ │ │ ├── BorderRadius.java
│ │ │ │ │ ├── BoxShadow.java
│ │ │ │ │ ├── Color.java
│ │ │ │ │ ├── Display.java
│ │ │ │ │ ├── HorizontalAlign.java
│ │ │ │ │ ├── Overflow.java
│ │ │ │ │ ├── PointerEvents.java
│ │ │ │ │ ├── Position.java
│ │ │ │ │ ├── VerticalAlign.java
│ │ │ │ │ ├── WhiteSpace.java
│ │ │ │ │ ├── background
│ │ │ │ │ ├── BackgroundOrigin.java
│ │ │ │ │ ├── BackgroundRepeat.java
│ │ │ │ │ └── BackgroundSize.java
│ │ │ │ │ ├── border
│ │ │ │ │ ├── BorderItem.java
│ │ │ │ │ └── BorderStyle.java
│ │ │ │ │ ├── flex
│ │ │ │ │ ├── AlignContent.java
│ │ │ │ │ ├── AlignItems.java
│ │ │ │ │ ├── AlignSelf.java
│ │ │ │ │ ├── FlexDirection.java
│ │ │ │ │ ├── FlexWrap.java
│ │ │ │ │ └── JustifyContent.java
│ │ │ │ │ └── length
│ │ │ │ │ ├── Length.java
│ │ │ │ │ ├── LengthConverter.java
│ │ │ │ │ └── Unit.java
│ │ │ │ ├── system
│ │ │ │ ├── event
│ │ │ │ │ ├── SystemCharEvent.java
│ │ │ │ │ ├── SystemCharModsEvent.java
│ │ │ │ │ ├── SystemCursorEnterEvent.java
│ │ │ │ │ ├── SystemCursorPosEvent.java
│ │ │ │ │ ├── SystemEvent.java
│ │ │ │ │ ├── SystemFileDropEvent.java
│ │ │ │ │ ├── SystemFramebufferSizeEvent.java
│ │ │ │ │ ├── SystemKeyEvent.java
│ │ │ │ │ ├── SystemMouseClickEvent.java
│ │ │ │ │ ├── SystemScrollEvent.java
│ │ │ │ │ ├── SystemWindowCloseEvent.java
│ │ │ │ │ ├── SystemWindowFocusEvent.java
│ │ │ │ │ ├── SystemWindowIconifyEvent.java
│ │ │ │ │ ├── SystemWindowPosEvent.java
│ │ │ │ │ ├── SystemWindowRefreshEvent.java
│ │ │ │ │ ├── SystemWindowSizeEvent.java
│ │ │ │ │ ├── listener
│ │ │ │ │ │ ├── AbstractSystemEventListener.java
│ │ │ │ │ │ ├── SystemCharEventListener.java
│ │ │ │ │ │ ├── SystemCursorEnterEventListener.java
│ │ │ │ │ │ ├── SystemCursorPosEventListener.java
│ │ │ │ │ │ ├── SystemEventListener.java
│ │ │ │ │ │ ├── SystemFileDropEventListener.java
│ │ │ │ │ │ ├── SystemKeyEventListener.java
│ │ │ │ │ │ ├── SystemMouseClickEventListener.java
│ │ │ │ │ │ ├── SystemScrollEventListener.java
│ │ │ │ │ │ ├── SystemWindowFocusEventListener.java
│ │ │ │ │ │ ├── SystemWindowIconifyEventListener.java
│ │ │ │ │ │ ├── SystemWindowPosEventListener.java
│ │ │ │ │ │ ├── SystemWindowRefreshEventListener.java
│ │ │ │ │ │ ├── SystemWindowSizeEventListener.java
│ │ │ │ │ │ └── SystemWindowsCloseEventListener.java
│ │ │ │ │ ├── processor
│ │ │ │ │ │ ├── SystemEventProcessor.java
│ │ │ │ │ │ └── SystemEventProcessorImpl.java
│ │ │ │ │ └── provider
│ │ │ │ │ │ ├── SystemEventListenerProvider.java
│ │ │ │ │ │ └── SystemEventListenerProviderImpl.java
│ │ │ │ ├── font
│ │ │ │ │ ├── FontDirectoriesProvider.java
│ │ │ │ │ ├── FontLoadingException.java
│ │ │ │ │ ├── FontService.java
│ │ │ │ │ ├── FontStorage.java
│ │ │ │ │ ├── SystemFontLoader.java
│ │ │ │ │ ├── TextLineMetrics.java
│ │ │ │ │ ├── TextMetrics.java
│ │ │ │ │ └── impl
│ │ │ │ │ │ ├── FontServiceImpl.java
│ │ │ │ │ │ ├── FontStorageImpl.java
│ │ │ │ │ │ └── PlatformSpecificFontDirectoriesProvider.java
│ │ │ │ └── input
│ │ │ │ │ ├── SystemKeyAction.java
│ │ │ │ │ ├── SystemKeyMod.java
│ │ │ │ │ └── SystemMouseButton.java
│ │ │ │ ├── time
│ │ │ │ └── TimeService.java
│ │ │ │ └── util
│ │ │ │ ├── ClassKeyMap.java
│ │ │ │ ├── IOUtil.java
│ │ │ │ ├── NodeUtilities.java
│ │ │ │ ├── Reference.java
│ │ │ │ └── TextUtil.java
│ │ └── module-info.java
│ └── resources
│ │ ├── fonts
│ │ ├── FontAwesome.otf
│ │ ├── MaterialIcons-Regular.ttf
│ │ ├── NotoEmoji-Regular.ttf
│ │ ├── Roboto-Bold.ttf
│ │ ├── Roboto-Light.ttf
│ │ ├── Roboto-Regular.ttf
│ │ ├── entypo.ttf
│ │ └── materialdesignicons.ttf
│ │ └── logback.xml
│ └── test
│ └── java
│ └── com
│ └── spinyowl
│ └── spinygui
│ └── core
│ ├── system
│ └── event
│ │ └── listener
│ │ ├── SystemCharEventListenerTest.java
│ │ ├── SystemCursorPosEventListenerTest.java
│ │ ├── SystemFileDropEventListenerTest.java
│ │ ├── SystemKeyEventListenerTest.java
│ │ ├── SystemMouseClickEventListenerTest.java
│ │ ├── SystemScrollEventListenerTest.java
│ │ ├── SystemWindowFocusEventListenerTest.java
│ │ ├── SystemWindowIconifyEventListenerTest.java
│ │ ├── SystemWindowPosEventListenerTest.java
│ │ ├── SystemWindowRefreshEventListenerTest.java
│ │ ├── SystemWindowSizeEventListenerTest.java
│ │ └── SystemWindowsCloseEventListenerTest.java
│ └── util
│ ├── ClassKeyMapTest.java
│ └── IOUtilTest.java
├── demo.complex
├── .gitignore
├── build.gradle
└── src
│ └── main
│ ├── java
│ ├── com
│ │ └── spinyowl
│ │ │ └── spinygui
│ │ │ └── demo
│ │ │ └── complex
│ │ │ ├── Demo.java
│ │ │ └── NvgExample.java
│ └── module-info.java
│ └── resources
│ └── com
│ └── spinyowl
│ └── spinygui
│ └── demo
│ └── index.html
├── demo.simple
├── .gitignore
├── build.gradle
└── src
│ └── main
│ ├── java
│ ├── com
│ │ └── spinyowl
│ │ │ └── spinygui
│ │ │ └── demo
│ │ │ └── simple
│ │ │ ├── Main.java
│ │ │ └── OtherMain.java
│ └── module-info.java
│ └── resources
│ └── com
│ └── spinyowl
│ └── spinygui
│ └── demo
│ └── index.html
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── intellij-java-google-style.xml
├── logging.properties
├── lombok.config
├── settings.gradle
└── spinygui
├── .gitignore
├── build.gradle
└── src
└── main
└── java
└── module-info.java
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | ko_fi: SpinyOwl
2 | custom: [paypal.me/spinyowl]
3 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: 'Bug report'
3 | about: 'Create a report to help us improve title: "[BUG]"'
4 | labels: ''
5 | assignees: ''
6 |
7 | ---
8 |
9 | **Describe the bug**
10 | A clear and concise description of what the bug is.
11 |
12 | **To Reproduce**
13 | Code snippet to reproduce the bug:
14 |
15 | **Expected behavior**
16 | A clear and concise description of what you expected to happen.
17 |
18 | **Screenshots**
19 | If applicable, add screenshots to help explain your problem.
20 |
21 | **Logs**
22 | If applicable, add some logs or stacktrace
23 |
24 | **Desktop (please complete the following information):**
25 |
26 | - OS: [e.g. Windows 10 / MacOS / Linux]
27 | - SpinyGUI Version [e.g. 22]
28 |
29 | **Additional context**
30 | Add any other context about the problem here.
31 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/custom.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: 'Custom issue template'
3 | about: "Describe this issue template's purpose here."
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: 'Feature request'
3 | about: 'Suggest an idea for this project title: "[FEATURE]"'
4 | labels: ''
5 | assignees: ''
6 |
7 | ---
8 |
9 | **Is your feature request related to a problem? Please describe.**
10 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
11 |
12 | **Describe the solution you'd like**
13 | A clear and concise description of what you want to happen.
14 |
15 | **Describe alternatives you've considered**
16 | A clear and concise description of any alternative solutions or features you've considered.
17 |
18 | **Additional context**
19 | Add any other context or screenshots about the feature request here.
20 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: "github-actions"
4 | directory: "/"
5 | schedule:
6 | interval: "weekly"
7 |
8 | - package-ecosystem: "gradle"
9 | directory: "/"
10 | schedule:
11 | interval: "weekly"
12 | open-pull-requests-limit: 10
13 |
--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | # For most projects, this workflow file will not need changing; you simply need
2 | # to commit it to your repository.
3 | #
4 | # You may wish to alter this file to override the set of languages analyzed,
5 | # or to provide custom queries or build logic.
6 | #
7 | # ******** NOTE ********
8 | # We have attempted to detect the languages in your repository. Please check
9 | # the `language` matrix defined below to confirm you have the correct set of
10 | # supported CodeQL languages.
11 | #
12 | name: "CodeQL"
13 |
14 | on:
15 | push:
16 | branches: [ develop ]
17 | pull_request:
18 | branches: [ develop ]
19 |
20 | jobs:
21 | analyze:
22 | name: Analyze
23 | runs-on: ubuntu-latest
24 | permissions:
25 | actions: read
26 | contents: read
27 | security-events: write
28 |
29 | steps:
30 | - name: Checkout repository
31 | uses: actions/checkout@v3
32 |
33 | # Initializes the CodeQL tools for scanning.
34 | - name: Initialize CodeQL
35 | uses: github/codeql-action/init@v3
36 | with:
37 | languages: java
38 |
39 | # PREPARE
40 | - name: Setup Java JDK
41 | uses: actions/setup-java@v4.2.1
42 | with:
43 | java-version: 21
44 | distribution: 'adopt'
45 | cache: 'gradle'
46 |
47 | # BUILD
48 | - name: Build with Gradle
49 | run: ./gradlew clean build
50 |
51 | # ANALYZE
52 | - name: Perform CodeQL Analysis
53 | uses: github/codeql-action/analyze@v3
54 |
--------------------------------------------------------------------------------
/.github/workflows/gradle.yml:
--------------------------------------------------------------------------------
1 | name: Build and check
2 | on:
3 | push:
4 | branches: [ develop ]
5 | pull_request:
6 | branches: [ develop ]
7 |
8 | jobs:
9 | build:
10 | name: Build SpinyGUI
11 | runs-on: ubuntu-latest
12 | steps:
13 | # CHECKOUT
14 | - uses: actions/checkout@v3
15 |
16 | # PREPARE
17 | - name: Setup Java JDK
18 | uses: actions/setup-java@v4.2.1
19 | with:
20 | java-version: 21
21 | distribution: 'adopt'
22 | cache: 'gradle'
23 |
24 | # BUILD
25 | - name: Build with Gradle
26 | run: ./gradlew clean build
27 |
28 | # Run tests
29 | - name: Test with Gradle
30 | run: ./gradlew test
31 |
32 | # Run codecov
33 | - name: Code Coverage
34 | env:
35 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
36 | uses: codecov/codecov-action@v4.3.1
37 | with:
38 | files: ./**/build/**/reports/jacoco/test/jacocoTestReport.xml
39 | name: codecov
40 | token: ${{secrets.CODECOV_TOKEN}}
41 | verbose: true
42 |
43 | # # Run sonarqube
44 | # - name: Sonarqube
45 | # env:
46 | # GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
47 | # sonarLogin: ${{secrets.SONAR_TOKEN}}
48 | # JAVA_OPTS: -Xmx3072m -XX:MaxPermSize=512m -XX:ReservedCodeCacheSize=128m
49 | # GRADLE_OPTS: -Xmx3800m -XX:ReservedCodeCacheSize=128m -Dorg.gradle.daemon=false
50 | # run: ./gradlew test sonarqube
51 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /.idea/
2 | **/build/*
3 | **/target/*
4 | /.gradle/
5 | **/classes/*
6 | **/out/*
7 | hs_err_pid*
8 | /logs/
9 |
10 | *.class
11 | *.iml
12 | /bin/
13 | /build/
14 |
15 | .classpath
16 | .project
17 | /.settings/
18 | /lwjgl-extract/
19 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Spiny Owl
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](/)
2 | [](/)
3 | [](/LICENSE)
4 | [](https://ko-fi.com/J3J4L9ASJ)
5 | [](https://github.com/SpinyOwl/SpinyGUI/actions/workflows/gradle.yml)
6 | [](https://sonarcloud.io/summary/overall?id=SpinyOwl_SpinyGUI)
7 | [](https://sonarcloud.io/component_measures?metric=Coverage&id=SpinyOwl_SpinyGUI)
8 |
9 | [](https://codecov.io/gh/SpinyOwl/SpinyGUI)
10 |
11 | ---
12 | # SpinyGUI (WIP)
13 | ---
14 | First implementation is known as https://github.com/SpinyOwl/legui
15 |
16 | New implementation should be more flexible.
17 |
18 | ## System requirements
19 |
20 | SpinyGUI requires Java 15+.
21 |
22 | ## Links
23 |
24 | [LWJGL - Lightweight Java Game Library 3](https://github.com/LWJGL/lwjgl3)
25 | [JOML – Java OpenGL Math Library](https://github.com/JOML-CI/JOML)
26 | [NanoVG -Small antialiased vector graphics rendering library for OpenGL.](https://github.com/memononen/nanovg)
27 |
--------------------------------------------------------------------------------
/core.backend.lwjgl.nanovg/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'java-library'
3 | id 'jacoco'
4 | }
5 | dependencies {
6 | api project(':core')
7 | api project(':core.backend')
8 |
9 | api group: 'org.lwjgl', name: 'lwjgl', version: lwjgl_version
10 | api group: 'org.lwjgl', name: 'lwjgl', version: lwjgl_version, classifier: 'natives-windows'
11 | api group: 'org.lwjgl', name: 'lwjgl', version: lwjgl_version, classifier: 'natives-linux'
12 | api group: 'org.lwjgl', name: 'lwjgl', version: lwjgl_version, classifier: 'natives-macos'
13 |
14 | api group: 'org.lwjgl', name: 'lwjgl-glfw', version: lwjgl_version
15 | api group: 'org.lwjgl', name: 'lwjgl-glfw', version: lwjgl_version, classifier: 'natives-windows'
16 | api group: 'org.lwjgl', name: 'lwjgl-glfw', version: lwjgl_version, classifier: 'natives-linux'
17 | api group: 'org.lwjgl', name: 'lwjgl-glfw', version: lwjgl_version, classifier: 'natives-macos'
18 |
19 | api group: 'org.lwjgl', name: 'lwjgl-opengl', version: lwjgl_version
20 | api group: 'org.lwjgl', name: 'lwjgl-opengl', version: lwjgl_version, classifier: 'natives-windows'
21 | api group: 'org.lwjgl', name: 'lwjgl-opengl', version: lwjgl_version, classifier: 'natives-linux'
22 | api group: 'org.lwjgl', name: 'lwjgl-opengl', version: lwjgl_version, classifier: 'natives-macos'
23 |
24 | api group: 'org.lwjgl', name: 'lwjgl-nanovg', version: lwjgl_version
25 | api group: 'org.lwjgl', name: 'lwjgl-nanovg', version: lwjgl_version, classifier: 'natives-windows'
26 | api group: 'org.lwjgl', name: 'lwjgl-nanovg', version: lwjgl_version, classifier: 'natives-linux'
27 | api group: 'org.lwjgl', name: 'lwjgl-nanovg', version: lwjgl_version, classifier: 'natives-macos'
28 |
29 | api group: 'org.slf4j', name: 'slf4j-api', version: slf4j_version
30 | api group: 'ch.qos.logback', name: 'logback-classic', version: logback_version
31 | }
32 |
33 | test {
34 | finalizedBy jacocoTestReport // report is always generated after tests run
35 | }
36 | jacocoTestReport {
37 | dependsOn test // tests are required to run before generating the report
38 | reports.xml.required = true
39 | }
--------------------------------------------------------------------------------
/core.backend.lwjgl.nanovg/src/main/java/com/spinyowl/spinygui/core/backend/renderer/lwjgl/nanovg/NvgBorderRenderer.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.backend.renderer.lwjgl.nanovg;
2 |
3 | import static com.spinyowl.spinygui.core.backend.renderer.lwjgl.nanovg.util.NvgRenderUtils.createScissor;
4 | import static com.spinyowl.spinygui.core.backend.renderer.lwjgl.nanovg.util.NvgRenderUtils.resetScissor;
5 | import static com.spinyowl.spinygui.core.backend.renderer.lwjgl.nanovg.util.NvgShapes.drawRectStroke;
6 |
7 | import com.spinyowl.spinygui.core.node.Element;
8 | import com.spinyowl.spinygui.core.node.Node;
9 | import com.spinyowl.spinygui.core.style.types.border.BorderStyle;
10 | import org.joml.Vector2f;
11 |
12 | public class NvgBorderRenderer {
13 |
14 | public void render(Node node, long nanovg) {
15 | Element element = node.asElement();
16 |
17 | createScissor(nanovg, node);
18 | var style = element.resolvedStyle();
19 | if (BorderStyle.NONE.equals(style.borderTopStyle())) return;
20 | float borderThickness = element.box().border().top();
21 |
22 | Vector2f position = element.absolutePosition().add(borderThickness / 2, borderThickness / 2);
23 | Vector2f size = element.size().sub(borderThickness, borderThickness);
24 |
25 | drawRectStroke(nanovg, position, size, style.borderTopColor(), borderThickness);
26 | resetScissor(nanovg);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/core.backend.lwjgl.nanovg/src/main/java/com/spinyowl/spinygui/core/backend/renderer/lwjgl/nanovg/NvgElementRenderer.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.backend.renderer.lwjgl.nanovg;
2 |
3 | import static com.spinyowl.spinygui.core.backend.renderer.lwjgl.nanovg.util.NvgRenderUtils.createScissor;
4 | import static com.spinyowl.spinygui.core.backend.renderer.lwjgl.nanovg.util.NvgRenderUtils.getBorderRadius;
5 | import static com.spinyowl.spinygui.core.backend.renderer.lwjgl.nanovg.util.NvgRenderUtils.resetScissor;
6 | import static com.spinyowl.spinygui.core.backend.renderer.lwjgl.nanovg.util.NvgShapes.drawRect;
7 | import static com.spinyowl.spinygui.core.util.NodeUtilities.visible;
8 | import static org.lwjgl.nanovg.NanoVG.nvgRestore;
9 | import static org.lwjgl.nanovg.NanoVG.nvgSave;
10 |
11 | import com.spinyowl.spinygui.core.node.Element;
12 | import com.spinyowl.spinygui.core.node.Node;
13 |
14 | public class NvgElementRenderer {
15 |
16 | public void render(Node node, long nanovg) {
17 | Element element = node.asElement();
18 | if (visible(element) /*&& visibleInParents(element)*/) {
19 | var style = element.resolvedStyle();
20 | var backgroundColor = style.backgroundColor();
21 | var borderRadius = getBorderRadius(element, style);
22 |
23 | var position = element.absolutePosition();
24 | var size = element.size();
25 |
26 | // render self
27 | createScissor(nanovg, node);
28 | nvgSave(nanovg);
29 | drawRect(nanovg, position, size, backgroundColor, borderRadius);
30 | nvgRestore(nanovg);
31 | resetScissor(nanovg);
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/core.backend.lwjgl.nanovg/src/main/java/com/spinyowl/spinygui/core/backend/renderer/lwjgl/nanovg/NvgTextRenderer.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.backend.renderer.lwjgl.nanovg;
2 |
3 | import static com.spinyowl.spinygui.core.backend.renderer.lwjgl.nanovg.util.NvgRenderUtils.createScissor;
4 | import static com.spinyowl.spinygui.core.backend.renderer.lwjgl.nanovg.util.NvgRenderUtils.resetScissor;
5 | import static com.spinyowl.spinygui.core.backend.renderer.lwjgl.nanovg.util.NvgShapes.drawRect;
6 | import static com.spinyowl.spinygui.core.backend.renderer.lwjgl.nanovg.util.NvgShapes.drawRectStroke;
7 | import static org.lwjgl.nanovg.NanoVG.nvgRestore;
8 | import static org.lwjgl.nanovg.NanoVG.nvgSave;
9 |
10 | import com.spinyowl.spinygui.core.node.Element;
11 | import com.spinyowl.spinygui.core.node.Node;
12 | import com.spinyowl.spinygui.core.node.Text;
13 | import com.spinyowl.spinygui.core.style.stylesheet.util.StyleUtils;
14 | import com.spinyowl.spinygui.core.style.types.Color;
15 |
16 | public class NvgTextRenderer {
17 |
18 | public void render(Node node, long nanovg) {
19 | Text text = node.asText();
20 | var position = text.absolutePosition();
21 | var size = text.size();
22 |
23 | Element parent = text.parent();
24 | if (parent == null) return;
25 |
26 | Float fontSize = StyleUtils.getFontSize(text);
27 | if (fontSize == null) return;
28 |
29 | createScissor(nanovg, node);
30 |
31 | nvgSave(nanovg);
32 | drawRect(nanovg, position, size, Color.ROYALBLUE, 1);
33 | drawRectStroke(nanovg, position, size, Color.RED, 1);
34 | nvgRestore(nanovg);
35 |
36 | resetScissor(nanovg);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/core.backend.lwjgl.nanovg/src/main/java/module-info.java:
--------------------------------------------------------------------------------
1 | open module com.spinyowl.spinygui.core.backend.lwjgl.nanovg {
2 | requires com.spinyowl.spinygui.core;
3 | requires com.spinyowl.spinygui.core.backend;
4 |
5 | requires lombok;
6 |
7 | requires org.lwjgl;
8 | requires org.lwjgl.natives;
9 | requires org.lwjgl.glfw;
10 | requires org.lwjgl.glfw.natives;
11 | requires org.lwjgl.nanovg;
12 | requires org.lwjgl.nanovg.natives;
13 | requires org.lwjgl.opengl;
14 | requires org.lwjgl.opengl.natives;
15 |
16 | exports com.spinyowl.spinygui.core.backend.renderer.lwjgl.nanovg;
17 | exports com.spinyowl.spinygui.core.backend.renderer.lwjgl.nanovg.util;
18 | }
19 |
--------------------------------------------------------------------------------
/core.backend/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'java-library'
3 | id 'jacoco'
4 | }
5 | dependencies {
6 | api project(':core')
7 |
8 | api group: 'org.slf4j', name: 'slf4j-api', version: slf4j_version
9 | api group: 'ch.qos.logback', name: 'logback-classic', version: logback_version
10 | }
11 |
12 | java {
13 | withJavadocJar()
14 | withSourcesJar()
15 | }
16 |
17 | test {
18 | finalizedBy jacocoTestReport // report is always generated after tests run
19 | }
20 |
21 | jacocoTestReport {
22 | dependsOn test // tests are required to run before generating the report
23 | reports.xml.required = true
24 | }
25 |
--------------------------------------------------------------------------------
/core.backend/src/main/java/com/spinyowl/spinygui/core/backend/renderer/Renderer.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.backend.renderer;
2 |
3 | import com.spinyowl.spinygui.core.node.Frame;
4 | import org.joml.Vector2fc;
5 | import org.joml.Vector2ic;
6 |
7 | /** Common renderer interface. */
8 | public interface Renderer {
9 |
10 | void initialize();
11 |
12 | void render(long window, Vector2fc windowSize, Vector2ic frameBufferSize, Frame frame);
13 |
14 | void destroy();
15 | }
16 |
--------------------------------------------------------------------------------
/core.backend/src/main/java/module-info.java:
--------------------------------------------------------------------------------
1 | open module com.spinyowl.spinygui.core.backend {
2 | requires com.spinyowl.spinygui.core;
3 |
4 | exports com.spinyowl.spinygui.core.backend.renderer;
5 | }
6 |
--------------------------------------------------------------------------------
/core/.gitignore:
--------------------------------------------------------------------------------
1 | /.idea/
2 | /gradle.properties
3 | /build/
4 | /target/
5 | /.gradle/
6 | /classes/
7 | /out/
8 | hs_err_pid*
9 | /logs/
10 |
11 | *.class
12 | *.iml
13 | /bin/
14 |
15 | .classpath
16 | .project
17 | /.settings/
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/animation/Animation.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.animation;
2 |
3 | import lombok.EqualsAndHashCode;
4 | import lombok.Getter;
5 | import lombok.NonNull;
6 | import lombok.RequiredArgsConstructor;
7 | import lombok.ToString;
8 |
9 | /**
10 | * Draft animation realization.
11 | */
12 | @RequiredArgsConstructor
13 | @Getter
14 | @EqualsAndHashCode
15 | @ToString
16 | public abstract class Animation {
17 |
18 | @NonNull private final Animator animator;
19 | /** Flag that indicates if animation was started. */
20 | private boolean animationStarted = false;
21 |
22 | /** Adds animation to animator. */
23 | public void startAnimation() {
24 | if (!animationStarted) {
25 | animator.pushAnimation(this);
26 | animationStarted = true;
27 | }
28 | }
29 |
30 | /** Called once during animation lifetime before animate loop. */
31 | protected void initialize() {
32 | // Could be implemented later.
33 | }
34 |
35 | /**
36 | * This method used to update animated object. Called by animator every frame. Removed from
37 | * animator and stops when this method returns true.
38 | *
39 | *
Returns true if animation is finished and could be removed from animator.
40 | *
41 | * @param delta delta time (from previous call).
42 | * @return true if animation is finished and could be removed from animator.
43 | */
44 | protected abstract boolean animate(double delta);
45 |
46 | /** Called once during animation lifetime when animation ended. */
47 | protected void destroy() {
48 | // Could be implemented later.
49 | }
50 |
51 | /** Used to stop animation. Removes animation from animator. */
52 | public void stopAnimation() {
53 | animator.removeAnimation(this);
54 | }
55 |
56 | /**
57 | * Returns the flag that indicates if animation was started.
58 | *
59 | * @return the flag that indicates if animation was started.
60 | */
61 | public boolean isAnimationStarted() {
62 | return animationStarted;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/animation/Animator.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.animation;
2 |
3 | /**
4 | * Animation processor.
5 | */
6 | public interface Animator {
7 |
8 | /** This method used to process animations. */
9 | void runAnimations();
10 |
11 | /**
12 | * Used to add animation to animator.
13 | *
14 | * @param animation animation to add.
15 | */
16 | void pushAnimation(Animation animation);
17 |
18 | /**
19 | * Used to remove animation from animator. In case if animation is not finished animation still
20 | * should be removed and terminated.
21 | *
22 | * @param animation animation to remove.
23 | */
24 | void removeAnimation(Animation animation);
25 | }
26 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/clipboard/Clipboard.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.clipboard;
2 |
3 | public interface Clipboard {
4 | String getClipboardString();
5 |
6 | void setClipboardString(String string);
7 | }
8 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/cursor/Cursor.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.cursor;
2 |
3 | import com.spinyowl.spinygui.core.image.Image;
4 | import lombok.EqualsAndHashCode;
5 | import lombok.Getter;
6 | import lombok.NonNull;
7 | import lombok.ToString;
8 |
9 | @Getter
10 | @ToString
11 | @EqualsAndHashCode
12 | public class Cursor {
13 |
14 | public static final Cursor ARROW = new Cursor();
15 | public static final Cursor H_RESIZE = new Cursor();
16 | public static final Cursor V_RESIZE = new Cursor();
17 | public static final Cursor CROSSHAIR = new Cursor();
18 | public static final Cursor HAND = new Cursor();
19 | public static final Cursor IBEAM = new Cursor();
20 |
21 | private final int xHot;
22 | private final int yHot;
23 | private final Image image;
24 |
25 | private Cursor() {
26 | xHot = 0;
27 | yHot = 0;
28 | image = null;
29 | }
30 |
31 | public Cursor(int xHot, int yHot, @NonNull Image image) {
32 | this.xHot = xHot;
33 | this.yHot = yHot;
34 | this.image = image;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/cursor/CursorMod.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.cursor;
2 |
3 | public enum CursorMod {
4 | /** Meaning the regular arrow cursor is used and cursor motion is not limited. */
5 | NORMAL,
6 | /**
7 | * Just hides cursor without any automatic centering. This mode puts no limit on the motion of the
8 | * cursor.
9 | */
10 | HIDDEN,
11 | /**
12 | * Hides cursor and automatically centers it. Used when you need to implement mouse motion based
13 | * camera controls or other input schemes that require unlimited mouse movement.
14 | *
15 | *
This will hide the cursor and lock it to the specified window. Backend should then take care
16 | * of all the details of cursor re-centering and offset calculation and providing the application
17 | * with a virtual cursor position. This virtual position is provided normally via both the cursor
18 | * position callback and through polling.
19 | */
20 | DISABLED
21 | }
22 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/cursor/CursorService.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.cursor;
2 |
3 | import com.spinyowl.spinygui.core.node.Frame;
4 | import lombok.NonNull;
5 |
6 | public interface CursorService {
7 |
8 | /**
9 | * Used to set specified cursor for specified frame.
10 | *
11 | * @param cursor cursor to set.
12 | * @param frame frame to set.
13 | */
14 | void serCursor(@NonNull Cursor cursor, @NonNull Frame frame);
15 |
16 | /**
17 | * Used to set specified cursor mod for specified frame.
18 | *
19 | * @param cursorMod cursor mod.
20 | * @param frame frame.
21 | */
22 | void setCursorMod(@NonNull CursorMod cursorMod, @NonNull Frame frame);
23 | }
24 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/event/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SpinyOwl/SpinyGUI/4db014a5fb9b5adb485e100089b31d28c0d45995/core/src/main/java/com/spinyowl/spinygui/core/event/.gitkeep
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/event/ChangePositionEvent.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.event;
2 |
3 | import lombok.Data;
4 | import lombok.NonNull;
5 | import lombok.experimental.SuperBuilder;
6 | import org.joml.Vector2f;
7 |
8 | @Data
9 | @SuperBuilder
10 | public class ChangePositionEvent extends Event {
11 | @NonNull private final Vector2f oldPos;
12 | @NonNull private final Vector2f newPos;
13 | }
14 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/event/ChangeSizeEvent.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.event;
2 |
3 | import lombok.Data;
4 | import lombok.NonNull;
5 | import lombok.experimental.SuperBuilder;
6 | import org.joml.Vector2f;
7 |
8 | @Data
9 | @SuperBuilder
10 | public class ChangeSizeEvent extends Event {
11 | @NonNull private final Vector2f oldSize;
12 | @NonNull private final Vector2f newSize;
13 | }
14 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/event/ChangeTextEvent.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.event;
2 |
3 | import lombok.Data;
4 | import lombok.NonNull;
5 | import lombok.experimental.SuperBuilder;
6 |
7 | @Data
8 | @SuperBuilder
9 | public class ChangeTextEvent extends Event {
10 | @NonNull private final String oldText;
11 | @NonNull private final String newText;
12 | }
13 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/event/CharEvent.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.event;
2 |
3 | import lombok.Data;
4 | import lombok.NonNull;
5 | import lombok.experimental.SuperBuilder;
6 |
7 | @Data
8 | @SuperBuilder
9 | public class CharEvent extends Event {
10 | @NonNull private final String input;
11 | }
12 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/event/CursorEnterEvent.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.event;
2 |
3 | import lombok.Data;
4 | import lombok.NonNull;
5 | import lombok.experimental.SuperBuilder;
6 | import org.joml.Vector2fc;
7 |
8 | @Data
9 | @SuperBuilder
10 | public class CursorEnterEvent extends Event {
11 | @NonNull private final Vector2fc cursorPosition;
12 | @NonNull private final Vector2fc intersection;
13 | }
14 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/event/CursorExitEvent.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.event;
2 |
3 | import lombok.Data;
4 | import lombok.NonNull;
5 | import lombok.experimental.SuperBuilder;
6 | import org.joml.Vector2fc;
7 |
8 | @Data
9 | @SuperBuilder
10 | public class CursorExitEvent extends Event {
11 | @NonNull private final Vector2fc cursorPosition;
12 | @NonNull private final Vector2fc intersection;
13 | }
14 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/event/Event.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.event;
2 |
3 | import lombok.Data;
4 | import lombok.NonNull;
5 | import lombok.experimental.SuperBuilder;
6 |
7 | @Data
8 | @SuperBuilder
9 | public class Event {
10 |
11 | /** Element which cause event generation. */
12 | @NonNull private final EventTarget source;
13 | /** Target element to which the event was originally dispatched. */
14 | @NonNull private final EventTarget target;
15 | /** Timestamp of event. */
16 | private final double timestamp;
17 |
18 | /**
19 | * Currently registered target for the event. This is the object to which the event is currently
20 | * slated to be sent. It's possible this has been changed along the way through retargeting.
21 | */
22 | private EventTarget currentTarget;
23 | }
24 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/event/EventTarget.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.event;
2 |
3 | import com.spinyowl.spinygui.core.event.listener.EventListener;
4 | import java.util.List;
5 |
6 | /** Event target interface. Event target should allow to store and retrieve listeners for events. */
7 | public interface EventTarget {
8 |
9 | /**
10 | * Adds event listener to queue of listener for specified event class.
11 | *
12 | * @param eventClass event class.
13 | * @param listener listener to add.
14 | * @param type of event.
15 | */
16 | void addListener(Class eventClass, EventListener listener);
17 |
18 | /**
19 | * Removes specified event listener for specified event.
20 | *
21 | * @param eventClass event class.
22 | * @param listener listener to remove.
23 | * @param type of event.
24 | */
25 | void removeListener(Class eventClass, EventListener listener);
26 |
27 | /**
28 | * Returns list of listeners for specified event class.
29 | *
30 | * @param eventClass event class.
31 | * @param type of event.
32 | * @return list of event listeners for specified event class.
33 | */
34 | List> getListeners(Class eventClass);
35 | }
36 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/event/FileDropEvent.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.event;
2 |
3 | import lombok.Data;
4 | import lombok.NonNull;
5 | import lombok.experimental.SuperBuilder;
6 |
7 | @Data
8 | @SuperBuilder
9 | public class FileDropEvent extends Event {
10 | @NonNull private final String[] paths;
11 | }
12 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/event/FocusInEvent.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.event;
2 |
3 | import com.spinyowl.spinygui.core.node.Element;
4 | import lombok.Data;
5 | import lombok.NonNull;
6 | import lombok.experimental.SuperBuilder;
7 |
8 | @Data
9 | @SuperBuilder
10 | public class FocusInEvent extends Event {
11 |
12 | @NonNull
13 | private final Element prevFocus;
14 | }
15 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/event/FocusOutEvent.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.event;
2 |
3 | import com.spinyowl.spinygui.core.node.Element;
4 | import lombok.Data;
5 | import lombok.NonNull;
6 | import lombok.experimental.SuperBuilder;
7 |
8 | @Data
9 | @SuperBuilder
10 | public class FocusOutEvent extends Event {
11 |
12 | @NonNull
13 | private final Element nextFocus;
14 | }
15 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/event/KeyboardEvent.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.event;
2 |
3 | import com.spinyowl.spinygui.core.input.KeyAction;
4 | import com.spinyowl.spinygui.core.input.KeyMod;
5 | import com.spinyowl.spinygui.core.input.KeyboardKey;
6 | import java.util.Set;
7 | import lombok.Data;
8 | import lombok.NonNull;
9 | import lombok.experimental.SuperBuilder;
10 |
11 | @Data
12 | @SuperBuilder
13 | public class KeyboardEvent extends Event {
14 | @NonNull private final KeyAction action;
15 | @NonNull private final KeyboardKey key;
16 | @NonNull private final Set mods;
17 | }
18 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/event/MouseClickEvent.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.event;
2 |
3 | import com.google.common.collect.ImmutableSet;
4 | import com.spinyowl.spinygui.core.input.KeyAction;
5 | import com.spinyowl.spinygui.core.input.KeyMod;
6 | import com.spinyowl.spinygui.core.input.MouseButton;
7 | import lombok.Data;
8 | import lombok.NonNull;
9 | import lombok.experimental.SuperBuilder;
10 | import org.joml.Vector2fc;
11 |
12 | @Data
13 | @SuperBuilder
14 | public class MouseClickEvent extends Event {
15 | @NonNull private final KeyAction action;
16 | @NonNull private final MouseButton mouseButton;
17 | @NonNull private final Vector2fc position;
18 | @NonNull private final Vector2fc absolutePosition;
19 | @NonNull private final ImmutableSet mods;
20 | }
21 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/event/MouseDragEvent.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.event;
2 |
3 | import lombok.Data;
4 | import lombok.NonNull;
5 | import lombok.experimental.SuperBuilder;
6 | import org.joml.Vector2fc;
7 |
8 | @Data
9 | @SuperBuilder
10 | public class MouseDragEvent extends Event {
11 | @NonNull private final Vector2fc delta;
12 | }
13 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/event/ScrollEvent.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.event;
2 |
3 | import lombok.Data;
4 | import lombok.experimental.SuperBuilder;
5 |
6 | @Data
7 | @SuperBuilder
8 | public class ScrollEvent extends Event {
9 | private final float offsetX;
10 | private final float offsetY;
11 | }
12 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/event/WindowCloseEvent.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.event;
2 |
3 | import lombok.Data;
4 | import lombok.experimental.SuperBuilder;
5 |
6 | @Data
7 | @SuperBuilder
8 | public class WindowCloseEvent extends Event {}
9 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/event/WindowFocusEvent.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.event;
2 |
3 | import lombok.Data;
4 | import lombok.experimental.SuperBuilder;
5 |
6 | @Data
7 | @SuperBuilder
8 | public class WindowFocusEvent extends Event {}
9 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/event/WindowIconifyEvent.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.event;
2 |
3 | import lombok.Data;
4 | import lombok.experimental.SuperBuilder;
5 |
6 | @Data
7 | @SuperBuilder
8 | public class WindowIconifyEvent extends Event {
9 | private final boolean iconified;
10 | }
11 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/event/WindowPosEvent.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.event;
2 |
3 | import lombok.Data;
4 | import lombok.experimental.SuperBuilder;
5 |
6 | @Data
7 | @SuperBuilder
8 | public class WindowPosEvent extends Event {
9 | private final int posX;
10 | private final int posY;
11 | }
12 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/event/WindowRefreshEvent.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.event;
2 |
3 | import lombok.Data;
4 | import lombok.experimental.SuperBuilder;
5 |
6 | @Data
7 | @SuperBuilder
8 | public class WindowRefreshEvent extends Event {}
9 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/event/WindowSizeEvent.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.event;
2 |
3 | import lombok.Data;
4 | import lombok.experimental.SuperBuilder;
5 |
6 | @Data
7 | @SuperBuilder
8 | public class WindowSizeEvent extends Event {
9 | private final int width;
10 | private final int height;
11 | }
12 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/event/listener/EventListener.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.event.listener;
2 |
3 | import com.spinyowl.spinygui.core.event.Event;
4 |
5 | public interface EventListener {
6 |
7 | void process(T event);
8 | }
9 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/event/processor/DefaultEventProcessor.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.event.processor;
2 |
3 | import com.spinyowl.spinygui.core.event.Event;
4 | import com.spinyowl.spinygui.core.event.EventTarget;
5 | import com.spinyowl.spinygui.core.event.listener.EventListener;
6 | import java.util.LinkedList;
7 | import java.util.Queue;
8 |
9 | public class DefaultEventProcessor implements EventProcessor {
10 |
11 | private Queue first = new LinkedList<>();
12 | private Queue second = new LinkedList<>();
13 |
14 | @Override
15 | public void push(Event event) {
16 | first.add(event);
17 | }
18 |
19 | @Override
20 | @SuppressWarnings({"rawtypes", "unchecked"})
21 | public void processEvents() {
22 | if (first.isEmpty()) {
23 | return;
24 | }
25 |
26 | swap();
27 | for (var event = second.poll(); event != null; event = second.poll()) {
28 | EventTarget target = event.target();
29 | var listeners = target.getListeners(event.getClass());
30 | for (EventListener listener : listeners) {
31 | listener.process(event);
32 | }
33 | }
34 | }
35 |
36 | private void swap() {
37 | Queue queue = this.first;
38 | this.first = second;
39 | this.second = queue;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/event/processor/EventProcessor.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.event.processor;
2 |
3 | import com.spinyowl.spinygui.core.event.Event;
4 |
5 | public interface EventProcessor {
6 |
7 | void push(Event event);
8 |
9 | void processEvents();
10 | }
11 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/image/Image.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.image;
2 |
3 | public interface Image {
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/input/KeyAction.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.input;
2 |
3 | public enum KeyAction {
4 | PRESS,
5 | RELEASE,
6 | REPEAT,
7 | CLICK // applicable to mouse keys
8 | }
9 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/input/KeyMod.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.input;
2 |
3 | public enum KeyMod {
4 | SHIFT,
5 | CONTROL,
6 | ALT,
7 | SUPER,
8 | CAPS_LOCK,
9 | NUM_LOCK
10 | }
11 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/input/Keyboard.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.input;
2 |
3 | import lombok.Data;
4 | import lombok.NonNull;
5 |
6 | @Data
7 | public class Keyboard {
8 |
9 | @NonNull private KeyboardLayout layout;
10 | @NonNull private ShortcutRegistry shortcuts;
11 | }
12 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/input/KeyboardKey.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.input;
2 |
3 | import lombok.Data;
4 | import lombok.NonNull;
5 |
6 | @Data
7 | public class KeyboardKey {
8 |
9 | /**
10 | * KeyCode associated with native key code. Could be different (in case of using some different
11 | * from QWERTY keyboard layout).
12 | */
13 | @NonNull private final KeyCode keyCode;
14 |
15 | /** The code value of the physical key represented by the event. */
16 | private final int nativeKeyCode;
17 |
18 | /**
19 | * The system-specific scancode of the key.
20 | *
21 | * The scancode is unique for every key, regardless of whether it has a key token. Scancodes
22 | * are platform-specific but consistent over time, so keys will have different scancodes depending
23 | * on the platform, but they are safe to save to disk.
24 | */
25 | private final int scancode;
26 | }
27 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/input/KeyboardLayout.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.input;
2 |
3 | /**
4 | * Used to store key mapping to native keys. That allows user to have custom keyboard layouts (that
5 | * differ from standard QWERTY layout).
6 | */
7 | public interface KeyboardLayout {
8 |
9 | /**
10 | * Used to set mapping for specified keyCode.
11 | *
12 | * @param keyCode key code.
13 | * @param nativeCode native key code.
14 | */
15 | void updateMapping(KeyCode keyCode, Integer nativeCode);
16 |
17 | /**
18 | * Used to get mapped {@link KeyCode} by native key code.
19 | *
20 | * @param nativeCode native key code.
21 | * @return mapped {@link KeyCode} or null if not found.
22 | */
23 | KeyCode keyCode(Integer nativeCode);
24 |
25 | /**
26 | * Used to get mapped native key code by {@link KeyCode}.
27 | *
28 | * @param keyCode key code.
29 | * @return mapped native key code or null if not found.
30 | */
31 | Integer nativeCode(KeyCode keyCode);
32 | }
33 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/input/MouseButton.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.input;
2 |
3 | public enum MouseButton {
4 | MOUSE_BUTTON_1, // LEFT
5 | MOUSE_BUTTON_2, // RIGHT
6 | MOUSE_BUTTON_3, // MIDDLE
7 | MOUSE_BUTTON_4, // SIDE LEFT TOP
8 | MOUSE_BUTTON_5, // SIDE LEFT BOTTOM
9 | MOUSE_BUTTON_6,
10 | MOUSE_BUTTON_7,
11 | MOUSE_BUTTON_8;
12 |
13 | public static final MouseButton LEFT = MOUSE_BUTTON_1;
14 | public static final MouseButton RIGHT = MOUSE_BUTTON_2;
15 | public static final MouseButton MIDDLE = MOUSE_BUTTON_3;
16 | }
17 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/input/MouseService.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.input;
2 |
3 | import com.spinyowl.spinygui.core.node.Frame;
4 | import lombok.Data;
5 | import org.joml.Vector2fc;
6 |
7 | /** Provides ability to get and set cursor positions. */
8 | public interface MouseService {
9 |
10 | CursorPositions getCursorPositions(Frame frame);
11 |
12 | void setCursorPositions(Frame frame, CursorPositions positions);
13 |
14 | boolean pressed(MouseButton button);
15 |
16 | void pressed(MouseButton button, boolean pressed);
17 |
18 | @Data
19 | final class CursorPositions {
20 | private final Vector2fc current;
21 | private final Vector2fc previous;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/input/Shortcut.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.input;
2 |
3 | import java.util.Set;
4 | import lombok.AllArgsConstructor;
5 | import lombok.Data;
6 | import lombok.NonNull;
7 |
8 | @Data
9 | @AllArgsConstructor
10 | public class Shortcut {
11 |
12 | @NonNull private KeyCode key;
13 | @NonNull private Set mods;
14 |
15 | public boolean check(@NonNull KeyCode key, @NonNull KeyMod... mods) {
16 | return check(key, Set.of(mods));
17 | }
18 |
19 | public boolean check(@NonNull KeyCode key, @NonNull Set mods) {
20 | return this.key.equals(key) && mods.size() == this.mods.size() && this.mods.containsAll(mods);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/input/ShortcutRegistry.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.input;
2 |
3 | /** Used to store shortcuts. */
4 | public interface ShortcutRegistry {
5 |
6 | String CUT = "CUT";
7 | String COPY = "COPY";
8 | String PASTE = "PASTE";
9 | String SELECT_ALL = "SELECT_ALL";
10 |
11 | /**
12 | * Used to set shortcut for specified name (for example 'copy').
13 | *
14 | * Name would be automatically trimmed and cast to lowercase.
15 | *
16 | * @param name shortcut name.
17 | * @param shortcut shortcut.
18 | */
19 | void shortcut(String name, Shortcut shortcut);
20 |
21 | /**
22 | * Used to remove specified shortcut.
23 | *
24 | *
Name would be automatically cast to lowercase.
25 | *
26 | * @param name shortcut name.
27 | */
28 | void removeShortcut(String name);
29 |
30 | /**
31 | * Used to get shortcut by name.
32 | *
33 | *
Name would be automatically cast to lowercase.
34 | *
35 | * @param name shortcut name.
36 | * @return shortcut for specified name or null if not found.
37 | */
38 | Shortcut shortcut(String name);
39 | }
40 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/input/impl/KeyboardLayoutImpl.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.input.impl;
2 |
3 | import com.google.common.collect.BiMap;
4 | import com.google.common.collect.HashBiMap;
5 | import com.spinyowl.spinygui.core.input.KeyCode;
6 | import com.spinyowl.spinygui.core.input.KeyboardLayout;
7 | import java.util.Map;
8 | import lombok.NonNull;
9 |
10 | /**
11 | * This class used to store key mapping to native keys. That allows user to have custom keyboard
12 | * layouts (that differ from standard QWERTY layout).
13 | */
14 | public class KeyboardLayoutImpl implements KeyboardLayout {
15 |
16 | private final BiMap keys;
17 |
18 | public KeyboardLayoutImpl(Map keys) {
19 | this.keys = HashBiMap.create(keys);
20 | }
21 |
22 | /**
23 | * Used to set mapping for specified keyCode.
24 | *
25 | * @param keyCode key code.
26 | * @param nativeCode native key code.
27 | */
28 | @Override
29 | public void updateMapping(@NonNull KeyCode keyCode, @NonNull Integer nativeCode) {
30 | keys.put(keyCode, nativeCode);
31 | }
32 |
33 | /**
34 | * Used to get mapped {@link KeyCode} by native key code.
35 | *
36 | * @param nativeCode native key code.
37 | * @return mapped {@link KeyCode} or null if not found.
38 | */
39 | @Override
40 | public KeyCode keyCode(@NonNull Integer nativeCode) {
41 | return keys.inverse().get(nativeCode);
42 | }
43 |
44 | /**
45 | * Used to get mapped native key code by {@link KeyCode}.
46 | *
47 | * @param keyCode key code.
48 | * @return mapped native key code or null if not found.
49 | */
50 | @Override
51 | public Integer nativeCode(@NonNull KeyCode keyCode) {
52 | return keys.get(keyCode);
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/input/impl/MouseServiceImpl.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.input.impl;
2 |
3 | import com.spinyowl.spinygui.core.input.MouseButton;
4 | import com.spinyowl.spinygui.core.input.MouseService;
5 | import com.spinyowl.spinygui.core.node.Frame;
6 | import java.util.EnumMap;
7 | import java.util.IdentityHashMap;
8 | import java.util.Map;
9 | import org.joml.Vector2f;
10 |
11 | /** Default implementation of MouseService. */
12 | public class MouseServiceImpl implements MouseService {
13 | private final Map cursorPositionsMap = new IdentityHashMap<>();
14 | private final Map buttons = new EnumMap<>(MouseButton.class);
15 |
16 | @Override
17 | public CursorPositions getCursorPositions(Frame frame) {
18 | return cursorPositionsMap.getOrDefault(
19 | frame, new CursorPositions(new Vector2f(), new Vector2f()));
20 | }
21 |
22 | @Override
23 | public void setCursorPositions(Frame frame, CursorPositions positions) {
24 | cursorPositionsMap.put(frame, positions);
25 | }
26 |
27 | @Override
28 | public boolean pressed(MouseButton button) {
29 | return buttons.getOrDefault(button, false);
30 | }
31 |
32 | @Override
33 | public void pressed(MouseButton button, boolean pressed) {
34 | buttons.put(button, pressed);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/input/impl/ShortcutRegistryImpl.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.input.impl;
2 |
3 | import com.spinyowl.spinygui.core.input.Shortcut;
4 | import com.spinyowl.spinygui.core.input.ShortcutRegistry;
5 | import java.util.HashMap;
6 | import java.util.Map;
7 | import lombok.NonNull;
8 |
9 | /** Used to store shortcuts. */
10 | public class ShortcutRegistryImpl implements ShortcutRegistry {
11 |
12 | private final Map shortcuts = new HashMap<>();
13 |
14 | /**
15 | * Used to set shortcut for specified name (for example 'copy').
16 | *
17 | * Name would be automatically trimmed and cast to lowercase.
18 | *
19 | * @param name shortcut name.
20 | * @param shortcut shortcut.
21 | */
22 | @Override
23 | public void shortcut(@NonNull String name, @NonNull Shortcut shortcut) {
24 | shortcuts.put(name.toLowerCase().trim(), shortcut);
25 | }
26 |
27 | /**
28 | * Used to remove specified shortcut.
29 | *
30 | *
Name would be automatically cast to lowercase.
31 | *
32 | * @param name shortcut name.
33 | */
34 | @Override
35 | public void removeShortcut(@NonNull String name) {
36 | shortcuts.remove(name.toLowerCase().trim());
37 | }
38 |
39 | /**
40 | * Used to get shortcut by name.
41 | *
42 | *
Name would be automatically cast to lowercase.
43 | *
44 | * @param name shortcut name.
45 | * @return shortcut for specified name or null if not found.
46 | */
47 | @Override
48 | public Shortcut shortcut(@NonNull String name) {
49 | return shortcuts.get(name.toLowerCase().trim());
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/layout/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SpinyOwl/SpinyGUI/4db014a5fb9b5adb485e100089b31d28c0d45995/core/src/main/java/com/spinyowl/spinygui/core/layout/.gitkeep
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/layout/ElementLayout.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.layout;
2 |
3 | import com.spinyowl.spinygui.core.node.Element;
4 |
5 | /** Defines branch for element node layout implementations. */
6 | public interface ElementLayout extends Layout {
7 | /** {@inheritDoc} */
8 | void layout(Element element, LayoutContext context);
9 | }
10 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/layout/Layout.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.layout;
2 |
3 | import com.spinyowl.spinygui.core.node.Node;
4 |
5 | /** Layout manager. Used to layout provided element of specified display type. */
6 | public interface Layout {
7 |
8 | /**
9 | * Used to lay out element, and it's child nodes.
10 | *
11 | * @param element element to lay out.
12 | */
13 | void layout(T element, LayoutContext context);
14 | }
15 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/layout/LayoutContext.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.layout;
2 |
3 | import com.spinyowl.spinygui.core.node.Node;
4 | import lombok.Getter;
5 | import lombok.Setter;
6 |
7 | @Getter
8 | @Setter
9 | public class LayoutContext {
10 | private Float lastTextEndX;
11 | private Float lastTextEndY;
12 | private Float lastBlockBottomY;
13 | private Node previousNode;
14 |
15 | public void cleanup() {
16 | lastTextEndX = null;
17 | lastTextEndY = null;
18 | previousNode = null;
19 | lastBlockBottomY = null;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/layout/LayoutService.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.layout;
2 |
3 | import com.spinyowl.spinygui.core.node.Element;
4 | import com.spinyowl.spinygui.core.node.Frame;
5 | import com.spinyowl.spinygui.core.node.Node;
6 | import lombok.NonNull;
7 |
8 | /** Layout service is an entry point to layout system. Used to layout provided element. */
9 | public interface LayoutService {
10 |
11 | /**
12 | * Used to layout element tree.
13 | *
14 | * @param frame frame to lay out.
15 | */
16 | void layout(@NonNull Frame frame);
17 |
18 | /**
19 | * Used to layout node tree.
20 | * For internal use by layout service.
21 | *
22 | * @param node node to layout.
23 | */
24 | void layoutNode(@NonNull Node node, @NonNull LayoutContext context);
25 |
26 | /**
27 | * Used to layout element tree.
28 | *
29 | * @param element element to layout.
30 | */
31 | void layoutChildNodes(@NonNull Element element, @NonNull LayoutContext context);
32 | }
33 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/layout/TextLayout.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.layout;
2 |
3 | import com.spinyowl.spinygui.core.node.Text;
4 |
5 | /** Defines branch for text node layout implementations. */
6 | public interface TextLayout extends Layout {
7 | void layout(Text element, LayoutContext context);
8 | }
9 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/layout/impl/LayoutServiceProvider.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.layout.impl;
2 |
3 | import com.spinyowl.spinygui.core.event.processor.EventProcessor;
4 | import com.spinyowl.spinygui.core.layout.ElementLayout;
5 | import com.spinyowl.spinygui.core.layout.LayoutService;
6 | import com.spinyowl.spinygui.core.style.types.Display;
7 | import com.spinyowl.spinygui.core.system.event.processor.SystemEventProcessor;
8 | import com.spinyowl.spinygui.core.system.font.FontService;
9 | import com.spinyowl.spinygui.core.time.TimeService;
10 | import java.util.HashMap;
11 | import lombok.AccessLevel;
12 | import lombok.NoArgsConstructor;
13 | import lombok.NonNull;
14 |
15 | @NoArgsConstructor(access = AccessLevel.PRIVATE)
16 | public final class LayoutServiceProvider {
17 | public static LayoutService create(
18 | @NonNull SystemEventProcessor systemEventProcessor,
19 | @NonNull EventProcessor eventProcessor,
20 | @NonNull TimeService timeService,
21 | @NonNull FontService fontService) {
22 |
23 | var textLayout = new TextLayoutImpl(fontService);
24 | var elementLayoutMap = new HashMap();
25 | LayoutService layoutService = new LayoutServiceImpl(textLayout, elementLayoutMap);
26 |
27 | elementLayoutMap.put(Display.NONE, new NoneLayout());
28 |
29 | var blockLayout = new BlockLayout(layoutService);
30 | elementLayoutMap.put(Display.BLOCK, blockLayout);
31 |
32 | var flexLayout =
33 | new FlexLayout(
34 | systemEventProcessor, eventProcessor, timeService, blockLayout, layoutService);
35 | elementLayoutMap.put(Display.FLEX, flexLayout);
36 |
37 | return layoutService;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/layout/impl/NoneLayout.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.layout.impl;
2 |
3 | import com.spinyowl.spinygui.core.layout.ElementLayout;
4 | import com.spinyowl.spinygui.core.layout.LayoutContext;
5 | import com.spinyowl.spinygui.core.node.Element;
6 |
7 | public class NoneLayout implements ElementLayout {
8 |
9 | /** {@inheritDoc} */
10 | @Override
11 | public void layout(Element element, LayoutContext context) {
12 | // do nothing, as there is no need to do anything with this or child nodes, it is excluded.
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/node/EmptyElement.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.node;
2 |
3 | import java.util.Collections;
4 | import java.util.List;
5 | import lombok.EqualsAndHashCode;
6 | import lombok.Getter;
7 | import lombok.NonNull;
8 | import lombok.Setter;
9 | import lombok.ToString;
10 |
11 | /** Defines node that can not contain child elements. */
12 | @Getter
13 | @Setter
14 | @ToString
15 | @EqualsAndHashCode
16 | public class EmptyElement extends Element {
17 |
18 | public EmptyElement(String nodeName) {
19 | super(nodeName);
20 | }
21 |
22 | /**
23 | * Used to get child nodes.
24 | *
25 | * For empty node returns empty list.
26 | *
27 | * @return list of child nodes.
28 | */
29 | @Override
30 | public List childNodes() {
31 | return Collections.emptyList();
32 | }
33 |
34 | /**
35 | * Child operations are not supported for {@link EmptyElement}.
36 | *
37 | * @param node node.
38 | * @throws UnsupportedOperationException because child operations are not supported for {@link
39 | * EmptyElement}.
40 | */
41 | @Override
42 | public final void removeChild(@NonNull Node node) {
43 | throw new UnsupportedOperationException("Child operations are not supported for EmptyNode.");
44 | }
45 |
46 | /**
47 | * Child operations are not supported for {@link EmptyElement}.
48 | *
49 | * @param node node.
50 | * @throws UnsupportedOperationException because child operations are not supported for {@link
51 | * EmptyElement}.
52 | */
53 | @Override
54 | public final void addChild(@NonNull Node node) {
55 | throw new UnsupportedOperationException("Child operations are not supported for EmptyNode.");
56 | }
57 |
58 | /**
59 | * Returns true if an element has any child nodes, otherwise false.
60 | *
61 | * @return true if an element has any child nodes, otherwise false.
62 | */
63 | @Override
64 | public boolean hasChildNodes() {
65 | return false;
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/node/intersection/Intersection.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.node.intersection;
2 |
3 | import com.spinyowl.spinygui.core.node.Node;
4 | import org.joml.Vector2fc;
5 |
6 | /**
7 | * Intersection class specifies intersection rules for node and point. Used to detect
8 | * intersection of point on virtual window surface and node.
9 | */
10 | public interface Intersection {
11 |
12 | /**
13 | * Intersection rule.
14 | *
15 | * @param node node to check intersection.
16 | * @param point vector with coordinates of point to check intersection.
17 | * @return true if node intersected by point.
18 | */
19 | boolean intersects(Node node, Vector2fc point);
20 | }
21 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/node/intersection/Intersections.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.node.intersection;
2 |
3 | public final class Intersections {
4 |
5 | public static final Intersection RECTANGLE_INTERSECTION = new RectangleIntersection();
6 |
7 | private static Intersection defaultIntersection = RECTANGLE_INTERSECTION;
8 |
9 | private Intersections() {}
10 |
11 | public static Intersection getDefaultIntersection() {
12 | return defaultIntersection;
13 | }
14 |
15 | public static void setDefaultIntersection(Intersection defaultIntersection) {
16 | if (defaultIntersection != null) {
17 | Intersections.defaultIntersection = defaultIntersection;
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/node/intersection/RectangleIntersection.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.node.intersection;
2 |
3 | import com.spinyowl.spinygui.core.node.Node;
4 | import lombok.EqualsAndHashCode;
5 | import lombok.ToString;
6 | import org.joml.Vector2fc;
7 |
8 | @ToString
9 | @EqualsAndHashCode
10 | public class RectangleIntersection implements Intersection {
11 |
12 | @Override
13 | public boolean intersects(Node node, Vector2fc point) {
14 | Vector2fc pos = node.absolutePosition();
15 | Vector2fc size = node.size();
16 | return point.x() >= pos.x()
17 | && point.x() < pos.x() + size.x()
18 | && point.y() >= pos.y()
19 | && point.y() < pos.y() + size.y();
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/node/layout/Edges.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.node.layout;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Data;
5 | import lombok.NoArgsConstructor;
6 |
7 | @Data
8 | @NoArgsConstructor
9 | @AllArgsConstructor
10 | public final class Edges {
11 |
12 | private float top;
13 | private float right;
14 | private float bottom;
15 | private float left;
16 | }
17 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/node/layout/Rect.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.node.layout;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Data;
5 | import lombok.NoArgsConstructor;
6 | import org.joml.Vector2f;
7 |
8 | @Data
9 | @NoArgsConstructor
10 | @AllArgsConstructor
11 | public final class Rect {
12 | private float x;
13 | private float y;
14 | private float width;
15 | private float height;
16 |
17 | public Rect expandedBy(Edges... edges) {
18 | var rect = new Rect(x, y, width, height);
19 | for (var edge : edges) {
20 | rect.x(rect.x() - edge.left());
21 | rect.y(rect.y() - edge.top());
22 | rect.width(rect.width() + edge.left() + edge.right());
23 | rect.height(rect.height() + edge.top() + edge.bottom());
24 | }
25 | return rect;
26 | }
27 |
28 | public Vector2f position() {
29 | return new Vector2f(x, y);
30 | }
31 |
32 | public Vector2f size() {
33 | return new Vector2f(width, height);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/parser/NodeParser.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.parser;
2 |
3 | import com.spinyowl.spinygui.core.node.Node;
4 |
5 | /** Node marshaller. Used to convert node to xml and vise versa. */
6 | public interface NodeParser {
7 |
8 | Node fromHtml(String xml);
9 |
10 | String toHtml(Node node);
11 |
12 | String toHtml(Node node, boolean pretty);
13 | }
14 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/parser/StyleSheetParser.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.parser;
2 |
3 | import com.spinyowl.spinygui.core.style.stylesheet.Declaration;
4 | import com.spinyowl.spinygui.core.style.stylesheet.Ruleset;
5 | import com.spinyowl.spinygui.core.style.stylesheet.StyleSheet;
6 | import java.util.List;
7 |
8 | /** Used to read stylesheets from css. */
9 | public interface StyleSheetParser {
10 |
11 | StyleSheet parse(String css);
12 |
13 | List parseDeclarations(String css);
14 |
15 | String toCss(StyleSheet styleSheet);
16 |
17 | String toCss(Ruleset ruleSet);
18 |
19 | String toCss(Declaration declaration);
20 | }
21 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/parser/impl/StyleSheetParserFactory.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.parser.impl;
2 |
3 | import com.spinyowl.spinygui.core.parser.StyleSheetParser;
4 | import com.spinyowl.spinygui.core.parser.impl.css.visitor.AtRuleVisitor;
5 | import com.spinyowl.spinygui.core.parser.impl.css.visitor.DeclarationListVisitor;
6 | import com.spinyowl.spinygui.core.parser.impl.css.visitor.DeclarationVisitor;
7 | import com.spinyowl.spinygui.core.parser.impl.css.visitor.PropertyValueVisitor;
8 | import com.spinyowl.spinygui.core.parser.impl.css.visitor.RulesetVisitor;
9 | import com.spinyowl.spinygui.core.parser.impl.css.visitor.SelectorGroupVisitor;
10 | import com.spinyowl.spinygui.core.parser.impl.css.visitor.SelectorVisitor;
11 | import com.spinyowl.spinygui.core.parser.impl.css.visitor.StyleSheetVisitor;
12 | import com.spinyowl.spinygui.core.style.stylesheet.PropertyStore;
13 | import lombok.AccessLevel;
14 | import lombok.NoArgsConstructor;
15 |
16 | @NoArgsConstructor(access = AccessLevel.PRIVATE)
17 | public final class StyleSheetParserFactory {
18 |
19 | /**
20 | * Creates stylesheet parser using {@link DefaultStyleSheetParser} implementation.
21 | *
22 | * @param propertyStore property store with properties definitions.
23 | * @return style sheet parser.
24 | */
25 | public static StyleSheetParser createParser(PropertyStore propertyStore) {
26 | var selectorVisitor = new SelectorVisitor();
27 | var selectorGroupVisitor = new SelectorGroupVisitor(selectorVisitor);
28 | var propertyValueVisitor = new PropertyValueVisitor();
29 | var propertyVisitor = new DeclarationVisitor(propertyStore, propertyValueVisitor);
30 | var propertyListVisitor = new DeclarationListVisitor(propertyVisitor);
31 | var rulesetVisitor = new RulesetVisitor(selectorGroupVisitor, propertyListVisitor);
32 | var atRuleVisitor = new AtRuleVisitor();
33 | var styleSheetVisitor = new StyleSheetVisitor(rulesetVisitor, atRuleVisitor);
34 | return new DefaultStyleSheetParser(styleSheetVisitor, propertyListVisitor);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/parser/impl/css/antlr/CSS3.tokens:
--------------------------------------------------------------------------------
1 | T__0=1
2 | T__1=2
3 | T__2=3
4 | T__3=4
5 | T__4=5
6 | T__5=6
7 | T__6=7
8 | T__7=8
9 | T__8=9
10 | T__9=10
11 | T__10=11
12 | T__11=12
13 | T__12=13
14 | T__13=14
15 | T__14=15
16 | Comment=16
17 | Space=17
18 | Cdo=18
19 | Cdc=19
20 | Includes=20
21 | DashMatch=21
22 | Hash=22
23 | Import=23
24 | Page=24
25 | Media=25
26 | Namespace=26
27 | Charset=27
28 | Important=28
29 | Percentage=29
30 | Uri=30
31 | UnicodeRange=31
32 | MediaOnly=32
33 | Not=33
34 | And=34
35 | Dimension=35
36 | UnknownDimension=36
37 | Plus=37
38 | Minus=38
39 | Greater=39
40 | Comma=40
41 | Tilde=41
42 | PseudoNot=42
43 | Number=43
44 | String_=44
45 | PrefixMatch=45
46 | SuffixMatch=46
47 | SubstringMatch=47
48 | FontFace=48
49 | Supports=49
50 | Or=50
51 | Keyframes=51
52 | From=52
53 | To=53
54 | Calc=54
55 | Viewport=55
56 | CounterStyle=56
57 | FontFeatureValues=57
58 | DxImageTransform=58
59 | Variable=59
60 | Var=60
61 | Ident=61
62 | Function_=62
63 | ';'=1
64 | '('=2
65 | ':'=3
66 | ')'=4
67 | '{'=5
68 | '}'=6
69 | '*'=7
70 | '|'=8
71 | '.'=9
72 | '['=10
73 | '='=11
74 | ']'=12
75 | '/'=13
76 | '_'=14
77 | '@'=15
78 | ''=19
80 | '~='=20
81 | '|='=21
82 | '@charset '=27
83 | '+'=37
84 | '-'=38
85 | '>'=39
86 | ','=40
87 | '~'=41
88 | '^='=45
89 | '$='=46
90 | '*='=47
91 | 'calc('=54
92 | 'var('=60
93 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/parser/impl/css/antlr/CSS3Lexer.tokens:
--------------------------------------------------------------------------------
1 | T__0=1
2 | T__1=2
3 | T__2=3
4 | T__3=4
5 | T__4=5
6 | T__5=6
7 | T__6=7
8 | T__7=8
9 | T__8=9
10 | T__9=10
11 | T__10=11
12 | T__11=12
13 | T__12=13
14 | T__13=14
15 | T__14=15
16 | Comment=16
17 | Space=17
18 | Cdo=18
19 | Cdc=19
20 | Includes=20
21 | DashMatch=21
22 | Hash=22
23 | Import=23
24 | Page=24
25 | Media=25
26 | Namespace=26
27 | Charset=27
28 | Important=28
29 | Percentage=29
30 | Uri=30
31 | UnicodeRange=31
32 | MediaOnly=32
33 | Not=33
34 | And=34
35 | Dimension=35
36 | UnknownDimension=36
37 | Plus=37
38 | Minus=38
39 | Greater=39
40 | Comma=40
41 | Tilde=41
42 | PseudoNot=42
43 | Number=43
44 | String_=44
45 | PrefixMatch=45
46 | SuffixMatch=46
47 | SubstringMatch=47
48 | FontFace=48
49 | Supports=49
50 | Or=50
51 | Keyframes=51
52 | From=52
53 | To=53
54 | Calc=54
55 | Viewport=55
56 | CounterStyle=56
57 | FontFeatureValues=57
58 | DxImageTransform=58
59 | Variable=59
60 | Var=60
61 | Ident=61
62 | Function_=62
63 | ';'=1
64 | '('=2
65 | ':'=3
66 | ')'=4
67 | '{'=5
68 | '}'=6
69 | '*'=7
70 | '|'=8
71 | '.'=9
72 | '['=10
73 | '='=11
74 | ']'=12
75 | '/'=13
76 | '_'=14
77 | '@'=15
78 | ''=19
80 | '~='=20
81 | '|='=21
82 | '@charset '=27
83 | '+'=37
84 | '-'=38
85 | '>'=39
86 | ','=40
87 | '~'=41
88 | '^='=45
89 | '$='=46
90 | '*='=47
91 | 'calc('=54
92 | 'var('=60
93 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/parser/impl/css/visitor/DeclarationListVisitor.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.parser.impl.css.visitor;
2 |
3 | import com.spinyowl.spinygui.core.parser.impl.css.antlr.CSS3BaseVisitor;
4 | import com.spinyowl.spinygui.core.parser.impl.css.antlr.CSS3Parser.DeclarationListContext;
5 | import com.spinyowl.spinygui.core.style.stylesheet.Declaration;
6 | import java.util.List;
7 | import java.util.Objects;
8 | import lombok.NonNull;
9 | import lombok.RequiredArgsConstructor;
10 | import lombok.extern.slf4j.Slf4j;
11 |
12 | @Slf4j
13 | @RequiredArgsConstructor
14 | public class DeclarationListVisitor extends CSS3BaseVisitor> {
15 |
16 | @NonNull private final DeclarationVisitor declarationVisitor;
17 |
18 | @Override
19 | public List visitDeclarationList(DeclarationListContext ctx) {
20 | return ctx.declaration().stream()
21 | .map(declarationVisitor::visit)
22 | .filter(Objects::nonNull)
23 | .toList();
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/parser/impl/css/visitor/DeclarationVisitor.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.parser.impl.css.visitor;
2 |
3 | import com.spinyowl.spinygui.core.parser.impl.css.antlr.CSS3BaseVisitor;
4 | import com.spinyowl.spinygui.core.parser.impl.css.antlr.CSS3Parser.KnownDeclarationContext;
5 | import com.spinyowl.spinygui.core.style.stylesheet.Declaration;
6 | import com.spinyowl.spinygui.core.style.stylesheet.Property;
7 | import com.spinyowl.spinygui.core.style.stylesheet.PropertyStore;
8 | import com.spinyowl.spinygui.core.style.stylesheet.Term;
9 | import lombok.NonNull;
10 | import lombok.RequiredArgsConstructor;
11 | import lombok.extern.slf4j.Slf4j;
12 |
13 | @Slf4j
14 | @RequiredArgsConstructor
15 | public class DeclarationVisitor extends CSS3BaseVisitor {
16 |
17 | @NonNull private final PropertyStore propertyStore;
18 | @NonNull private final PropertyValueVisitor propertyValueVisitor;
19 |
20 | @Override
21 | public Declaration visitKnownDeclaration(KnownDeclarationContext ctx) {
22 | String propertyName = ctx.property_().getText();
23 | Property property = propertyStore.getProperty(propertyName);
24 |
25 | log.debug("Reading property: {}", propertyName);
26 | String stringValue = ctx.expr().getText();
27 | Term> term = propertyValueVisitor.visit(ctx.expr());
28 |
29 |
30 | if (property != null && stringValue != null) {
31 | log.debug("Reading property: {} - Done.\n", propertyName);
32 | return new Declaration(property, term);
33 | }
34 |
35 | log.warn("Can't parse '{}' property with '{}' value", propertyName, stringValue);
36 | return super.visitKnownDeclaration(ctx);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/parser/impl/css/visitor/RulesetVisitor.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.parser.impl.css.visitor;
2 |
3 | import com.spinyowl.spinygui.core.parser.impl.css.antlr.CSS3BaseVisitor;
4 | import com.spinyowl.spinygui.core.parser.impl.css.antlr.CSS3Parser.KnownRulesetContext;
5 | import com.spinyowl.spinygui.core.style.stylesheet.Declaration;
6 | import com.spinyowl.spinygui.core.style.stylesheet.Ruleset;
7 | import com.spinyowl.spinygui.core.style.stylesheet.selector.Selector;
8 | import java.util.List;
9 | import lombok.NonNull;
10 | import lombok.RequiredArgsConstructor;
11 |
12 | @RequiredArgsConstructor
13 | public class RulesetVisitor extends CSS3BaseVisitor {
14 |
15 | @NonNull private final CSS3BaseVisitor> selectorGroupVisitor;
16 | @NonNull private final CSS3BaseVisitor> declarationListVisitor;
17 |
18 | /**
19 | * Grammar rule: selectorGroup '{' ws declarationList? '}' ws # knownRuleset
20 | *
21 | * @param ctx ruleset context.
22 | * @return read ruleset.
23 | */
24 | @Override
25 | public Ruleset visitKnownRuleset(KnownRulesetContext ctx) {
26 |
27 | var selectors = selectorGroupVisitor.visit(ctx.selectorGroup());
28 | var properties = declarationListVisitor.visit(ctx.declarationList());
29 |
30 | return new Ruleset(selectors, properties);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/parser/impl/css/visitor/SelectorGroupVisitor.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.parser.impl.css.visitor;
2 |
3 | import com.spinyowl.spinygui.core.parser.impl.css.antlr.CSS3BaseVisitor;
4 | import com.spinyowl.spinygui.core.parser.impl.css.antlr.CSS3Parser.SelectorGroupContext;
5 | import com.spinyowl.spinygui.core.style.stylesheet.selector.Selector;
6 | import java.util.List;
7 | import java.util.Objects;
8 | import lombok.NonNull;
9 | import lombok.RequiredArgsConstructor;
10 | import lombok.extern.slf4j.Slf4j;
11 |
12 | @Slf4j
13 | @RequiredArgsConstructor
14 | public class SelectorGroupVisitor extends CSS3BaseVisitor> {
15 |
16 | @NonNull private final CSS3BaseVisitor selectorVisitor;
17 |
18 | @Override
19 | public List visitSelectorGroup(SelectorGroupContext ctx) {
20 | return ctx.selector().stream().map(selectorVisitor::visit).filter(Objects::nonNull).toList();
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/parser/impl/css/visitor/StyleSheetVisitor.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.parser.impl.css.visitor;
2 |
3 | import com.spinyowl.spinygui.core.parser.impl.css.antlr.CSS3BaseVisitor;
4 | import com.spinyowl.spinygui.core.parser.impl.css.antlr.CSS3Parser.StylesheetContext;
5 | import com.spinyowl.spinygui.core.style.stylesheet.AtRule;
6 | import com.spinyowl.spinygui.core.style.stylesheet.Ruleset;
7 | import com.spinyowl.spinygui.core.style.stylesheet.StyleSheet;
8 | import java.util.Objects;
9 | import lombok.NonNull;
10 | import lombok.RequiredArgsConstructor;
11 |
12 | @RequiredArgsConstructor
13 | public class StyleSheetVisitor extends CSS3BaseVisitor {
14 |
15 | @NonNull private final CSS3BaseVisitor rulesetVisitor;
16 | @NonNull private final CSS3BaseVisitor atRuleVisitor;
17 |
18 | @Override
19 | public StyleSheet visitStylesheet(StylesheetContext ctx) {
20 | var ruleSetList =
21 | ctx.nestedStatement().stream().map(rulesetVisitor::visit).filter(Objects::nonNull).toList();
22 |
23 | var rules =
24 | ctx.nestedStatement().stream().map(atRuleVisitor::visit).filter(Objects::nonNull).toList();
25 |
26 | return new StyleSheet(ruleSetList, rules);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/manager/StyleManager.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.manager;
2 |
3 | import com.spinyowl.spinygui.core.node.Frame;
4 |
5 | public interface StyleManager {
6 |
7 | /** Recalculates all styles for provided frame and its children. */
8 | void recalculate(Frame frame);
9 | }
10 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/stylesheet/AtRule.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.stylesheet;
2 |
3 | public interface AtRule {}
4 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/stylesheet/Declaration.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.stylesheet;
2 |
3 | import static com.spinyowl.spinygui.core.style.stylesheet.term.TermIdent.INHERIT;
4 | import static com.spinyowl.spinygui.core.style.stylesheet.term.TermIdent.INITIAL;
5 |
6 | import com.spinyowl.spinygui.core.node.Element;
7 | import lombok.Data;
8 | import lombok.NonNull;
9 | import lombok.RequiredArgsConstructor;
10 |
11 | /** Declaration is combination of property and it's value. */
12 | @Data
13 | @RequiredArgsConstructor
14 | public class Declaration {
15 |
16 | /** Property definition. */
17 | @NonNull private final Property property;
18 |
19 | protected Term> term;
20 |
21 | private boolean enabled = true;
22 |
23 | public Declaration(@NonNull Property property, Term> term) {
24 | this.property = property;
25 | this.term = term;
26 | }
27 |
28 | public void apply(Element element) {
29 | if (enabled) {
30 | property.apply(element, term);
31 | }
32 | }
33 |
34 | /** Used to reset property value to default. */
35 | public void resetToDefault() {
36 | this.term = property.defaultValue();
37 | }
38 |
39 | @SuppressWarnings("squid:S2159")
40 | public boolean isInitial() {
41 | return INITIAL.equals(term);
42 | }
43 |
44 | @SuppressWarnings("squid:S2159")
45 | public boolean isInherit() {
46 | return INHERIT.equals(term);
47 | }
48 |
49 | @Override
50 | public String toString() {
51 | return property.name() + ": " + term.toString();
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/stylesheet/PropertiesScanner.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.stylesheet;
2 |
3 | import lombok.AccessLevel;
4 | import lombok.NoArgsConstructor;
5 | import lombok.extern.slf4j.Slf4j;
6 |
7 | @Slf4j
8 | @NoArgsConstructor(access = AccessLevel.PRIVATE)
9 | public final class PropertiesScanner {
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/stylesheet/PropertyProvider.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.stylesheet;
2 |
3 | import java.util.List;
4 |
5 | public interface PropertyProvider {
6 | List getProperties();
7 | }
8 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/stylesheet/PropertyStore.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.stylesheet;
2 |
3 | import java.util.List;
4 | import java.util.Map;
5 |
6 | public interface PropertyStore {
7 |
8 | /**
9 | * Returns property by property name.
10 | *
11 | * @param propertyName property name.
12 | * @return property.
13 | */
14 | Property getProperty(String propertyName);
15 |
16 | /**
17 | * Used to add property support.
18 | *
19 | * @param name property to support (will be converted to lower-case string).
20 | * @param property property supplier which will be used to create new {@link Property} instance.
21 | */
22 | void addProperty(String name, Property property);
23 |
24 | /**
25 | * Used to remove property binding.
26 | *
27 | * @param property property to remove.
28 | */
29 | void removeProperty(String property);
30 |
31 | /**
32 | * Returns unmodifiable list of names of supported properties.
33 | *
34 | * @return unmodifiable list of names of supported properties.
35 | */
36 | List getPropertyNames();
37 |
38 | /**
39 | * Returns unmodifiable map of supported properties.
40 | *
41 | * @return unmodifiable map of supported properties.
42 | */
43 | Map getPropertyMap();
44 | /**
45 | * Returns unmodifiable list of supported properties.
46 | *
47 | * @return unmodifiable list of supported properties.
48 | */
49 | List getProperties();
50 | }
51 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/stylesheet/PropertyStoreProvider.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.stylesheet;
2 |
3 | public interface PropertyStoreProvider {
4 |
5 | PropertyStore createPropertyStore();
6 | }
7 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/stylesheet/Specificity.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.stylesheet;
2 |
3 | import lombok.Data;
4 | import lombok.RequiredArgsConstructor;
5 |
6 | @Data
7 | @RequiredArgsConstructor(staticName = "of")
8 | public class Specificity implements Comparable {
9 |
10 | public static final Specificity ZERO = Specificity.of(0, 0, 0);
11 | public static final Specificity ID = Specificity.of(1, 0, 0);
12 | public static final Specificity CLASS = Specificity.of(0, 1, 0);
13 | public static final Specificity TYPE = Specificity.of(0, 0, 1);
14 |
15 | private final int idSpecificity;
16 | private final int classSpecificity;
17 | private final int typeSpecificity;
18 |
19 | public static Specificity max(Specificity left, Specificity right) {
20 | return left.compareTo(right) > 0 ? left : right;
21 | }
22 |
23 | public Specificity add(Specificity specificity) {
24 | return new Specificity(
25 | this.idSpecificity + specificity.idSpecificity,
26 | this.classSpecificity + specificity.classSpecificity,
27 | this.typeSpecificity + specificity.typeSpecificity);
28 | }
29 |
30 | @Override
31 | public int compareTo(Specificity o) {
32 | // @formatter:off
33 | if (this.idSpecificity > o.idSpecificity) return 1;
34 | if (this.idSpecificity < o.idSpecificity) return -1;
35 | if (this.classSpecificity > o.classSpecificity) return 1;
36 | if (this.classSpecificity < o.classSpecificity) return -1;
37 | return Integer.compare(this.typeSpecificity, o.typeSpecificity);
38 | // @formatter:on
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/stylesheet/StyleSheet.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.stylesheet;
2 |
3 | import com.spinyowl.spinygui.core.node.Element;
4 | import java.util.ArrayList;
5 | import java.util.Comparator;
6 | import java.util.List;
7 | import java.util.StringJoiner;
8 | import lombok.Data;
9 | import lombok.NonNull;
10 | import org.apache.commons.lang3.tuple.Pair;
11 |
12 | @Data
13 | public class StyleSheet {
14 |
15 | @NonNull private List rulesets;
16 | @NonNull private List atRules;
17 |
18 | /**
19 | * Used to search rules in stylesheet that are correspond to specified element.
20 | *
21 | * @param element element to search rules.
22 | * @return list of rules that are correspond to specified element.
23 | */
24 | public List searchRules(@NonNull Element element) {
25 | ArrayList result = new ArrayList<>();
26 | for (Ruleset ruleSet : rulesets) {
27 | if (ruleSet.test(element)) {
28 | result.add(ruleSet);
29 | }
30 | }
31 | result.sort(Comparator.comparing(o -> o.specificity(element)));
32 | return result;
33 | }
34 |
35 | /**
36 | * Used to get rules in stylesheet that correspond to specified element sorted by specificity.
37 | *
38 | * @param element element to search rules.
39 | * @return list of rules that correspond to specified element.
40 | */
41 | public List searchSpecificRules(@NonNull Element element) {
42 | return rulesets.stream()
43 | .map(rs -> Pair.of(rs, rs.specificity(element)))
44 | .filter(pair -> pair.getRight() != null)
45 | .sorted(Comparator.comparing(Pair::getRight))
46 | .map(Pair::getLeft)
47 | .toList();
48 | }
49 |
50 | @Override
51 | public String toString() {
52 | var rulesetJoiner = new StringJoiner("\n\n");
53 | for (Ruleset ruleSet : rulesets) {
54 | rulesetJoiner.add(ruleSet.toString());
55 | }
56 |
57 | return rulesetJoiner.toString();
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/stylesheet/Term.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.stylesheet;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Data;
5 | import lombok.NoArgsConstructor;
6 | import org.apache.commons.lang3.NotImplementedException;
7 |
8 | /**
9 | * Term is a part of a property value. For example, in the following declaration:
10 | * font-size: 12px;
11 | * font-size
is a property name, 12px
is a property value, 12
12 | * and px
are terms.
13 | *
14 | * Note: toString() method is used to generate css-compatible value.
15 | *
16 | * @param type of term value.
17 | */
18 | @Data
19 | @NoArgsConstructor
20 | @AllArgsConstructor
21 | public abstract class Term {
22 | protected T value;
23 |
24 | public String toString() {
25 | throw new NotImplementedException("toString() method is not implemented for " + getClass());
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/stylesheet/annotation/Priority.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.stylesheet.annotation;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | /**
9 | * Used to mark property provider to add properties to property store with higher priority. By
10 | * default, all tags added with 0 priority.
11 | *
12 | * Property providers with higher priority will be used to add properties to property store later
13 | * which means that properties with higher priority will override properties with lower priority.
14 | */
15 | @Retention(RetentionPolicy.RUNTIME)
16 | @Target(ElementType.TYPE)
17 | public @interface Priority {
18 |
19 | /**
20 | * Allows to overload some property / tag handler.
21 | *
22 | * @return priority - used only in case if there is more than one element with the same name.
23 | */
24 | int value() default 0;
25 | }
26 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/stylesheet/atrule/FontFaceRule.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.stylesheet.atrule;
2 |
3 | import com.spinyowl.spinygui.core.font.FontStretch;
4 | import com.spinyowl.spinygui.core.font.FontStyle;
5 | import com.spinyowl.spinygui.core.font.FontWeight;
6 | import com.spinyowl.spinygui.core.style.stylesheet.AtRule;
7 | import lombok.Data;
8 | import lombok.RequiredArgsConstructor;
9 |
10 | /** Font face at-rule. Used to store info about fonts that should be imported. */
11 | @Data
12 | @RequiredArgsConstructor
13 | public class FontFaceRule implements AtRule {
14 |
15 | /** Name of font family */
16 | private final String fontFamily;
17 |
18 | /** Path to source. Could be url or relative path to source on file system/resources. */
19 | private final String src;
20 |
21 | /** Font stretch. Default is {@link FontStretch#NORMAL}. */
22 | private FontStretch fontStretch = FontStretch.NORMAL;
23 |
24 | /** Font style. Default is {@link FontStyle#NORMAL} */
25 | private FontStyle fontStyle = FontStyle.NORMAL;
26 |
27 | /** Font weight. Default is {@link FontWeight#NORMAL} */
28 | private FontWeight fontWeight = FontWeight.NORMAL;
29 | }
30 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/stylesheet/impl/DefaultPropertyStore.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.stylesheet.impl;
2 |
3 | import com.spinyowl.spinygui.core.style.stylesheet.Property;
4 | import com.spinyowl.spinygui.core.style.stylesheet.PropertyStore;
5 | import java.util.HashMap;
6 | import java.util.List;
7 | import java.util.Map;
8 | import java.util.Objects;
9 | import lombok.NonNull;
10 | import lombok.extern.slf4j.Slf4j;
11 |
12 | @Slf4j
13 | public class DefaultPropertyStore implements PropertyStore {
14 | private final Map propertyMap = new HashMap<>();
15 |
16 | @Override
17 | public Property getProperty(@NonNull String propertyName) {
18 | return propertyMap.get(propertyName.toLowerCase());
19 | }
20 |
21 | @Override
22 | public void addProperty(@NonNull String name, @NonNull Property property) {
23 | String propertyName = name.toLowerCase();
24 | if (!Objects.equals(propertyName, property.name())) {
25 | throw new IllegalArgumentException("Name should be the same as the property name.");
26 | }
27 |
28 | if (log.isWarnEnabled() && propertyMap.containsKey(propertyName)) {
29 | log.warn(
30 | "There is already exist property handler for {} : {}. Would be replaced by {}.",
31 | propertyName,
32 | propertyMap.get(propertyName).getClass().getName(),
33 | property.getClass().getName());
34 | }
35 |
36 | propertyMap.put(propertyName, property);
37 | }
38 |
39 | @Override
40 | public void removeProperty(@NonNull String property) {
41 | propertyMap.remove(property.toLowerCase());
42 | }
43 |
44 | @Override
45 | public List getPropertyNames() {
46 | return List.copyOf(propertyMap.keySet());
47 | }
48 |
49 | @Override
50 | public Map getPropertyMap() {
51 | return Map.copyOf(propertyMap);
52 | }
53 |
54 | @Override
55 | public List getProperties() {
56 | return List.copyOf(propertyMap.values());
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/stylesheet/property/ColorPropertyProvider.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.stylesheet.property;
2 |
3 | import static com.spinyowl.spinygui.core.style.stylesheet.Properties.COLOR;
4 | import static com.spinyowl.spinygui.core.style.stylesheet.Property.checkValue;
5 | import static com.spinyowl.spinygui.core.style.stylesheet.Property.put;
6 |
7 | import com.spinyowl.spinygui.core.style.stylesheet.Property;
8 | import com.spinyowl.spinygui.core.style.stylesheet.PropertyProvider;
9 | import com.spinyowl.spinygui.core.style.stylesheet.term.TermColor;
10 | import com.spinyowl.spinygui.core.style.stylesheet.term.TermIdent;
11 | import com.spinyowl.spinygui.core.style.types.Color;
12 | import java.util.List;
13 |
14 | public class ColorPropertyProvider implements PropertyProvider {
15 |
16 | @Override
17 | public List getProperties() {
18 | return List.of(
19 | Property.builder()
20 | .name(COLOR)
21 | .defaultValue(new TermColor(Color.BLACK))
22 | .inheritable(true)
23 | .animatable(true)
24 | .updater(put(COLOR, TermIdent.class, Color::get).or(put(COLOR, TermColor.class)))
25 | .validator(checkValue(TermIdent.class, Color::exists).or(TermColor.class::isInstance))
26 | .build());
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/stylesheet/property/DisplayPropertyProvider.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.stylesheet.property;
2 |
3 | import static com.spinyowl.spinygui.core.style.stylesheet.Properties.DISPLAY;
4 | import static com.spinyowl.spinygui.core.style.stylesheet.Property.checkValue;
5 | import static com.spinyowl.spinygui.core.style.stylesheet.Property.put;
6 | import static com.spinyowl.spinygui.core.style.types.Display.BLOCK;
7 |
8 | import com.spinyowl.spinygui.core.style.stylesheet.Property;
9 | import com.spinyowl.spinygui.core.style.stylesheet.PropertyProvider;
10 | import com.spinyowl.spinygui.core.style.stylesheet.term.TermIdent;
11 | import com.spinyowl.spinygui.core.style.types.Display;
12 | import java.util.List;
13 |
14 | public class DisplayPropertyProvider implements PropertyProvider {
15 |
16 | @Override
17 | public List getProperties() {
18 | return List.of(
19 | Property.builder()
20 | .name(DISPLAY)
21 | .defaultValue(new TermIdent(BLOCK.name()))
22 | .inheritable(true)
23 | .animatable(true)
24 | .updater(put(DISPLAY, TermIdent.class, Display::find))
25 | .validator(checkValue(TermIdent.class, Display::contains))
26 | .build());
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/stylesheet/property/OpacityPropertyProvider.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.stylesheet.property;
2 |
3 | import static com.spinyowl.spinygui.core.style.stylesheet.Properties.OPACITY;
4 | import static com.spinyowl.spinygui.core.style.stylesheet.Property.put;
5 |
6 | import com.spinyowl.spinygui.core.style.stylesheet.Property;
7 | import com.spinyowl.spinygui.core.style.stylesheet.PropertyProvider;
8 | import com.spinyowl.spinygui.core.style.stylesheet.term.TermFloat;
9 | import java.util.List;
10 |
11 | public class OpacityPropertyProvider implements PropertyProvider {
12 |
13 | @Override
14 | public List getProperties() {
15 | return List.of(
16 | Property.builder()
17 | .name(OPACITY)
18 | .defaultValue(new TermFloat(1f))
19 | .animatable(true)
20 | .updater(put(OPACITY, TermFloat.class))
21 | .validator(TermFloat.class::isInstance)
22 | .build());
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/stylesheet/property/PointerEventsPropertyProvider.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.stylesheet.property;
2 |
3 | import static com.spinyowl.spinygui.core.style.stylesheet.Properties.POINTER_EVENTS;
4 | import static com.spinyowl.spinygui.core.style.stylesheet.Property.checkValue;
5 | import static com.spinyowl.spinygui.core.style.stylesheet.Property.put;
6 |
7 | import com.spinyowl.spinygui.core.style.stylesheet.Property;
8 | import com.spinyowl.spinygui.core.style.stylesheet.PropertyProvider;
9 | import com.spinyowl.spinygui.core.style.stylesheet.term.TermIdent;
10 | import com.spinyowl.spinygui.core.style.types.PointerEvents;
11 | import java.util.List;
12 |
13 | public class PointerEventsPropertyProvider implements PropertyProvider {
14 |
15 | @Override
16 | public List getProperties() {
17 | return List.of(
18 | Property.builder()
19 | .name(POINTER_EVENTS)
20 | .defaultValue(new TermIdent(PointerEvents.AUTO.name()))
21 | .inheritable(true)
22 | .updater(put(POINTER_EVENTS, TermIdent.class, PointerEvents::find))
23 | .validator(checkValue(TermIdent.class, PointerEvents::contains))
24 | .build());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/stylesheet/property/PositionPropertyProvider.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.stylesheet.property;
2 |
3 | import static com.spinyowl.spinygui.core.style.stylesheet.Properties.POSITION;
4 | import static com.spinyowl.spinygui.core.style.stylesheet.Property.checkValue;
5 | import static com.spinyowl.spinygui.core.style.stylesheet.Property.put;
6 |
7 | import com.spinyowl.spinygui.core.style.stylesheet.Property;
8 | import com.spinyowl.spinygui.core.style.stylesheet.PropertyProvider;
9 | import com.spinyowl.spinygui.core.style.stylesheet.term.TermIdent;
10 | import com.spinyowl.spinygui.core.style.types.Position;
11 | import java.util.List;
12 |
13 | public class PositionPropertyProvider implements PropertyProvider {
14 |
15 | @Override
16 | public List getProperties() {
17 | return List.of(
18 | Property.builder()
19 | .name(POSITION)
20 | .defaultValue(new TermIdent(Position.STATIC.name()))
21 | .inheritable(true)
22 | .animatable(true)
23 | .updater(put(POSITION, TermIdent.class, Position::find))
24 | .validator(checkValue(TermIdent.class, Position::contains))
25 | .build());
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/stylesheet/property/TabSizePropertyProvider.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.stylesheet.property;
2 |
3 | import static com.spinyowl.spinygui.core.style.stylesheet.Properties.TAB_SIZE;
4 | import static com.spinyowl.spinygui.core.style.stylesheet.Property.put;
5 |
6 | import com.spinyowl.spinygui.core.style.stylesheet.Property;
7 | import com.spinyowl.spinygui.core.style.stylesheet.PropertyProvider;
8 | import com.spinyowl.spinygui.core.style.stylesheet.term.TermInteger;
9 | import java.util.List;
10 |
11 | public class TabSizePropertyProvider implements PropertyProvider {
12 |
13 | @Override
14 | public List getProperties() {
15 | return List.of(
16 | Property.builder()
17 | .name(TAB_SIZE)
18 | .defaultValue(new TermInteger(4))
19 | .inheritable(true)
20 | .updater(put(TAB_SIZE, TermInteger.class))
21 | .validator(TermInteger.class::isInstance)
22 | .build());
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/stylesheet/property/WhiteSpacePropertyProvider.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.stylesheet.property;
2 |
3 | import static com.spinyowl.spinygui.core.style.stylesheet.Properties.WHITE_SPACE;
4 | import static com.spinyowl.spinygui.core.style.stylesheet.Property.checkValue;
5 | import static com.spinyowl.spinygui.core.style.stylesheet.Property.put;
6 |
7 | import com.spinyowl.spinygui.core.style.stylesheet.Property;
8 | import com.spinyowl.spinygui.core.style.stylesheet.PropertyProvider;
9 | import com.spinyowl.spinygui.core.style.stylesheet.term.TermIdent;
10 | import com.spinyowl.spinygui.core.style.types.WhiteSpace;
11 | import java.util.List;
12 |
13 | public class WhiteSpacePropertyProvider implements PropertyProvider {
14 |
15 | @Override
16 | public List getProperties() {
17 | return List.of(
18 | Property.builder()
19 | .name(WHITE_SPACE)
20 | .defaultValue(new TermIdent(WhiteSpace.NORMAL.name()))
21 | .inheritable(true)
22 | .updater(put(WHITE_SPACE, TermIdent.class, WhiteSpace::find))
23 | .validator(checkValue(TermIdent.class, WhiteSpace::contains))
24 | .build());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/stylesheet/property/ZIndexPropertyProvider.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.stylesheet.property;
2 |
3 | import static com.spinyowl.spinygui.core.style.stylesheet.Properties.Z_INDEX;
4 | import static com.spinyowl.spinygui.core.style.stylesheet.Property.isAuto;
5 | import static com.spinyowl.spinygui.core.style.stylesheet.Property.put;
6 |
7 | import com.spinyowl.spinygui.core.style.stylesheet.Property;
8 | import com.spinyowl.spinygui.core.style.stylesheet.PropertyProvider;
9 | import com.spinyowl.spinygui.core.style.stylesheet.Term;
10 | import com.spinyowl.spinygui.core.style.stylesheet.term.TermIdent;
11 | import com.spinyowl.spinygui.core.style.stylesheet.term.TermInteger;
12 | import java.util.List;
13 |
14 | public class ZIndexPropertyProvider implements PropertyProvider {
15 |
16 | private static final Term> AUTO = new TermIdent("auto");
17 |
18 | @Override
19 | public List getProperties() {
20 | return List.of(
21 | Property.builder()
22 | .name(Z_INDEX)
23 | .defaultValue(AUTO)
24 | .updater(put(Z_INDEX, TermIdent.class, v -> 0).or(put(Z_INDEX, TermInteger.class)))
25 | .validator(isAuto().or(TermInteger.class::isInstance))
26 | .build());
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/stylesheet/selector/CombinatorSelector.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.stylesheet.selector;
2 |
3 | import com.spinyowl.spinygui.core.style.stylesheet.Specificity;
4 | import lombok.Getter;
5 | import lombok.NonNull;
6 | import lombok.RequiredArgsConstructor;
7 |
8 | @Getter
9 | @RequiredArgsConstructor
10 | public abstract class CombinatorSelector implements Selector {
11 |
12 | @NonNull protected final Selector first;
13 | @NonNull protected final Selector second;
14 |
15 | @Override
16 | public Specificity specificity() {
17 | return first.specificity().add(second.specificity());
18 | }
19 |
20 | @Override
21 | public boolean appliesToPseudoElement() {
22 | return first.appliesToPseudoElement() && second().appliesToPseudoElement();
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/stylesheet/selector/PseudoClassSelector.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.stylesheet.selector;
2 |
3 | import com.spinyowl.spinygui.core.style.stylesheet.Specificity;
4 |
5 | /**
6 | * Interface for pseudo-class selectors, which used to define a special state of an element.
7 | *
8 | * For example, it can be used to:
9 | *
10 | *
11 | * - Style an element when a user mouses over it
12 | *
- Style visited and unvisited links differently
13 | *
- Style an element when it gets focus
14 | *
15 | */
16 | public interface PseudoClassSelector extends Selector {
17 |
18 | @Override
19 | default Specificity specificity() {
20 | return Specificity.CLASS;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/stylesheet/selector/PseudoElementSelector.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.stylesheet.selector;
2 |
3 | import com.spinyowl.spinygui.core.node.Element;
4 | import com.spinyowl.spinygui.core.style.stylesheet.Specificity;
5 |
6 | /** Interface for pseudo-element selectors, which used to define a special inner part of element. */
7 | public interface PseudoElementSelector extends Selector {
8 |
9 | @Override
10 | default boolean test(Element element) {
11 | return true;
12 | }
13 |
14 | @Override
15 | default Specificity specificity() {
16 | return Specificity.TYPE;
17 | }
18 |
19 | @Override
20 | default boolean appliesToPseudoElement() {
21 | return true;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/stylesheet/selector/Selector.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.stylesheet.selector;
2 |
3 | import com.spinyowl.spinygui.core.node.Element;
4 | import com.spinyowl.spinygui.core.style.stylesheet.Specificity;
5 |
6 | /**
7 | * Style selector interface.
8 | * Style selectors are patterns used to select the elements you want to style.
9 | */
10 | public interface Selector extends Comparable {
11 |
12 | /**
13 | * Returns true if provided node could be selected using this selector.
14 | *
15 | * @param element node to test.
16 | * @return true if provided node could be selected using this selector.
17 | */
18 | boolean test(Element element);
19 |
20 | Specificity specificity();
21 |
22 | default Selector last() {
23 | return this;
24 | }
25 |
26 | @Override
27 | default int compareTo(Selector o) {
28 | return this.specificity().compareTo(o.specificity());
29 | }
30 |
31 | /** Used to determine if selector applies to pseudo element. */
32 | default boolean appliesToPseudoElement() {
33 | return false;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/stylesheet/selector/combinator/AdjacentSiblingSelector.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.stylesheet.selector.combinator;
2 |
3 | import com.spinyowl.spinygui.core.node.Element;
4 | import com.spinyowl.spinygui.core.style.stylesheet.selector.CombinatorSelector;
5 | import com.spinyowl.spinygui.core.style.stylesheet.selector.Selector;
6 | import java.util.Optional;
7 |
8 | /**
9 | * Adjacent Sibling Selector (+)
10 | *
11 | * The adjacent sibling selector is used to select an element that is directly after another
12 | * specific element.
13 | *
14 | *
Sibling elements must have the same parent element, and "adjacent" means "immediately
15 | * following".
16 | *
17 | *
Example: 'first + second'.
18 | */
19 | public class AdjacentSiblingSelector extends CombinatorSelector {
20 |
21 | public AdjacentSiblingSelector(Selector first, Selector second) {
22 | super(first, second);
23 | }
24 |
25 | @Override
26 | public boolean test(Element element) {
27 | return second.test(element)
28 | && Optional.ofNullable(element.previousElementSibling()).map(first::test).orElse(false);
29 | }
30 |
31 | @Override
32 | public String toString() {
33 | return first + " + " + second;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/stylesheet/selector/combinator/AndSelector.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.stylesheet.selector.combinator;
2 |
3 | import com.spinyowl.spinygui.core.node.Element;
4 | import com.spinyowl.spinygui.core.style.stylesheet.selector.CombinatorSelector;
5 | import com.spinyowl.spinygui.core.style.stylesheet.selector.Selector;
6 |
7 | public class AndSelector extends CombinatorSelector {
8 |
9 | public AndSelector(Selector first, Selector second) {
10 | super(first, second);
11 | }
12 |
13 | @Override
14 | public boolean test(Element element) {
15 | return first.test(element) && second.test(element);
16 | }
17 |
18 | @Override
19 | public String toString() {
20 | return "" + first + second;
21 | }
22 |
23 | @Override
24 | public boolean appliesToPseudoElement() {
25 | return first.appliesToPseudoElement() || second.appliesToPseudoElement();
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/stylesheet/selector/combinator/ChildSelector.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.stylesheet.selector.combinator;
2 |
3 | import com.spinyowl.spinygui.core.node.Element;
4 | import com.spinyowl.spinygui.core.style.stylesheet.selector.CombinatorSelector;
5 | import com.spinyowl.spinygui.core.style.stylesheet.selector.Selector;
6 |
7 | /**
8 | * Child Selector (>)
9 | *
10 | *
The child selector selects all elements that are the children of a specified element.
11 | *
12 | *
Example: 'first > second'.
13 | */
14 | public class ChildSelector extends CombinatorSelector {
15 |
16 | public ChildSelector(Selector first, Selector second) {
17 | super(first, second);
18 | }
19 |
20 | @Override
21 | public boolean test(Element t) {
22 | return t.parent() != null && second.test(t) && first.test(t.parent());
23 | }
24 |
25 | @Override
26 | public String toString() {
27 | return first + " > " + second;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/stylesheet/selector/combinator/DescendantSelector.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.stylesheet.selector.combinator;
2 |
3 | import com.spinyowl.spinygui.core.node.Element;
4 | import com.spinyowl.spinygui.core.style.stylesheet.selector.CombinatorSelector;
5 | import com.spinyowl.spinygui.core.style.stylesheet.selector.Selector;
6 |
7 | /**
8 | * Descendant Selector
9 | *
10 | *
The descendant selector matches all elements that are descendants of a specified element.
11 | *
12 | *
Example: 'first second'.
13 | */
14 | public class DescendantSelector extends CombinatorSelector {
15 |
16 | public DescendantSelector(Selector first, Selector second) {
17 | super(first, second);
18 | }
19 |
20 | @Override
21 | public boolean test(Element element) {
22 | boolean componentTest = second.test(element);
23 | if (!componentTest) {
24 | return false;
25 | }
26 | var parent = element.parent();
27 | while (parent != null) {
28 | if (first.test(parent)) {
29 | return true;
30 | }
31 | parent = parent.parent();
32 | }
33 | return false;
34 | }
35 |
36 | @Override
37 | public String toString() {
38 | return first + " " + second;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/stylesheet/selector/combinator/GeneralSiblingSelector.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.stylesheet.selector.combinator;
2 |
3 | import com.spinyowl.spinygui.core.node.Element;
4 | import com.spinyowl.spinygui.core.style.stylesheet.selector.CombinatorSelector;
5 | import com.spinyowl.spinygui.core.style.stylesheet.selector.Selector;
6 |
7 | /**
8 | * General Sibling Selector (~)
9 | *
10 | *
The general sibling selector selects all elements that are siblings of a specified element.
11 | *
12 | *
Example: 'first ~ second'.
13 | */
14 | public class GeneralSiblingSelector extends CombinatorSelector {
15 |
16 | public GeneralSiblingSelector(Selector first, Selector second) {
17 | super(first, second);
18 | }
19 |
20 | @Override
21 | public boolean test(Element element) {
22 | if (!second.test(element)) {
23 | return false;
24 | }
25 |
26 | Element previous;
27 | while ((previous = element.previousElementSibling()) != null) {
28 | if (first.test(previous)) {
29 | return true;
30 | }
31 | }
32 | return false;
33 | }
34 |
35 | @Override
36 | public String toString() {
37 | return first + " ~ " + second;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/stylesheet/selector/pseudoclass/HoverSelector.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.stylesheet.selector.pseudoclass;
2 |
3 | import com.spinyowl.spinygui.core.node.Element;
4 | import com.spinyowl.spinygui.core.style.stylesheet.selector.PseudoClassSelector;
5 |
6 | public class HoverSelector implements PseudoClassSelector {
7 |
8 | @Override
9 | public boolean test(Element element) {
10 | return element.hovered();
11 | }
12 |
13 | @Override
14 | public String toString() {
15 | return ":hover";
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/stylesheet/selector/pseudoelement/AfterSelector.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.stylesheet.selector.pseudoelement;
2 |
3 | import com.spinyowl.spinygui.core.style.stylesheet.Specificity;
4 | import com.spinyowl.spinygui.core.style.stylesheet.selector.PseudoElementSelector;
5 |
6 | public class AfterSelector implements PseudoElementSelector {
7 |
8 | @Override
9 | public Specificity specificity() {
10 | return Specificity.TYPE;
11 | }
12 |
13 | @Override
14 | public String toString() {
15 | return "::after";
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/stylesheet/selector/pseudoelement/BeforeSelector.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.stylesheet.selector.pseudoelement;
2 |
3 | import com.spinyowl.spinygui.core.style.stylesheet.Specificity;
4 | import com.spinyowl.spinygui.core.style.stylesheet.selector.PseudoElementSelector;
5 |
6 | public class BeforeSelector implements PseudoElementSelector {
7 |
8 | @Override
9 | public Specificity specificity() {
10 | return Specificity.TYPE;
11 | }
12 |
13 | @Override
14 | public String toString() {
15 | return "::before";
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/stylesheet/selector/pseudoelement/ScrollbarSelector.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.stylesheet.selector.pseudoelement;
2 |
3 | import com.spinyowl.spinygui.core.style.stylesheet.Specificity;
4 | import com.spinyowl.spinygui.core.style.stylesheet.selector.PseudoElementSelector;
5 |
6 | public class ScrollbarSelector implements PseudoElementSelector {
7 |
8 | @Override
9 | public Specificity specificity() {
10 | return Specificity.TYPE;
11 | }
12 |
13 | @Override
14 | public String toString() {
15 | return "::scrollbar";
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/stylesheet/selector/simple/AllSelector.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.stylesheet.selector.simple;
2 |
3 | import com.spinyowl.spinygui.core.node.Element;
4 | import com.spinyowl.spinygui.core.style.stylesheet.Specificity;
5 | import com.spinyowl.spinygui.core.style.stylesheet.selector.Selector;
6 |
7 | public class AllSelector implements Selector {
8 |
9 | /**
10 | * Returns true if provided node could be selected using this selector.
11 | *
12 | * @param element node to test.
13 | * @return true if provided node could be selected using this selector.
14 | */
15 | @Override
16 | public boolean test(Element element) {
17 | return true;
18 | }
19 |
20 | @Override
21 | public Specificity specificity() {
22 | return Specificity.ZERO;
23 | }
24 |
25 | @Override
26 | public String toString() {
27 | return "*";
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/stylesheet/selector/simple/ClassAttributeSelector.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.stylesheet.selector.simple;
2 |
3 | import com.spinyowl.spinygui.core.node.Element;
4 | import com.spinyowl.spinygui.core.style.stylesheet.Specificity;
5 | import com.spinyowl.spinygui.core.style.stylesheet.selector.Selector;
6 | import java.util.Arrays;
7 | import lombok.Getter;
8 | import lombok.NonNull;
9 | import lombok.RequiredArgsConstructor;
10 |
11 | /**
12 | * The class selector selects elements with a specific class attribute.
13 | *
14 | *
To select elements with a specific class, write a period (.) character, followed by the class
15 | * name.
16 | */
17 | @Getter
18 | @RequiredArgsConstructor
19 | public class ClassAttributeSelector implements Selector {
20 |
21 | @NonNull private final String className;
22 |
23 | @Override
24 | public boolean test(Element element) {
25 | var classAttributes = element.getAttribute("class");
26 | if (classAttributes != null) {
27 | var classList = classAttributes.split("\\s+");
28 | return Arrays.asList(classList).contains(className);
29 | }
30 | return false;
31 | }
32 |
33 | @Override
34 | public Specificity specificity() {
35 | return Specificity.CLASS;
36 | }
37 |
38 | @Override
39 | public String toString() {
40 | return "." + className;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/stylesheet/selector/simple/ElementSelector.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.stylesheet.selector.simple;
2 |
3 | import com.spinyowl.spinygui.core.node.Element;
4 | import com.spinyowl.spinygui.core.style.stylesheet.Specificity;
5 | import com.spinyowl.spinygui.core.style.stylesheet.selector.Selector;
6 | import lombok.Getter;
7 | import lombok.NonNull;
8 | import lombok.RequiredArgsConstructor;
9 |
10 | /** The element selector selects elements based on the Element's nodeName. */
11 | @Getter
12 | @RequiredArgsConstructor
13 | public class ElementSelector implements Selector {
14 |
15 | @NonNull private final String nodeName;
16 |
17 | @Override
18 | public boolean test(Element node) {
19 | return nodeName.equals(node.nodeName());
20 | }
21 |
22 | @Override
23 | public Specificity specificity() {
24 | return Specificity.TYPE;
25 | }
26 |
27 | @Override
28 | public String toString() {
29 | return nodeName;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/stylesheet/selector/simple/IdAttributeSelector.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.stylesheet.selector.simple;
2 |
3 | import com.spinyowl.spinygui.core.node.Element;
4 | import com.spinyowl.spinygui.core.style.stylesheet.Specificity;
5 | import com.spinyowl.spinygui.core.style.stylesheet.selector.Selector;
6 | import lombok.Getter;
7 | import lombok.NonNull;
8 | import lombok.RequiredArgsConstructor;
9 |
10 | /**
11 | * The CSS ID selector matches an element based on the value of the element’s id attribute.
12 | *
13 | *
In order for the element to be selected, its id attribute must match exactly the value given
14 | * in the selector.
15 | */
16 | @Getter
17 | @RequiredArgsConstructor
18 | public class IdAttributeSelector implements Selector {
19 | @NonNull private final String id;
20 |
21 | @Override
22 | public boolean test(Element element) {
23 | var idAttribute = element.getAttribute("id");
24 | if (idAttribute != null && !idAttribute.isBlank()) {
25 | return id.equals(idAttribute);
26 | }
27 | return false;
28 | }
29 |
30 | @Override
31 | public Specificity specificity() {
32 | return Specificity.ID;
33 | }
34 |
35 | @Override
36 | public String toString() {
37 | return "#" + id;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/stylesheet/term/TermColor.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.stylesheet.term;
2 |
3 | import com.spinyowl.spinygui.core.style.stylesheet.Term;
4 | import com.spinyowl.spinygui.core.style.types.Color;
5 | import lombok.EqualsAndHashCode;
6 |
7 | @EqualsAndHashCode
8 | public class TermColor extends Term {
9 | public TermColor(Color value) {
10 | super(value);
11 | }
12 |
13 | @Override
14 | public String toString() {
15 | return value.toString();
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/stylesheet/term/TermFloat.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.stylesheet.term;
2 |
3 | import com.spinyowl.spinygui.core.style.stylesheet.Term;
4 | import lombok.EqualsAndHashCode;
5 |
6 | @EqualsAndHashCode
7 | public class TermFloat extends Term {
8 |
9 | public TermFloat(Float value) {
10 | super(value);
11 | }
12 |
13 | @Override
14 | public String toString() {
15 | return Float.toString(value);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/stylesheet/term/TermFunction.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.stylesheet.term;
2 |
3 | import com.spinyowl.spinygui.core.style.stylesheet.Term;
4 | import java.util.List;
5 | import lombok.EqualsAndHashCode;
6 | import lombok.Getter;
7 |
8 | @Getter
9 | @EqualsAndHashCode
10 | public class TermFunction extends TermList {
11 | private final String name;
12 |
13 | public TermFunction(String name, Operator operator, List> value) {
14 | super(operator, value);
15 | this.name = name;
16 | }
17 |
18 | public TermFunction(String name, Operator operator, Term>... value) {
19 | super(operator, value);
20 | this.name = name;
21 | }
22 |
23 | @Override
24 | public String toString() {
25 | return name + "(" + super.toString() + ")";
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/stylesheet/term/TermIdent.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.stylesheet.term;
2 |
3 | import com.spinyowl.spinygui.core.style.stylesheet.Term;
4 | import lombok.EqualsAndHashCode;
5 |
6 | @EqualsAndHashCode
7 | public class TermIdent extends Term {
8 |
9 | public static final TermIdent INHERIT = new TermIdent("inherit");
10 | public static final TermIdent INITIAL = new TermIdent("initial");
11 |
12 | public TermIdent(String value) {
13 | super(value);
14 | }
15 |
16 | @Override
17 | public String toString() {
18 | return value;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/stylesheet/term/TermInteger.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.stylesheet.term;
2 |
3 | import com.spinyowl.spinygui.core.style.stylesheet.Term;
4 | import lombok.EqualsAndHashCode;
5 |
6 | @EqualsAndHashCode
7 | public class TermInteger extends Term {
8 |
9 | public TermInteger(Integer value) {
10 | super(value);
11 | }
12 |
13 | @Override
14 | public String toString() {
15 | return Integer.toString(value);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/stylesheet/term/TermLength.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.stylesheet.term;
2 |
3 | import com.spinyowl.spinygui.core.style.types.length.Length;
4 | import lombok.EqualsAndHashCode;
5 |
6 | @EqualsAndHashCode
7 | public class TermLength extends TermUnit> {
8 | public TermLength(Length> value) {
9 | super(value);
10 | }
11 |
12 | public String toString() {
13 | return value.toString();
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/stylesheet/term/TermList.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.stylesheet.term;
2 |
3 | import com.spinyowl.spinygui.core.style.stylesheet.Term;
4 | import java.util.List;
5 | import java.util.StringJoiner;
6 | import lombok.AccessLevel;
7 | import lombok.EqualsAndHashCode;
8 | import lombok.Getter;
9 | import lombok.NonNull;
10 | import lombok.RequiredArgsConstructor;
11 | import lombok.Setter;
12 |
13 | @Getter
14 | @Setter
15 | @EqualsAndHashCode
16 | public class TermList extends Term>> {
17 | @NonNull private Operator operator;
18 |
19 | public TermList(Operator operator, List> value) {
20 | super(value);
21 | this.operator = operator;
22 | }
23 |
24 | public TermList(Operator operator, Term>... value) {
25 | super(List.of(value));
26 | this.operator = operator;
27 | }
28 |
29 | public List> terms() {
30 | return value;
31 | }
32 |
33 | public Term> get(int index) {
34 | return value.get(index);
35 | }
36 |
37 | public int size() {
38 | return value.size();
39 | }
40 |
41 | public boolean isEmpty() {
42 | return value.isEmpty();
43 | }
44 |
45 | @Override
46 | public String toString() {
47 | StringJoiner joiner = new StringJoiner(operator.value);
48 | terms().forEach(term -> joiner.add(term.toString()));
49 | return joiner.toString();
50 | }
51 |
52 | @Getter
53 | @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
54 | public enum Operator {
55 | SPACE(" "),
56 | SLASH("/"),
57 | COMMA(", ");
58 |
59 | private final String value;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/stylesheet/term/TermURI.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.stylesheet.term;
2 |
3 | import com.spinyowl.spinygui.core.style.stylesheet.Term;
4 |
5 | public class TermURI extends Term {}
6 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/stylesheet/term/TermUnit.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.stylesheet.term;
2 |
3 | import com.spinyowl.spinygui.core.style.stylesheet.Term;
4 | import com.spinyowl.spinygui.core.style.types.length.Unit;
5 | import lombok.EqualsAndHashCode;
6 |
7 | @EqualsAndHashCode
8 | public class TermUnit extends Term {
9 | public TermUnit(U value) {
10 | super(value);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/types/BorderRadius.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.types;
2 |
3 | import com.spinyowl.spinygui.core.style.types.length.Length;
4 | import lombok.Data;
5 | import lombok.NoArgsConstructor;
6 | import lombok.NonNull;
7 |
8 | /** Class container for border radius properties. */
9 | @Data
10 | @NoArgsConstructor
11 | public class BorderRadius {
12 |
13 | /** Top left border radius. */
14 | @NonNull private Length> topLeft = Length.ZERO;
15 | /** Top right border radius. */
16 | @NonNull private Length> topRight = Length.ZERO;
17 | /** Bottom right border radius. */
18 | @NonNull private Length> bottomRight = Length.ZERO;
19 | /** Bottom left border radius. */
20 | @NonNull private Length> bottomLeft = Length.ZERO;
21 |
22 | /**
23 | * Used to create border radius.
24 | *
25 | * @param radius radius to set. Sets border radius to all corners.
26 | */
27 | public BorderRadius(@NonNull Length> radius) {
28 | topLeft = topRight = bottomRight = bottomLeft = radius;
29 | }
30 |
31 | /**
32 | * Used to create border radius.
33 | *
34 | * @param topLeftBottomRight top left and bottom right radius.
35 | * @param topRightBottomLeft top right and bottom left radius.
36 | */
37 | public BorderRadius(
38 | @NonNull Length> topLeftBottomRight, @NonNull Length> topRightBottomLeft) {
39 | topLeft = bottomRight = topLeftBottomRight;
40 | topRight = bottomLeft = topRightBottomLeft;
41 | }
42 |
43 | /**
44 | * Used to create border radius.
45 | *
46 | * @param topLeft top left radius.
47 | * @param bottomRight bottom right radius.
48 | * @param topRightBottomLeft top right and bottom left radius.
49 | */
50 | public BorderRadius(
51 | @NonNull Length> topLeft,
52 | @NonNull Length> topRightBottomLeft,
53 | @NonNull Length> bottomRight) {
54 | this.topLeft = topLeft;
55 | this.topRight = this.bottomLeft = topRightBottomLeft;
56 | this.bottomRight = bottomRight;
57 | }
58 |
59 | /**
60 | * Used to create border radius.
61 | *
62 | * @param topLeft top left radius.
63 | * @param topRight top right radius.
64 | * @param bottomRight bottom right radius.
65 | * @param bottomLeft bottom left radius.
66 | */
67 | public BorderRadius(
68 | @NonNull Length> topLeft,
69 | @NonNull Length> topRight,
70 | @NonNull Length> bottomRight,
71 | @NonNull Length> bottomLeft) {
72 | this.topLeft = topLeft;
73 | this.topRight = topRight;
74 | this.bottomRight = bottomRight;
75 | this.bottomLeft = bottomLeft;
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/types/BoxShadow.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.types;
2 |
3 | import com.spinyowl.spinygui.core.style.types.length.Length;
4 | import lombok.Data;
5 | import lombok.NoArgsConstructor;
6 | import lombok.NonNull;
7 |
8 | @Data
9 | @NoArgsConstructor
10 | public class BoxShadow {
11 |
12 | public static final BoxShadow NO_SHADOW = new BoxShadow();
13 |
14 | private boolean inset;
15 |
16 | private Length> hOffset;
17 | private Length> vOffset;
18 | private Length> blur;
19 | private Length> spread;
20 |
21 | @NonNull private Color color = Color.TRANSPARENT;
22 | }
23 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/types/HorizontalAlign.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.types;
2 |
3 | import static lombok.AccessLevel.PRIVATE;
4 |
5 | import java.util.HashMap;
6 | import java.util.Map;
7 | import java.util.Set;
8 | import lombok.AllArgsConstructor;
9 | import lombok.EqualsAndHashCode;
10 | import lombok.Getter;
11 | import lombok.NonNull;
12 |
13 | @Getter
14 | @EqualsAndHashCode
15 | @AllArgsConstructor(access = PRIVATE)
16 | public class HorizontalAlign {
17 |
18 | /** Stores all existing dictionary values. */
19 | private static final Map VALUES = new HashMap<>();
20 | /** Content should be aligned to left border of container. */
21 | public static final HorizontalAlign LEFT = HorizontalAlign.create("left");
22 | /** Content should be aligned to center of container. */
23 | public static final HorizontalAlign CENTER = HorizontalAlign.create("center");
24 | /** Content should be aligned to right border of container. */
25 | public static final HorizontalAlign RIGHT = HorizontalAlign.create("right");
26 |
27 | /** Name of HorizontalAlign element. */
28 | @NonNull private final String name;
29 |
30 | /**
31 | * Used to create new HorizontalAlign element with specified name. Note that name will be
32 | * converted to lower case.
33 | *
34 | * @param name name of HorizontalAlign element.
35 | * @return new HorizontalAlign element (or existing one).
36 | */
37 | private static HorizontalAlign create(@NonNull String name) {
38 | return VALUES.computeIfAbsent(name.toLowerCase(), HorizontalAlign::new);
39 | }
40 |
41 | /**
42 | * Used to find HorizontalAlign element with specified name. Note that name will be converted to
43 | * lower case.
44 | *
45 | * @param name name of HorizontalAlign element.
46 | * @return existing HorizontalAlign element or null.
47 | */
48 | public static HorizontalAlign find(@NonNull String name) {
49 | return VALUES.get(name.toLowerCase());
50 | }
51 |
52 | /**
53 | * Returns set of all available HorizontalAlign values.
54 | *
55 | * @return set of all available HorizontalAlign values.
56 | */
57 | public static Set values() {
58 | return Set.copyOf(VALUES.values());
59 | }
60 |
61 | /**
62 | * Returns true if there is a HorizontalAlign value wth specified name.
63 | *
64 | * @param name HorizontalAlign name.
65 | * @return true if there is a HorizontalAlign value wth specified name.
66 | */
67 | public static boolean contains(@NonNull String name) {
68 | return VALUES.containsKey(name.toLowerCase());
69 | }
70 |
71 | @Override
72 | public String toString() {
73 | return name;
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/types/Overflow.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.types;
2 |
3 | import static lombok.AccessLevel.PRIVATE;
4 |
5 | import java.util.HashMap;
6 | import java.util.Map;
7 | import java.util.Set;
8 | import lombok.AllArgsConstructor;
9 | import lombok.EqualsAndHashCode;
10 | import lombok.Getter;
11 | import lombok.NonNull;
12 |
13 | @Getter
14 | @EqualsAndHashCode
15 | @AllArgsConstructor(access = PRIVATE)
16 | public final class Overflow {
17 |
18 | private static final Map VALUES = new HashMap<>();
19 |
20 | /** Name of Overflow */
21 | @NonNull private final String name;
22 |
23 | /**
24 | * Used to create new Overflow element with specified name. Note that name will be converted to
25 | * lower case.
26 | *
27 | * @param name name of Overflow element.
28 | * @return new Overflow element (or existing one).
29 | */
30 | public static Overflow create(@NonNull String name) {
31 | return VALUES.computeIfAbsent(name.toLowerCase(), Overflow::new);
32 | }
33 |
34 | /**
35 | * Used to find Overflow element with specified name. Note that name will be converted to lower
36 | * case.
37 | *
38 | * @param name name of Overflow element.
39 | * @return existing Overflow element or null.
40 | */
41 | public static Overflow find(@NonNull String name) {
42 | return VALUES.get(name.toLowerCase());
43 | }
44 |
45 | /**
46 | * Returns set of all available Overflow values.
47 | *
48 | * @return set of all available Overflow values.
49 | */
50 | public static Set values() {
51 | return Set.copyOf(VALUES.values());
52 | }
53 |
54 | /**
55 | * Returns true if there is a Overflow value wth specified name.
56 | *
57 | * @param name Overflow name.
58 | * @return true if there is a Overflow value wth specified name.
59 | */
60 | public static boolean contains(@NonNull String name) {
61 | return VALUES.containsKey(name.toLowerCase());
62 | }
63 |
64 | @Override
65 | public String toString() {
66 | return name;
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/types/PointerEvents.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.types;
2 |
3 | import static lombok.AccessLevel.PRIVATE;
4 |
5 | import java.util.HashMap;
6 | import java.util.Map;
7 | import java.util.Set;
8 | import lombok.AllArgsConstructor;
9 | import lombok.EqualsAndHashCode;
10 | import lombok.Getter;
11 | import lombok.NonNull;
12 |
13 | @Getter
14 | @EqualsAndHashCode
15 | @AllArgsConstructor(access = PRIVATE)
16 | public class PointerEvents {
17 |
18 | /** Stores all existing dictionary values. */
19 | private static final Map VALUES = new HashMap<>();
20 |
21 | /** The element reacts to pointer events, like :hover and click. This is default */
22 | public static final PointerEvents AUTO = PointerEvents.create("auto");
23 | /** The element does not react to pointer events */
24 | public static final PointerEvents NONE = PointerEvents.create("none");
25 |
26 | /** Name of PointerEvents element. */
27 | @NonNull private final String name;
28 |
29 | /**
30 | * Used to create new PointerEvents element with specified name. Note that name will be converted
31 | * to lower case.
32 | *
33 | * @param name name of PointerEvents element.
34 | * @return new PointerEvents element (or existing one).
35 | */
36 | public static PointerEvents create(@NonNull String name) {
37 | return VALUES.computeIfAbsent(name.toLowerCase(), PointerEvents::new);
38 | }
39 |
40 | /**
41 | * Used to find PointerEvents element with specified name. Note that name will be converted to
42 | * lower case.
43 | *
44 | * @param name name of PointerEvents element.
45 | * @return existing PointerEvents element or null.
46 | */
47 | public static PointerEvents find(@NonNull String name) {
48 | return VALUES.get(name.toLowerCase());
49 | }
50 |
51 | /**
52 | * Returns set of all available PointerEvents values.
53 | *
54 | * @return set of all available PointerEvents values.
55 | */
56 | public static Set values() {
57 | return Set.copyOf(VALUES.values());
58 | }
59 |
60 | /**
61 | * Returns true if there is a PointerEvents value wth specified name.
62 | *
63 | * @param name PointerEvents name.
64 | * @return true if there is a PointerEvents value wth specified name.
65 | */
66 | public static boolean contains(@NonNull String name) {
67 | return VALUES.containsKey(name.toLowerCase());
68 | }
69 |
70 | @Override
71 | public String toString() {
72 | return name;
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/types/background/BackgroundRepeat.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.types.background;
2 |
3 | import lombok.NonNull;
4 |
5 | public class BackgroundRepeat {
6 |
7 | private Repeat repeatX;
8 | private Repeat repeatY;
9 |
10 | public BackgroundRepeat() {
11 | repeatY = repeatX = Repeat.REPEAT;
12 | }
13 |
14 | public BackgroundRepeat(@NonNull Repeat repeat) {
15 | repeatY = repeatX = repeat;
16 | }
17 |
18 | public BackgroundRepeat(@NonNull Repeat repeatX, @NonNull Repeat repeatY) {
19 | this.repeatX = repeatX;
20 | this.repeatY = repeatY;
21 | }
22 |
23 | public Repeat getRepeatX() {
24 | return repeatX;
25 | }
26 |
27 | public void setRepeatX(@NonNull Repeat repeatX) {
28 | this.repeatX = repeatX;
29 | }
30 |
31 | public Repeat getRepeatY() {
32 | return repeatY;
33 | }
34 |
35 | public void setRepeatY(@NonNull Repeat repeatY) {
36 | this.repeatY = repeatY;
37 | }
38 |
39 | @Override
40 | public String toString() {
41 | return repeatX.name().toLowerCase() + " " + repeatY.name().toLowerCase();
42 | }
43 |
44 | public enum Repeat {
45 | REPEAT,
46 | NO_REPEAT,
47 | SPACE,
48 | ROUND
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/types/background/BackgroundSize.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.types.background;
2 |
3 | import com.spinyowl.spinygui.core.style.types.length.Unit;
4 | import lombok.AccessLevel;
5 | import lombok.EqualsAndHashCode;
6 | import lombok.Getter;
7 | import lombok.RequiredArgsConstructor;
8 | import lombok.ToString;
9 |
10 | @Getter
11 | @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
12 | public class BackgroundSize {
13 | private final boolean unit;
14 | private final boolean contain;
15 | private final boolean cover;
16 |
17 | public static BackgroundSize createSize(Unit sizeX) {
18 | return new BackgroundSizeUnit(sizeX);
19 | }
20 |
21 | public static BackgroundSize createSize(Unit sizeX, Unit sizeY) {
22 | return new BackgroundSizeUnit(sizeX, sizeY);
23 | }
24 |
25 | public static BackgroundSize createContain() {
26 | return new BackgroundSizeContain();
27 | }
28 |
29 | public static BackgroundSize createCover() {
30 | return new BackgroundSizeCover();
31 | }
32 |
33 | public BackgroundSizeUnit asUnit() {
34 | return (BackgroundSizeUnit) this;
35 | }
36 |
37 | @EqualsAndHashCode
38 | @ToString
39 | public static class BackgroundSizeContain extends BackgroundSize {
40 | private BackgroundSizeContain() {
41 | super(false, true, false);
42 | }
43 | }
44 |
45 | @EqualsAndHashCode
46 | @ToString
47 | public static class BackgroundSizeCover extends BackgroundSize {
48 |
49 | private BackgroundSizeCover() {
50 | super(false, false, true);
51 | }
52 | }
53 |
54 | @Getter
55 | @EqualsAndHashCode
56 | @ToString
57 | public static class BackgroundSizeUnit extends BackgroundSize {
58 | private final Unit sizeX;
59 | private final Unit sizeY;
60 |
61 | private BackgroundSizeUnit(Unit size) {
62 | this(size, Unit.AUTO);
63 | }
64 |
65 | private BackgroundSizeUnit(Unit sizeX, Unit sizeY) {
66 | super(true, false, false);
67 | this.sizeX = sizeX;
68 | this.sizeY = sizeY;
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/types/border/BorderItem.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.types.border;
2 |
3 | import com.spinyowl.spinygui.core.style.types.Color;
4 | import com.spinyowl.spinygui.core.style.types.length.Length;
5 | import lombok.Data;
6 | import lombok.NoArgsConstructor;
7 | import lombok.NonNull;
8 |
9 | @Data
10 | @NoArgsConstructor
11 | public class BorderItem {
12 |
13 | @NonNull private Color color = Color.TRANSPARENT;
14 | @NonNull private BorderStyle style = BorderStyle.NONE;
15 | @NonNull private Length> width = Length.ZERO;
16 | }
17 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/types/flex/FlexDirection.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.types.flex;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 | import java.util.Set;
6 | import lombok.AccessLevel;
7 | import lombok.EqualsAndHashCode;
8 | import lombok.Getter;
9 | import lombok.NonNull;
10 | import lombok.RequiredArgsConstructor;
11 |
12 | /** Specifies the direction of the flexible items */
13 | @Getter
14 | @EqualsAndHashCode
15 | @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
16 | public final class FlexDirection {
17 |
18 | private static final Map VALUES = new HashMap<>();
19 |
20 | /** Default value. The flexible items are displayed horizontally, as a row. */
21 | public static final FlexDirection ROW = FlexDirection.create("row");
22 | /** Same as row, but in reverse order. */
23 | public static final FlexDirection ROW_REVERSE = FlexDirection.create("row-reverse");
24 | /** The flexible items are displayed vertically, as a column. */
25 | public static final FlexDirection COLUMN = FlexDirection.create("column");
26 | /** Same as column, but in reverse order. */
27 | public static final FlexDirection COLUMN_REVERSE = FlexDirection.create("column-reverse");
28 |
29 | @NonNull private final String name;
30 |
31 | private static FlexDirection create(@NonNull String name) {
32 | return VALUES.computeIfAbsent(name.toLowerCase(), FlexDirection::new);
33 | }
34 |
35 | /**
36 | * Used to find flex-direction element with specified name. Note that name will be converted to
37 | * lower case and it should be the same as names of css flex-direction property in css
38 | * specification.
39 | *
40 | * @param name name of flex-direction element.
41 | * @return existing align-content element or null.
42 | */
43 | public static FlexDirection find(@NonNull String name) {
44 | return VALUES.get(name.toLowerCase());
45 | }
46 |
47 | /**
48 | * Returns set of all available flex-direction values.
49 | *
50 | * @return set of all available flex-direction values.
51 | */
52 | public static Set values() {
53 | return Set.copyOf(VALUES.values());
54 | }
55 |
56 | /**
57 | * Returns true if there is a flex-direction value wth specified name.
58 | *
59 | * @param name flex-direction name.
60 | * @return true if there is a flex-direction value wth specified name.
61 | */
62 | public static boolean contains(@NonNull String name) {
63 | return VALUES.containsKey(name.toLowerCase());
64 | }
65 |
66 | @Override
67 | public String toString() {
68 | return name;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/types/flex/FlexWrap.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.types.flex;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 | import java.util.Set;
6 | import lombok.AccessLevel;
7 | import lombok.EqualsAndHashCode;
8 | import lombok.Getter;
9 | import lombok.NonNull;
10 | import lombok.RequiredArgsConstructor;
11 |
12 | /** Specifies whether the flexible items should wrap or not. */
13 | @Getter
14 | @EqualsAndHashCode
15 | @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
16 | public final class FlexWrap {
17 |
18 | private static final Map VALUES = new HashMap<>();
19 |
20 | /** Default value. Specifies that the flexible items will not wrap. */
21 | public static final FlexWrap NOWRAP = FlexWrap.create("nowrap");
22 | /** Specifies that the flexible items will wrap if necessary. */
23 | public static final FlexWrap WRAP = FlexWrap.create("wrap");
24 | /** Specifies that the flexible items will wrap, if necessary, in reverse order. */
25 | public static final FlexWrap WRAP_REVERSE = FlexWrap.create("wrap-reverse");
26 |
27 | @NonNull private final String name;
28 |
29 | private static FlexWrap create(@NonNull String name) {
30 | return VALUES.computeIfAbsent(name.toLowerCase(), FlexWrap::new);
31 | }
32 |
33 | /**
34 | * Used to find flex-wrap element with specified name. Note that name will be converted to lower
35 | * case and it should be the same as names of css flex-wrap property in css specification.
36 | *
37 | * @param name name of flex-wrap element.
38 | * @return existing align-content element or null.
39 | */
40 | public static FlexWrap find(@NonNull String name) {
41 | return VALUES.get(name.toLowerCase());
42 | }
43 |
44 | /**
45 | * Returns set of all available flex-wrap values.
46 | *
47 | * @return set of all available flex-wrap values.
48 | */
49 | public static Set values() {
50 | return Set.copyOf(VALUES.values());
51 | }
52 |
53 | /**
54 | * Returns true if there is a flex-wrap value wth specified name.
55 | *
56 | * @param name flex-wrap name.
57 | * @return true if there is a flex-wrap value wth specified name.
58 | */
59 | public static boolean contains(@NonNull String name) {
60 | return VALUES.containsKey(name.toLowerCase());
61 | }
62 |
63 | @Override
64 | public String toString() {
65 | return name;
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/types/length/Length.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.types.length;
2 |
3 | import lombok.Data;
4 | import lombok.NonNull;
5 | import lombok.RequiredArgsConstructor;
6 |
7 | @Data
8 | @RequiredArgsConstructor
9 | public class Length implements Unit {
10 |
11 | public static final Length ZERO = new Length<>(0, "px");
12 |
13 | @NonNull private final T value;
14 | @NonNull private final String type;
15 |
16 | public static PixelLength pixel(float value) {
17 | return new PixelLength(value);
18 | }
19 |
20 | public static PercentLength percent(float value) {
21 | return new PercentLength(value);
22 | }
23 |
24 | /**
25 | * Used to convert length to pixels. Base length is 1.
26 | *
27 | * @return calculated length.
28 | */
29 | public float convert() {
30 | return convert(1);
31 | }
32 |
33 | /**
34 | * Used to convert length to pixels.
35 | *
36 | * @param baseLength base length (that could be used to calculate relative length).
37 | * @return calculated length.
38 | */
39 | public float convert(float baseLength) {
40 | return value.floatValue() * baseLength;
41 | }
42 |
43 | @Override
44 | public String toString() {
45 | return value + "" + type;
46 | }
47 |
48 | public static class PixelLength extends Length {
49 | public PixelLength(@NonNull Float value) {
50 | super(value, "px");
51 | }
52 |
53 | @Override
54 | public float convert(float baseLength) {
55 | return super.value;
56 | }
57 | }
58 |
59 | public static class PercentLength extends Length {
60 | public PercentLength(@NonNull Float value) {
61 | super(value, "%");
62 | }
63 | }
64 |
65 | private static final class ZH {
66 | private static final PixelLength I = new PixelLength(0F);
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/types/length/LengthConverter.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.types.length;
2 |
3 | /**
4 | * Converts length to pixels.
5 | *
6 | * @param type of length value.
7 | */
8 | @FunctionalInterface
9 | public interface LengthConverter {
10 |
11 | /**
12 | * Used to convert length of specified type to pixels.
13 | *
14 | * @param original original length.
15 | * @param baseLength base length (that could be used to calculate relative length).
16 | * @return calculated length.
17 | */
18 | float convert(Length original, float baseLength);
19 |
20 | /**
21 | * Used to convert length of specified type to pixels. Base length is 1.
22 | *
23 | * @param original original length.
24 | * @return calculated length.
25 | */
26 | default float convert(Length original) {
27 | return convert(original, 1);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/style/types/length/Unit.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.style.types.length;
2 |
3 | public interface Unit {
4 |
5 | // DECLARATION OF AUTO VALUE.
6 | Unit AUTO =
7 | new Unit() {
8 | @Override
9 | public String toString() {
10 | return "auto";
11 | }
12 | };
13 |
14 | default boolean isLength() {
15 | return this instanceof Length;
16 | }
17 |
18 | default boolean isAuto() {
19 | return AUTO.equals(this);
20 | }
21 |
22 | default Length asLength() {
23 | return (Length) this;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/system/event/SystemCharEvent.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.system.event;
2 |
3 | import lombok.Data;
4 | import lombok.experimental.SuperBuilder;
5 |
6 | /** Unicode character is input. */
7 | @Data
8 | @SuperBuilder
9 | public class SystemCharEvent extends SystemEvent {
10 | /** The Unicode code point of the character. */
11 | private final int codepoint;
12 | }
13 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/system/event/SystemCharModsEvent.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.system.event;
2 |
3 | import static com.google.common.collect.ImmutableSet.toImmutableSet;
4 | import com.google.common.collect.ImmutableSet;
5 | import com.spinyowl.spinygui.core.input.KeyMod;
6 | import com.spinyowl.spinygui.core.system.input.SystemKeyMod;
7 | import lombok.Data;
8 | import lombok.NonNull;
9 | import lombok.experimental.SuperBuilder;
10 |
11 | /** Event on Unicode character input regardless of what modifier keys are used. */
12 | @Data
13 | @SuperBuilder
14 | public class SystemCharModsEvent extends SystemEvent {
15 | /** The Unicode code point of the character. */
16 | private final int codepoint;
17 | /** Describes which modifier keys were held down. */
18 | @NonNull private final ImmutableSet mods;
19 |
20 | public ImmutableSet mappedMods() {
21 | return mods.stream().map(SystemKeyMod::keyMod).collect(toImmutableSet());
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/system/event/SystemCursorEnterEvent.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.system.event;
2 |
3 | import lombok.Data;
4 | import lombok.experimental.SuperBuilder;
5 |
6 | /** Event that generated when the cursor enters or leaves the client area of the window. */
7 | @Data
8 | @SuperBuilder
9 | public class SystemCursorEnterEvent extends SystemEvent {
10 | /**
11 | * {@code true} if the cursor entered the window's content area, or {@code false} if
12 | * it left it
13 | */
14 | private final boolean entered;
15 | }
16 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/system/event/SystemCursorPosEvent.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.system.event;
2 |
3 | import lombok.Data;
4 | import lombok.experimental.SuperBuilder;
5 |
6 | /** Will be generated when the cursor is moved. */
7 | @Data
8 | @SuperBuilder
9 | public class SystemCursorPosEvent extends SystemEvent {
10 | /** The new cursor x-coordinate, relative to the left edge of the content area. */
11 | private final float posX;
12 | /** The new cursor y-coordinate, relative to the top edge of the content area. */
13 | private final float posY;
14 | }
15 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/system/event/SystemEvent.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.system.event;
2 |
3 | import com.spinyowl.spinygui.core.node.Frame;
4 | import lombok.Data;
5 | import lombok.experimental.SuperBuilder;
6 |
7 | /** Defines tree of system events. */
8 | @Data
9 | @SuperBuilder
10 | public class SystemEvent {
11 |
12 | /** The frame that received the event. */
13 | private final Frame frame;
14 | }
15 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/system/event/SystemFileDropEvent.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.system.event;
2 |
3 | import lombok.Data;
4 | import lombok.NonNull;
5 | import lombok.experimental.SuperBuilder;
6 |
7 | @Data
8 | @SuperBuilder
9 | public class SystemFileDropEvent extends SystemEvent {
10 |
11 | /** The UTF-8 encoded file and/or directory path names. */
12 | @NonNull private final String[] paths;
13 | }
14 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/system/event/SystemFramebufferSizeEvent.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.system.event;
2 |
3 | import lombok.Data;
4 | import lombok.experimental.SuperBuilder;
5 |
6 | /** Will be generated when the framebuffer of the specified window is resized. */
7 | @Data
8 | @SuperBuilder
9 | public class SystemFramebufferSizeEvent extends SystemEvent {
10 | /** The new width, in pixels, of the framebuffer. */
11 | private final int width;
12 | /** The new height, in pixels, of the framebuffer. */
13 | private final int height;
14 | }
15 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/system/event/SystemKeyEvent.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.system.event;
2 |
3 | import static com.google.common.collect.ImmutableSet.toImmutableSet;
4 | import com.google.common.collect.ImmutableSet;
5 | import com.spinyowl.spinygui.core.input.KeyMod;
6 | import com.spinyowl.spinygui.core.system.input.SystemKeyAction;
7 | import com.spinyowl.spinygui.core.system.input.SystemKeyMod;
8 | import lombok.Data;
9 | import lombok.NonNull;
10 | import lombok.experimental.SuperBuilder;
11 |
12 | /** Will be generated when a key is pressed, repeated or released. */
13 | @Data
14 | @SuperBuilder
15 | public class SystemKeyEvent extends SystemEvent {
16 |
17 | /** The keyboard key that was pressed or released. */
18 | private final int keyCode;
19 |
20 | /**
21 | * The system-specific scancode of the key.
22 | *
23 | * The scancode is unique for every key, regardless of whether it has a key token. Scancodes
24 | * are platform-specific but consistent over time, so keys will have different scancodes depending
25 | * on the platform but they are safe to save to disk.
26 | */
27 | private final int scancode;
28 |
29 | /**
30 | * The key action. One of:
31 | *
32 | *
33 | * - {@link SystemKeyAction#PRESS PRESS}
34 | *
- {@link SystemKeyAction#RELEASE RELEASE}
35 | *
- {@link SystemKeyAction#REPEAT REPEAT}
36 | *
37 | */
38 | @NonNull private final SystemKeyAction action;
39 |
40 | /** Describes which modifier keys were held down. */
41 | @NonNull private final ImmutableSet mods;
42 |
43 | public ImmutableSet mappedMods() {
44 | return mods.stream().map(SystemKeyMod::keyMod).collect(toImmutableSet());
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/system/event/SystemMouseClickEvent.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.system.event;
2 |
3 | import static com.google.common.collect.ImmutableSet.toImmutableSet;
4 | import com.google.common.collect.ImmutableSet;
5 | import com.spinyowl.spinygui.core.input.KeyMod;
6 | import com.spinyowl.spinygui.core.system.input.SystemKeyAction;
7 | import com.spinyowl.spinygui.core.system.input.SystemKeyMod;
8 | import com.spinyowl.spinygui.core.system.input.SystemMouseButton;
9 | import lombok.Data;
10 | import lombok.NonNull;
11 | import lombok.experimental.SuperBuilder;
12 |
13 | /** Will be generated when a mouse button is pressed or released. */
14 | @Data
15 | @SuperBuilder
16 | public class SystemMouseClickEvent extends SystemEvent {
17 |
18 | /** System mouse button. */
19 | @NonNull private final SystemMouseButton button;
20 |
21 | /**
22 | * The key action. One of:
23 | *
24 | *
25 | * - {@link SystemKeyAction#PRESS PRESS}
26 | *
- {@link SystemKeyAction#RELEASE RELEASE}
27 | *
- {@link SystemKeyAction#REPEAT REPEAT}
28 | *
29 | */
30 | @NonNull private final SystemKeyAction action;
31 |
32 | /** Describes which modifier keys were held down. */
33 | @NonNull private final ImmutableSet mods;
34 |
35 | public ImmutableSet mappedMods() {
36 | return mods.stream().map(SystemKeyMod::keyMod).collect(toImmutableSet());
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/system/event/SystemScrollEvent.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.system.event;
2 |
3 | import lombok.Data;
4 | import lombok.experimental.SuperBuilder;
5 |
6 | /**
7 | * Will be generated when a scrolling device is used, such as a mouse wheel or scrolling area of a
8 | * touchpad.
9 | */
10 | @Data
11 | @SuperBuilder
12 | public class SystemScrollEvent extends SystemEvent {
13 | /** The scroll offset along the x-axis. */
14 | private final float offsetX;
15 | /** The scroll offset along the y-axis. */
16 | private final float offsetY;
17 | }
18 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/system/event/SystemWindowCloseEvent.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.system.event;
2 |
3 | import lombok.Data;
4 | import lombok.experimental.SuperBuilder;
5 |
6 | /**
7 | * Will be generated when the user attempts to close the specified window, for example by clicking
8 | * the close widget in the title bar.
9 | */
10 | @Data
11 | @SuperBuilder
12 | public class SystemWindowCloseEvent extends SystemEvent {}
13 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/system/event/SystemWindowFocusEvent.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.system.event;
2 |
3 | import lombok.Data;
4 | import lombok.experimental.SuperBuilder;
5 |
6 | @Data
7 | @SuperBuilder
8 | public class SystemWindowFocusEvent extends SystemEvent {
9 | /** {@code true} if the window was focused, or {@code false} if it was defocused */
10 | private final boolean focused;
11 | }
12 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/system/event/SystemWindowIconifyEvent.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.system.event;
2 |
3 | import lombok.Data;
4 | import lombok.experimental.SuperBuilder;
5 |
6 | /** Will be generated when the specified window is iconified or restored. */
7 | @Data
8 | @SuperBuilder
9 | public class SystemWindowIconifyEvent extends SystemEvent {
10 | /** {@code true} if the window was iconified, or {@code false} if it was restored */
11 | private final boolean iconified;
12 | }
13 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/system/event/SystemWindowPosEvent.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.system.event;
2 |
3 | import lombok.Data;
4 | import lombok.experimental.SuperBuilder;
5 |
6 | /** Will be generated when the specified window moves. */
7 | @Data
8 | @SuperBuilder
9 | public class SystemWindowPosEvent extends SystemEvent {
10 | /**
11 | * The new x-coordinate, in screen coordinates, of the upper-left corner of the content area of
12 | * the window.
13 | */
14 | private final int posX;
15 | /**
16 | * The new y-coordinate, in screen coordinates, of the upper-left corner of the content area of
17 | * the window.
18 | */
19 | private final int posY;
20 | }
21 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/system/event/SystemWindowRefreshEvent.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.system.event;
2 |
3 | import lombok.Data;
4 | import lombok.experimental.SuperBuilder;
5 |
6 | /**
7 | * Will be generated when the client area of the specified window needs to be redrawn, for example
8 | * if the window has been exposed after having been covered by another window.
9 | */
10 | @Data
11 | @SuperBuilder
12 | public class SystemWindowRefreshEvent extends SystemEvent {}
13 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/system/event/SystemWindowSizeEvent.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.system.event;
2 |
3 | import lombok.Data;
4 | import lombok.experimental.SuperBuilder;
5 |
6 | /** Will be generated when the specified window is resized. */
7 | @Data
8 | @SuperBuilder
9 | public class SystemWindowSizeEvent extends SystemEvent {
10 | /** The new width, in screen coordinates, of the window. */
11 | private final int width;
12 | /** The new height, in screen coordinates, of the window. */
13 | private final int height;
14 | }
15 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/system/event/listener/AbstractSystemEventListener.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.system.event.listener;
2 |
3 | import com.spinyowl.spinygui.core.event.processor.EventProcessor;
4 | import com.spinyowl.spinygui.core.system.event.SystemEvent;
5 | import com.spinyowl.spinygui.core.time.TimeService;
6 | import lombok.EqualsAndHashCode;
7 | import lombok.NonNull;
8 | import lombok.RequiredArgsConstructor;
9 |
10 | @EqualsAndHashCode
11 | @RequiredArgsConstructor
12 | public abstract class AbstractSystemEventListener
13 | implements SystemEventListener {
14 | /** Event processor to pass generated events to. */
15 | @NonNull final EventProcessor eventProcessor;
16 |
17 | /** Time provider to use by event listeners. */
18 | @NonNull final TimeService timeService;
19 | }
20 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/system/event/listener/SystemCharEventListener.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.system.event.listener;
2 |
3 | import com.spinyowl.spinygui.core.event.CharEvent;
4 | import com.spinyowl.spinygui.core.event.processor.EventProcessor;
5 | import com.spinyowl.spinygui.core.node.Frame;
6 | import com.spinyowl.spinygui.core.system.event.SystemCharEvent;
7 | import com.spinyowl.spinygui.core.time.TimeService;
8 | import com.spinyowl.spinygui.core.util.TextUtil;
9 | import lombok.Builder;
10 | import lombok.EqualsAndHashCode;
11 | import lombok.NonNull;
12 |
13 | @EqualsAndHashCode
14 | public class SystemCharEventListener extends AbstractSystemEventListener {
15 |
16 | @Builder
17 | public SystemCharEventListener(
18 | @NonNull EventProcessor eventProcessor, @NonNull TimeService timeService) {
19 | super(eventProcessor, timeService);
20 | }
21 |
22 | /**
23 | * Used to listen, process and translate system event to gui event.
24 | *
25 | * @param event system event to process
26 | * @param frame target frame for system event.
27 | */
28 | @Override
29 | public void process(@NonNull SystemCharEvent event, @NonNull Frame frame) {
30 | var focusedElement = frame.getFocusedElement();
31 | if (focusedElement == null) {
32 | return;
33 | }
34 |
35 | eventProcessor.push(
36 | CharEvent.builder()
37 | .source(frame)
38 | .target(focusedElement)
39 | .timestamp(timeService.currentTime())
40 | .input(TextUtil.cpToStr(event.codepoint()))
41 | .build());
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/system/event/listener/SystemEventListener.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.system.event.listener;
2 |
3 | import com.spinyowl.spinygui.core.node.Frame;
4 | import com.spinyowl.spinygui.core.system.event.SystemEvent;
5 | import lombok.NonNull;
6 |
7 | /**
8 | * Used to listen, process and translate system event to gui event.
9 | *
10 | * @param type of system event.
11 | */
12 | public interface SystemEventListener {
13 |
14 | /**
15 | * Used to listen, process and translate system event to gui event.
16 | *
17 | * @param event system event to process
18 | * @param frame target frame for system event.
19 | */
20 | void process(@NonNull E event, @NonNull Frame frame);
21 | }
22 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/system/event/listener/SystemFileDropEventListener.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.system.event.listener;
2 |
3 | import com.spinyowl.spinygui.core.event.FileDropEvent;
4 | import com.spinyowl.spinygui.core.event.processor.EventProcessor;
5 | import com.spinyowl.spinygui.core.input.MouseService;
6 | import com.spinyowl.spinygui.core.node.Frame;
7 | import com.spinyowl.spinygui.core.system.event.SystemFileDropEvent;
8 | import com.spinyowl.spinygui.core.time.TimeService;
9 | import com.spinyowl.spinygui.core.util.NodeUtilities;
10 | import lombok.Builder;
11 | import lombok.EqualsAndHashCode;
12 | import lombok.NonNull;
13 | import org.joml.Vector2fc;
14 |
15 | @EqualsAndHashCode
16 | public class SystemFileDropEventListener extends AbstractSystemEventListener {
17 |
18 | @NonNull private final MouseService mouseService;
19 |
20 | @Builder
21 | public SystemFileDropEventListener(
22 | @NonNull EventProcessor eventProcessor,
23 | @NonNull TimeService timeService,
24 | @NonNull MouseService mouseService) {
25 | super(eventProcessor, timeService);
26 | this.mouseService = mouseService;
27 | }
28 |
29 | /**
30 | * Used to listen, process and translate system event to gui event.
31 | *
32 | * @param event system event to process
33 | * @param frame target frame for system event.
34 | */
35 | @Override
36 | public void process(@NonNull SystemFileDropEvent event, @NonNull Frame frame) {
37 | Vector2fc currentMousePosition = mouseService.getCursorPositions(frame).current();
38 | var targetElement = NodeUtilities.getTargetElement(frame, currentMousePosition);
39 | if (targetElement != null) {
40 | eventProcessor.push(
41 | FileDropEvent.builder()
42 | .source(frame)
43 | .target(targetElement)
44 | .timestamp(timeService.currentTime())
45 | .paths(event.paths())
46 | .build());
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/system/event/listener/SystemKeyEventListener.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.system.event.listener;
2 |
3 | import com.spinyowl.spinygui.core.event.KeyboardEvent;
4 | import com.spinyowl.spinygui.core.event.processor.EventProcessor;
5 | import com.spinyowl.spinygui.core.input.KeyAction;
6 | import com.spinyowl.spinygui.core.input.Keyboard;
7 | import com.spinyowl.spinygui.core.input.KeyboardKey;
8 | import com.spinyowl.spinygui.core.node.Frame;
9 | import com.spinyowl.spinygui.core.system.event.SystemKeyEvent;
10 | import com.spinyowl.spinygui.core.time.TimeService;
11 | import lombok.Builder;
12 | import lombok.EqualsAndHashCode;
13 | import lombok.NonNull;
14 |
15 | @EqualsAndHashCode
16 | public class SystemKeyEventListener extends AbstractSystemEventListener {
17 |
18 | @NonNull private final Keyboard keyboard;
19 |
20 | @Builder
21 | public SystemKeyEventListener(
22 | @NonNull EventProcessor eventProcessor,
23 | @NonNull TimeService timeService,
24 | @NonNull Keyboard keyboard) {
25 | super(eventProcessor, timeService);
26 | this.keyboard = keyboard;
27 | }
28 |
29 | /**
30 | * Used to listen, process and translate system event to gui event.
31 | *
32 | * @param event system event to process
33 | * @param frame target frame for system event.
34 | */
35 | @Override
36 | public void process(@NonNull SystemKeyEvent event, @NonNull Frame frame) {
37 | var element = frame.getFocusedElement();
38 | if (element != null) {
39 |
40 | int keyCode = event.keyCode();
41 | var key = new KeyboardKey(keyboard.layout().keyCode(keyCode), keyCode, event.scancode());
42 |
43 | eventProcessor.push(
44 | KeyboardEvent.builder()
45 | .source(frame)
46 | .target(element)
47 | .key(key)
48 | .timestamp(timeService.currentTime())
49 | .mods(event.mappedMods())
50 | .action(getAction(event))
51 | .build());
52 | }
53 | }
54 |
55 | private KeyAction getAction(SystemKeyEvent event) {
56 | return switch (event.action()) {
57 | case PRESS -> KeyAction.PRESS;
58 | case RELEASE -> KeyAction.RELEASE;
59 | case REPEAT -> KeyAction.REPEAT;
60 | };
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/system/event/listener/SystemScrollEventListener.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.system.event.listener;
2 |
3 | import com.spinyowl.spinygui.core.event.ScrollEvent;
4 | import com.spinyowl.spinygui.core.event.processor.EventProcessor;
5 | import com.spinyowl.spinygui.core.input.MouseService;
6 | import com.spinyowl.spinygui.core.node.Frame;
7 | import com.spinyowl.spinygui.core.system.event.SystemScrollEvent;
8 | import com.spinyowl.spinygui.core.time.TimeService;
9 | import com.spinyowl.spinygui.core.util.NodeUtilities;
10 | import lombok.Builder;
11 | import lombok.EqualsAndHashCode;
12 | import lombok.NonNull;
13 | import org.joml.Vector2fc;
14 |
15 | @EqualsAndHashCode
16 | public class SystemScrollEventListener extends AbstractSystemEventListener {
17 |
18 | @NonNull private final MouseService mouseService;
19 |
20 | @Builder
21 | public SystemScrollEventListener(
22 | @NonNull EventProcessor eventProcessor,
23 | @NonNull TimeService timeService,
24 | @NonNull MouseService mouseService) {
25 | super(eventProcessor, timeService);
26 | this.mouseService = mouseService;
27 | }
28 |
29 | /**
30 | * Used to listen, process and translate system event to gui event.
31 | *
32 | * @param event system event to process
33 | * @param frame target frame for system event.
34 | */
35 | @Override
36 | public void process(@NonNull SystemScrollEvent event, @NonNull Frame frame) {
37 | Vector2fc current = mouseService.getCursorPositions(frame).current();
38 | var currentTargetElements = NodeUtilities.getTargetElementList(frame, current);
39 | float multiplier = 50;
40 | for (var target : currentTargetElements) {
41 | // TODO:
42 | // 1. If target prevents scroll - skip scrolling.
43 | // 2. Instead of direct updating of scrollTop and scrollLeft as another option we can start
44 | // scroll animation.
45 |
46 | float scrollTop = target.scrollTop() - event.offsetY() * multiplier;
47 | if (target.scrollHeight() > target.clientHeight()) {
48 | target.scrollTop(scrollTop);
49 | }
50 |
51 | float scrollLeft = target.scrollLeft() - event.offsetX() * multiplier;
52 | if (target.scrollWidth() > target.box().content().width()) {
53 | target.scrollLeft(scrollLeft);
54 | }
55 | eventProcessor.push(
56 | ScrollEvent.builder()
57 | .source(frame)
58 | .target(target)
59 | .timestamp(timeService.currentTime())
60 | .offsetX(event.offsetX())
61 | .offsetY(event.offsetY())
62 | .build());
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/system/event/listener/SystemWindowFocusEventListener.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.system.event.listener;
2 |
3 | import com.spinyowl.spinygui.core.event.WindowFocusEvent;
4 | import com.spinyowl.spinygui.core.event.processor.EventProcessor;
5 | import com.spinyowl.spinygui.core.node.Frame;
6 | import com.spinyowl.spinygui.core.system.event.SystemWindowFocusEvent;
7 | import com.spinyowl.spinygui.core.time.TimeService;
8 | import lombok.Builder;
9 | import lombok.EqualsAndHashCode;
10 | import lombok.NonNull;
11 |
12 | @EqualsAndHashCode
13 | public class SystemWindowFocusEventListener
14 | extends AbstractSystemEventListener {
15 |
16 | @Builder
17 | public SystemWindowFocusEventListener(
18 | @NonNull EventProcessor eventProcessor, @NonNull TimeService timeService) {
19 | super(eventProcessor, timeService);
20 | }
21 |
22 | /**
23 | * Used to listen, process and translate system event to gui event.
24 | *
25 | * @param event system event to process
26 | * @param frame target frame for system event.
27 | */
28 | @Override
29 | public void process(@NonNull SystemWindowFocusEvent event, @NonNull Frame frame) {
30 | eventProcessor.push(
31 | WindowFocusEvent.builder()
32 | .source(frame)
33 | .target(frame)
34 | .timestamp(timeService.currentTime())
35 | .build());
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/system/event/listener/SystemWindowIconifyEventListener.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.system.event.listener;
2 |
3 | import com.spinyowl.spinygui.core.event.WindowIconifyEvent;
4 | import com.spinyowl.spinygui.core.event.processor.EventProcessor;
5 | import com.spinyowl.spinygui.core.node.Frame;
6 | import com.spinyowl.spinygui.core.system.event.SystemWindowIconifyEvent;
7 | import com.spinyowl.spinygui.core.time.TimeService;
8 | import lombok.Builder;
9 | import lombok.EqualsAndHashCode;
10 | import lombok.NonNull;
11 |
12 | @EqualsAndHashCode
13 | public class SystemWindowIconifyEventListener
14 | extends AbstractSystemEventListener {
15 |
16 | @Builder
17 | public SystemWindowIconifyEventListener(
18 | @NonNull EventProcessor eventProcessor, @NonNull TimeService timeService) {
19 | super(eventProcessor, timeService);
20 | }
21 |
22 | /**
23 | * Used to listen, process and translate system event to gui event.
24 | *
25 | * @param event system event to process
26 | * @param frame target frame for system event.
27 | */
28 | @Override
29 | public void process(@NonNull SystemWindowIconifyEvent event, @NonNull Frame frame) {
30 | eventProcessor.push(
31 | WindowIconifyEvent.builder()
32 | .source(frame)
33 | .target(frame)
34 | .timestamp(timeService.currentTime())
35 | .iconified(event.iconified())
36 | .build());
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/system/event/listener/SystemWindowPosEventListener.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.system.event.listener;
2 |
3 | import com.spinyowl.spinygui.core.event.WindowPosEvent;
4 | import com.spinyowl.spinygui.core.event.processor.EventProcessor;
5 | import com.spinyowl.spinygui.core.node.Frame;
6 | import com.spinyowl.spinygui.core.system.event.SystemWindowPosEvent;
7 | import com.spinyowl.spinygui.core.time.TimeService;
8 | import lombok.Builder;
9 | import lombok.EqualsAndHashCode;
10 | import lombok.NonNull;
11 |
12 | @EqualsAndHashCode
13 | public class SystemWindowPosEventListener
14 | extends AbstractSystemEventListener {
15 |
16 | @Builder
17 | public SystemWindowPosEventListener(
18 | @NonNull EventProcessor eventProcessor, @NonNull TimeService timeService) {
19 | super(eventProcessor, timeService);
20 | }
21 |
22 | /**
23 | * Used to listen, process and translate system event to gui event.
24 | *
25 | * @param event system event to process
26 | * @param frame target frame for system event.
27 | */
28 | @Override
29 | public void process(@NonNull SystemWindowPosEvent event, @NonNull Frame frame) {
30 | eventProcessor.push(
31 | WindowPosEvent.builder()
32 | .source(frame)
33 | .target(frame)
34 | .timestamp(timeService.currentTime())
35 | .posX(event.posX())
36 | .posY(event.posY())
37 | .build());
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/system/event/listener/SystemWindowRefreshEventListener.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.system.event.listener;
2 |
3 | import com.spinyowl.spinygui.core.event.WindowRefreshEvent;
4 | import com.spinyowl.spinygui.core.event.processor.EventProcessor;
5 | import com.spinyowl.spinygui.core.node.Frame;
6 | import com.spinyowl.spinygui.core.system.event.SystemWindowRefreshEvent;
7 | import com.spinyowl.spinygui.core.time.TimeService;
8 | import lombok.Builder;
9 | import lombok.EqualsAndHashCode;
10 | import lombok.NonNull;
11 |
12 | @EqualsAndHashCode
13 | public class SystemWindowRefreshEventListener
14 | extends AbstractSystemEventListener {
15 |
16 | @Builder
17 | public SystemWindowRefreshEventListener(
18 | @NonNull EventProcessor eventProcessor, @NonNull TimeService timeService) {
19 | super(eventProcessor, timeService);
20 | }
21 |
22 | /**
23 | * Used to listen, process and translate system event to gui event.
24 | *
25 | * @param event system event to process
26 | * @param frame target frame for system event.
27 | */
28 | @Override
29 | public void process(@NonNull SystemWindowRefreshEvent event, @NonNull Frame frame) {
30 | eventProcessor.push(
31 | WindowRefreshEvent.builder()
32 | .source(frame)
33 | .target(frame)
34 | .timestamp(timeService.currentTime())
35 | .build());
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/system/event/listener/SystemWindowSizeEventListener.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.system.event.listener;
2 |
3 | import com.spinyowl.spinygui.core.event.WindowSizeEvent;
4 | import com.spinyowl.spinygui.core.event.processor.EventProcessor;
5 | import com.spinyowl.spinygui.core.node.Frame;
6 | import com.spinyowl.spinygui.core.system.event.SystemWindowSizeEvent;
7 | import com.spinyowl.spinygui.core.time.TimeService;
8 | import lombok.Builder;
9 | import lombok.EqualsAndHashCode;
10 | import lombok.NonNull;
11 |
12 | @EqualsAndHashCode
13 | public class SystemWindowSizeEventListener
14 | extends AbstractSystemEventListener {
15 |
16 | @Builder
17 | public SystemWindowSizeEventListener(
18 | @NonNull EventProcessor eventProcessor, @NonNull TimeService timeService) {
19 | super(eventProcessor, timeService);
20 | }
21 |
22 | /**
23 | * Used to listen, process and translate system event to gui event.
24 | *
25 | * @param event system event to process
26 | * @param frame target frame for system event.
27 | */
28 | @Override
29 | public void process(@NonNull SystemWindowSizeEvent event, @NonNull Frame frame) {
30 | eventProcessor.push(
31 | WindowSizeEvent.builder()
32 | .source(frame)
33 | .target(frame)
34 | .timestamp(timeService.currentTime())
35 | .width(event.width())
36 | .height(event.height())
37 | .build());
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/system/event/listener/SystemWindowsCloseEventListener.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.system.event.listener;
2 |
3 | import com.spinyowl.spinygui.core.event.WindowCloseEvent;
4 | import com.spinyowl.spinygui.core.event.processor.EventProcessor;
5 | import com.spinyowl.spinygui.core.node.Frame;
6 | import com.spinyowl.spinygui.core.system.event.SystemWindowCloseEvent;
7 | import com.spinyowl.spinygui.core.time.TimeService;
8 | import lombok.Builder;
9 | import lombok.EqualsAndHashCode;
10 | import lombok.NonNull;
11 |
12 | /** Generates {@link WindowCloseEvent} for frame and pushes it to {@link EventProcessor}. */
13 | @EqualsAndHashCode
14 | public class SystemWindowsCloseEventListener
15 | extends AbstractSystemEventListener {
16 |
17 | @Builder
18 | public SystemWindowsCloseEventListener(
19 | @NonNull EventProcessor eventProcessor, @NonNull TimeService timeService) {
20 | super(eventProcessor, timeService);
21 | }
22 |
23 | /**
24 | * Used to listen, process and translate system event to gui event.
25 | *
26 | * @param event system event to process
27 | * @param frame target frame for system event.
28 | */
29 | @Override
30 | public void process(@NonNull SystemWindowCloseEvent event, @NonNull Frame frame) {
31 | eventProcessor.push(
32 | WindowCloseEvent.builder()
33 | .timestamp(timeService.currentTime())
34 | .source(frame)
35 | .target(frame)
36 | .build());
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/system/event/processor/SystemEventProcessor.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.system.event.processor;
2 |
3 | import com.spinyowl.spinygui.core.system.event.SystemEvent;
4 |
5 | /**
6 | * System event processor. Used to store system events as a queue and process them (by delegating to
7 | * {@link com.spinyowl.spinygui.core.system.event.listener.SystemEventListener
8 | * SystemEventListeners}).
9 | */
10 | public interface SystemEventProcessor {
11 |
12 | /** Used to process stored events in system event processor. */
13 | void processEvents();
14 |
15 | /**
16 | * Used to push new system event to {@link SystemEventProcessor}.
17 | *
18 | * @param event system event to push to queue.
19 | */
20 | void push(SystemEvent event);
21 |
22 | /**
23 | * Used to check if current system event processor has any system events to process.
24 | *
25 | * @return true if there is any not processed system event.
26 | */
27 | boolean hasEvents();
28 | }
29 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/system/event/processor/SystemEventProcessorImpl.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.system.event.processor;
2 |
3 | import com.spinyowl.spinygui.core.system.event.SystemEvent;
4 | import com.spinyowl.spinygui.core.system.event.listener.SystemEventListener;
5 | import com.spinyowl.spinygui.core.system.event.provider.SystemEventListenerProvider;
6 | import com.spinyowl.spinygui.core.system.event.provider.SystemEventListenerProviderImpl;
7 | import java.util.Queue;
8 | import java.util.concurrent.ConcurrentLinkedQueue;
9 | import lombok.Builder;
10 | import lombok.Builder.Default;
11 |
12 | /**
13 | * Default implementation based on two {@link ConcurrentLinkedQueue} queues which swapped every time
14 | * during processing.
15 | */
16 | @Builder
17 | public class SystemEventProcessorImpl implements SystemEventProcessor {
18 |
19 | @Default
20 | private SystemEventListenerProvider eventListenerProvider = new SystemEventListenerProviderImpl();
21 |
22 | @Default private Queue first = new ConcurrentLinkedQueue<>();
23 | @Default private Queue second = new ConcurrentLinkedQueue<>();
24 |
25 | public static SystemEventProcessorImpl create() {
26 | return SystemEventProcessorImpl.builder().build();
27 | }
28 |
29 | private void swap() {
30 | var temp = first;
31 | first = second;
32 | second = temp;
33 | }
34 |
35 | /** Used to process stored events in system event processor. */
36 | @Override
37 | @SuppressWarnings({"rawtypes", "unchecked"})
38 | public void processEvents() {
39 | if (first.isEmpty()) {
40 | return;
41 | }
42 |
43 | swap();
44 |
45 | for (SystemEvent event = second.poll(); event != null; event = second.poll()) {
46 | SystemEventListener listener = eventListenerProvider.listener(event.getClass());
47 | if (listener != null) {
48 | listener.process(event, event.frame());
49 | }
50 | }
51 | }
52 |
53 | /**
54 | * Used to push new system event to {@link SystemEventProcessor}.
55 | *
56 | * @param event system event to push to queue.
57 | */
58 | @Override
59 | public void push(SystemEvent event) {
60 | first.add(event);
61 | }
62 |
63 | /**
64 | * Used to check if current system event processor has any system events to process.
65 | *
66 | * @return true if there is any not processed system event.
67 | */
68 | @Override
69 | public boolean hasEvents() {
70 | return !first.isEmpty() || !second.isEmpty();
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/system/event/provider/SystemEventListenerProvider.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.system.event.provider;
2 |
3 | import com.spinyowl.spinygui.core.system.event.SystemEvent;
4 | import com.spinyowl.spinygui.core.system.event.listener.SystemEventListener;
5 |
6 | /**
7 | * Used to store system event class to system event listener mapping that would be used by SpinyGUI.
8 | */
9 | public interface SystemEventListenerProvider {
10 |
11 | /**
12 | * Used to obtain system event listener by system event class.
13 | *
14 | * @param aClass system event class.
15 | * @param type of system event.
16 | * @return system event listener that correspond to specified system event class.
17 | */
18 | SystemEventListener listener(Class aClass);
19 |
20 | /**
21 | * Used to set system event listener for specified event class.
22 | *
23 | * @param eventClass system event class.
24 | * @param systemEventListener system event listener.
25 | * @param type of system event.
26 | */
27 | void listener(
28 | Class eventClass, SystemEventListener systemEventListener);
29 | }
30 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/system/event/provider/SystemEventListenerProviderImpl.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.system.event.provider;
2 |
3 | import com.spinyowl.spinygui.core.system.event.SystemEvent;
4 | import com.spinyowl.spinygui.core.system.event.listener.SystemEventListener;
5 | import java.util.HashMap;
6 | import java.util.Map;
7 |
8 | /** Default implementation based on {@link HashMap}. */
9 | public class SystemEventListenerProviderImpl implements SystemEventListenerProvider {
10 |
11 | private final Map, SystemEventListener extends SystemEvent>>
12 | listenerMap = new HashMap<>();
13 |
14 | /**
15 | * Used to obtain system event listener by system event class.
16 | *
17 | * @param eventClass system event class.
18 | * @return system event listener that correspond to specified system event class.
19 | */
20 | @Override
21 | @SuppressWarnings("unchecked")
22 | public SystemEventListener listener(Class eventClass) {
23 | return (SystemEventListener) listenerMap.get(eventClass);
24 | }
25 |
26 | /**
27 | * Used to set system event listener for specified event class.
28 | *
29 | * @param eventClass system event class.
30 | * @param systemEventListener system event listener.
31 | */
32 | @Override
33 | public void listener(
34 | Class eventClass, SystemEventListener systemEventListener) {
35 | listenerMap.put(eventClass, systemEventListener);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/system/font/FontDirectoriesProvider.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.system.font;
2 |
3 | import java.util.List;
4 | import lombok.NonNull;
5 |
6 | public interface FontDirectoriesProvider {
7 |
8 | @NonNull
9 | List getFontDirectories();
10 | }
11 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/system/font/FontLoadingException.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.system.font;
2 |
3 | public class FontLoadingException extends RuntimeException {
4 | public FontLoadingException(String message) {
5 | super(message);
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/system/font/FontService.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.system.font;
2 |
3 | import com.spinyowl.spinygui.core.font.Font;
4 | import lombok.NonNull;
5 |
6 | /** Font service, responsible for loading and caching font data, and calculating text metrics. */
7 | public interface FontService {
8 |
9 | /**
10 | * Loads font from file.
11 | *
12 | * @param path path to font file
13 | * @return loaded font
14 | * @throws FontLoadingException in case of font loading failure.
15 | */
16 | Font loadFont(String path) throws FontLoadingException;
17 |
18 | /**
19 | * Verifies if font exists and available to use.
20 | *
21 | * @param font font to verify.
22 | * @return true if font exists, false otherwise.
23 | */
24 | boolean isFontAvailable(@NonNull Font font);
25 |
26 | /**
27 | * Calculates text metrics.
28 | *
29 | * @param text text to calculate metrics for.
30 | * @param offsetX starting x offset for the first line of text.
31 | * @param font font to use.
32 | * @param fontSize font size.
33 | * @param lineHeight height of line box. It specifies the minimum height of line boxes within the
34 | * element. Default is {@code 1}.
35 | * @param maxWidth maximum width of text in pixels.
36 | * @param wordWrap if true, text will be wrapped by nearest characters to maxWidth, otherwise text
37 | * will be wrapped by spaces to fit maxWidth.
38 | * @return text metrics
39 | */
40 | TextMetrics getTextMetrics(
41 | @NonNull String text,
42 | float offsetX,
43 | @NonNull Font font,
44 | float fontSize,
45 | float lineHeight,
46 | float maxWidth,
47 | boolean wordWrap);
48 |
49 | /**
50 | * Calculates text line metrics.
51 | *
52 | * @param text text to calculate metrics for.
53 | * @param font font to use.
54 | * @param fontSize font size.
55 | * @return text line metrics.
56 | */
57 | TextLineMetrics getTextLineMetrics(
58 | @NonNull String text, @NonNull Font font, float fontSize, float lineHeight);
59 | }
60 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/system/font/FontStorage.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.system.font;
2 |
3 | import java.nio.ByteBuffer;
4 | import lombok.NonNull;
5 |
6 | public interface FontStorage {
7 | /**
8 | * Returns data if it was loaded before. Otherwise, loads font data from specified path.
9 | *
10 | * @param path path to font file.
11 | * @return {@link ByteBuffer} with font data.
12 | */
13 | ByteBuffer getFontData(@NonNull String path);
14 | /**
15 | * Loads font data from specified path.
16 | *
17 | * @param fontPath path to font file.
18 | * @return {@link ByteBuffer} with font data.
19 | */
20 | ByteBuffer loadFont(String fontPath);
21 | }
22 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/system/font/SystemFontLoader.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.system.font;
2 |
3 | import static org.slf4j.LoggerFactory.getLogger;
4 | import com.spinyowl.spinygui.core.font.Font;
5 | import java.io.File;
6 | import java.util.Collection;
7 | import java.util.LinkedList;
8 | import java.util.List;
9 | import lombok.Builder;
10 | import lombok.NonNull;
11 | import org.apache.commons.io.FileUtils;
12 | import org.slf4j.Logger;
13 |
14 | @Builder
15 | public class SystemFontLoader {
16 | private static final Logger LOG = getLogger(SystemFontLoader.class);
17 |
18 | @NonNull private final FontStorage fontStorage;
19 | @NonNull private final FontService fontService;
20 | @NonNull private final FontDirectoriesProvider fontDirectoriesProvider;
21 |
22 | /**
23 | * Loads all system fonts to {@link FontStorage}
24 | *
25 | * @return list of fonts loaded to font storage.
26 | */
27 | public List loadSystemFonts() {
28 | List fontPaths =
29 | fontDirectoriesProvider.getFontDirectories().stream()
30 | .map(this::getAllFilesInDirectory)
31 | .flatMap(List::stream)
32 | .toList();
33 |
34 | List loadedFonts = new LinkedList<>();
35 | for (String fontPath : fontPaths) {
36 | if (fontStorage.loadFont(fontPath) != null) loadedFonts.add(fontPath);
37 | }
38 |
39 | loadedFonts.forEach(this::loadFontSafe);
40 |
41 | return loadedFonts;
42 | }
43 |
44 | private void loadFontSafe(String font) {
45 | try {
46 | Font.addFont(fontService.loadFont(font));
47 | } catch (Exception e) {
48 | LOG.error("Can't load font {}", font, e);
49 | }
50 | }
51 |
52 | /** Returns list of files in given directory. */
53 | private List getAllFilesInDirectory(String directory) {
54 | Collection files = FileUtils.listFiles(new File(directory), new String[] {"ttf"}, true);
55 | return files.stream().map(File::getAbsolutePath).toList();
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/system/font/TextLineMetrics.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.system.font;
2 |
3 | import lombok.AccessLevel;
4 | import lombok.AllArgsConstructor;
5 | import lombok.Builder;
6 | import lombok.EqualsAndHashCode;
7 | import lombok.Getter;
8 | import lombok.Setter;
9 |
10 | @AllArgsConstructor
11 | @EqualsAndHashCode
12 | @Getter
13 | @Setter(AccessLevel.NONE)
14 | @Builder
15 | public final class TextLineMetrics {
16 |
17 | private CharSequence characters;
18 |
19 | /** Character count in the line. */
20 | private int charCount;
21 |
22 | /** Width of the line in pixels. */
23 | private float width;
24 |
25 | /** Height of the line in pixels. */
26 | private float height;
27 |
28 | public String toString() {
29 | return characters.toString();
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/system/font/TextMetrics.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.system.font;
2 |
3 | import com.google.common.collect.ImmutableSet;
4 | import lombok.AccessLevel;
5 | import lombok.AllArgsConstructor;
6 | import lombok.Builder;
7 | import lombok.EqualsAndHashCode;
8 | import lombok.Getter;
9 | import lombok.Setter;
10 | import lombok.Singular;
11 | import lombok.ToString;
12 |
13 | @AllArgsConstructor
14 | @EqualsAndHashCode
15 | @Getter
16 | @Setter(AccessLevel.NONE)
17 | @ToString
18 | @Builder
19 | public final class TextMetrics {
20 |
21 | @Singular private final ImmutableSet lines;
22 |
23 | private float height;
24 | private float fullLineHeight;
25 | }
26 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/system/font/impl/FontStorageImpl.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.system.font.impl;
2 |
3 | import static org.slf4j.LoggerFactory.getLogger;
4 | import com.spinyowl.spinygui.core.system.font.FontStorage;
5 | import com.spinyowl.spinygui.core.util.IOUtil;
6 | import java.nio.ByteBuffer;
7 | import java.util.Map;
8 | import java.util.concurrent.ConcurrentHashMap;
9 | import lombok.NonNull;
10 | import org.slf4j.Logger;
11 |
12 | public class FontStorageImpl implements FontStorage {
13 | private static final Logger LOG = getLogger(FontStorageImpl.class);
14 | private final Map dataMap = new ConcurrentHashMap<>();
15 |
16 | @Override
17 | public ByteBuffer getFontData(@NonNull String path) {
18 | if (dataMap.containsKey(path)) return dataMap.get(path);
19 | return loadFont(path);
20 | }
21 |
22 | /**
23 | * Error safe method to load font and add font data to file storage. In case of error it will
24 | * return null.
25 | *
26 | * @param fontPath path to font which should be loaded.
27 | * @return {@link ByteBuffer} with font data or null in case of failure.
28 | */
29 | @Override
30 | public ByteBuffer loadFont(String fontPath) {
31 | ByteBuffer fontData = null;
32 | try {
33 | fontData = IOUtil.resourceAsByteBuffer(fontPath);
34 | } catch (Exception e) {
35 | LOG.warn("Failed to load font from {}", fontPath);
36 | }
37 | if (fontData != null) {
38 | dataMap.put(fontPath, fontData);
39 | }
40 | return fontData;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/system/font/impl/PlatformSpecificFontDirectoriesProvider.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.system.font.impl;
2 |
3 | import com.spinyowl.spinygui.core.system.font.FontDirectoriesProvider;
4 | import java.util.List;
5 | import lombok.NonNull;
6 |
7 | public class PlatformSpecificFontDirectoriesProvider implements FontDirectoriesProvider {
8 |
9 | private static final String OS_NAME = "os.name";
10 |
11 | /** Returns list of font directories available on current platform. */
12 | @Override
13 | public @NonNull List getFontDirectories() {
14 | if (System.getProperty(OS_NAME).toLowerCase().contains("win")) {
15 | return getWindowsFontDirectories();
16 | } else if (System.getProperty(OS_NAME).toLowerCase().contains("mac")) {
17 | return getMacFontDirectories();
18 | } else if (System.getProperty(OS_NAME).toLowerCase().contains("nix")
19 | || System.getProperty(OS_NAME).toLowerCase().contains("nux")) {
20 | return getLinuxFontDirectories();
21 | } else {
22 | return List.of();
23 | }
24 | }
25 |
26 | /** Returns list of font directories available on windows. */
27 | private List getWindowsFontDirectories() {
28 | return List.of(System.getenv("windir") + "\\Fonts\\");
29 | }
30 |
31 | /** Returns list of font directories available on mac. */
32 | private List getMacFontDirectories() {
33 | return List.of(
34 | "/Library/Fonts/",
35 | "/System/Library/Fonts/",
36 | "/Network/Library/Fonts/",
37 | System.getProperty("user.home") + "/Library/Fonts");
38 | }
39 |
40 | /** Returns list of font directories available on linux. */
41 | private List getLinuxFontDirectories() {
42 | return List.of(
43 | "/usr/share/fonts/",
44 | "/usr/local/share/fonts/",
45 | System.getProperty("user.home") + "/.fonts/");
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/system/input/SystemKeyAction.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.system.input;
2 |
3 | public enum SystemKeyAction {
4 | PRESS,
5 | RELEASE,
6 | REPEAT // applicable only to keyboard keys
7 | }
8 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/system/input/SystemKeyMod.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.system.input;
2 |
3 | import com.spinyowl.spinygui.core.input.KeyMod;
4 | import lombok.AllArgsConstructor;
5 | import lombok.Getter;
6 | import lombok.NonNull;
7 | import lombok.ToString;
8 |
9 | @Getter
10 | @ToString
11 | @AllArgsConstructor
12 | public enum SystemKeyMod {
13 | SHIFT(KeyMod.SHIFT),
14 | CONTROL(KeyMod.CONTROL),
15 | ALT(KeyMod.ALT),
16 | SUPER(KeyMod.SUPER),
17 | CAPS_LOCK(KeyMod.CAPS_LOCK),
18 | NUM_LOCK(KeyMod.NUM_LOCK);
19 |
20 | @NonNull private final KeyMod keyMod;
21 | }
22 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/system/input/SystemMouseButton.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.system.input;
2 |
3 | import com.spinyowl.spinygui.core.input.MouseButton;
4 | import lombok.Getter;
5 | import lombok.NonNull;
6 | import lombok.RequiredArgsConstructor;
7 | import lombok.ToString;
8 |
9 | @Getter
10 | @ToString
11 | @RequiredArgsConstructor
12 | public enum SystemMouseButton {
13 | MOUSE_BUTTON_1(MouseButton.MOUSE_BUTTON_1), // LEFT
14 | MOUSE_BUTTON_2(MouseButton.MOUSE_BUTTON_2), // RIGHT
15 | MOUSE_BUTTON_3(MouseButton.MOUSE_BUTTON_3), // MIDDLE
16 | MOUSE_BUTTON_4(MouseButton.MOUSE_BUTTON_4), // SIDE LEFT TOP
17 | MOUSE_BUTTON_5(MouseButton.MOUSE_BUTTON_5), // SIDE LEFT BOTTOM
18 | MOUSE_BUTTON_6(MouseButton.MOUSE_BUTTON_6),
19 | MOUSE_BUTTON_7(MouseButton.MOUSE_BUTTON_7),
20 | MOUSE_BUTTON_8(MouseButton.MOUSE_BUTTON_8);
21 |
22 | public static final SystemMouseButton LEFT = MOUSE_BUTTON_1;
23 | public static final SystemMouseButton RIGHT = MOUSE_BUTTON_2;
24 | public static final SystemMouseButton MIDDLE = MOUSE_BUTTON_3;
25 |
26 | @NonNull private final MouseButton mouseButton;
27 | }
28 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/time/TimeService.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.time;
2 |
3 | /** Time provider interface. Used to get current time in double representation. */
4 | public interface TimeService {
5 |
6 | /**
7 | * Returns current time in seconds since epoch of 1970-01-01T00:00:00Z.
8 | *
9 | * @return current time in seconds since epoch of 1970-01-01T00:00:00Z.
10 | */
11 | double currentTime();
12 | }
13 |
--------------------------------------------------------------------------------
/core/src/main/java/com/spinyowl/spinygui/core/util/ClassKeyMap.java:
--------------------------------------------------------------------------------
1 | package com.spinyowl.spinygui.core.util;
2 |
3 | import static java.util.Objects.requireNonNull;
4 | import java.util.Map;
5 | import java.util.HashMap;
6 | import java.util.function.Supplier;
7 | import lombok.NonNull;
8 |
9 | /**
10 | * Used to store key-value structure, where key is class of type C and value is parameterized
11 | * instance V, where generic arg is {@code extends T>}.
12 | *
13 | * Used for such structures as class and processor for this class. Provides method to search
14 | * value recursively by key class hierarchy in case if there is no value for provided key.
15 | *
16 | * @param class type.
17 | * @param value type.
18 | */
19 | public class ClassKeyMap {
20 |
21 | private final Map, V> map;
22 | private final Class rootClass;
23 |
24 | public ClassKeyMap(
25 | @NonNull Supplier