├── .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 | [![REPO SIZE](https://img.shields.io/github/repo-size/SpinyOwl/SpinyGUI.svg?style=for-the-badge)](/) 2 | [![CODE SIZE](https://img.shields.io/github/languages/code-size/SpinyOwl/SpinyGUI.svg?style=for-the-badge)](/) 3 | [![License](https://img.shields.io/github/license/SpinyOwl/SpinyGUI.svg?style=for-the-badge)](/LICENSE) 4 | [![Support me on Ko-Fi](https://img.shields.io/badge/-Buy_me_a_coffee-FF5E5B?logo=kofi&logoColor=white&style=for-the-badge)](https://ko-fi.com/J3J4L9ASJ) 5 | [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/SpinyOwl/SpinyGUI/gradle.yml?style=for-the-badge)](https://github.com/SpinyOwl/SpinyGUI/actions/workflows/gradle.yml) 6 | [![Sonar Quality Gate](https://img.shields.io/sonar/quality_gate/SpinyOwl_SpinyGUI?server=https%3A%2F%2Fsonarcloud.io&style=for-the-badge)](https://sonarcloud.io/summary/overall?id=SpinyOwl_SpinyGUI) 7 | [![Sonar Coverage](https://img.shields.io/sonar/coverage/SpinyOwl_SpinyGUI?server=https%3A%2F%2Fsonarcloud.io&style=for-the-badge)](https://sonarcloud.io/component_measures?metric=Coverage&id=SpinyOwl_SpinyGUI) 8 | 9 | [![coverage graph](https://codecov.io/gh/SpinyOwl/SpinyGUI/branch/develop/graphs/icicle.svg)](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 | *

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 | *

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> 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 }. 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, V>> mapSupplier, @NonNull Class rootClass) { 26 | this.map = requireNonNull(mapSupplier.get()); 27 | this.rootClass = rootClass; 28 | } 29 | 30 | public ClassKeyMap(@NonNull Class rootClass) { 31 | this(HashMap::new, rootClass); 32 | } 33 | 34 | public void put(@NonNull Class key, @NonNull V value) { 35 | map.put(key, value); 36 | } 37 | 38 | public V get(@NonNull Class key) { 39 | return map.get(key); 40 | } 41 | 42 | public V get(@NonNull Class key, V defaultValue) { 43 | return map.getOrDefault(key, defaultValue); 44 | } 45 | 46 | public V recursiveGet(@NonNull Class key) { 47 | return recursiveGet(key, null); 48 | } 49 | 50 | public V recursiveGet(@NonNull Class key, V defaultValue) { 51 | return cycledSearch(key, map, defaultValue); 52 | } 53 | 54 | @SuppressWarnings({"rawtypes", "unchecked"}) 55 | private V cycledSearch(Class keyClass, Map map, V defaultValue) { 56 | V value = null; 57 | Class currentKeyClass = keyClass; 58 | 59 | while (value == null) { 60 | value = (V) map.get(currentKeyClass); 61 | // if current key class is root class 62 | if (currentKeyClass.equals(rootClass)) { 63 | break; 64 | } 65 | currentKeyClass = (Class) currentKeyClass.getSuperclass(); 66 | } 67 | if (value == null) { 68 | value = defaultValue; 69 | } 70 | return value; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /core/src/main/java/com/spinyowl/spinygui/core/util/Reference.java: -------------------------------------------------------------------------------- 1 | package com.spinyowl.spinygui.core.util; 2 | 3 | import java.util.Collection; 4 | import java.util.Objects; 5 | 6 | public final class Reference { 7 | 8 | private Reference() {} 9 | 10 | public static boolean containsReference(Collection collection, T element) { 11 | Objects.requireNonNull(collection); 12 | for (T elementOfCollection : collection) { 13 | if (elementOfCollection == element) { 14 | return true; 15 | } 16 | } 17 | return false; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /core/src/main/resources/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpinyOwl/SpinyGUI/4db014a5fb9b5adb485e100089b31d28c0d45995/core/src/main/resources/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /core/src/main/resources/fonts/MaterialIcons-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpinyOwl/SpinyGUI/4db014a5fb9b5adb485e100089b31d28c0d45995/core/src/main/resources/fonts/MaterialIcons-Regular.ttf -------------------------------------------------------------------------------- /core/src/main/resources/fonts/NotoEmoji-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpinyOwl/SpinyGUI/4db014a5fb9b5adb485e100089b31d28c0d45995/core/src/main/resources/fonts/NotoEmoji-Regular.ttf -------------------------------------------------------------------------------- /core/src/main/resources/fonts/Roboto-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpinyOwl/SpinyGUI/4db014a5fb9b5adb485e100089b31d28c0d45995/core/src/main/resources/fonts/Roboto-Bold.ttf -------------------------------------------------------------------------------- /core/src/main/resources/fonts/Roboto-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpinyOwl/SpinyGUI/4db014a5fb9b5adb485e100089b31d28c0d45995/core/src/main/resources/fonts/Roboto-Light.ttf -------------------------------------------------------------------------------- /core/src/main/resources/fonts/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpinyOwl/SpinyGUI/4db014a5fb9b5adb485e100089b31d28c0d45995/core/src/main/resources/fonts/Roboto-Regular.ttf -------------------------------------------------------------------------------- /core/src/main/resources/fonts/entypo.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpinyOwl/SpinyGUI/4db014a5fb9b5adb485e100089b31d28c0d45995/core/src/main/resources/fonts/entypo.ttf -------------------------------------------------------------------------------- /core/src/main/resources/fonts/materialdesignicons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpinyOwl/SpinyGUI/4db014a5fb9b5adb485e100089b31d28c0d45995/core/src/main/resources/fonts/materialdesignicons.ttf -------------------------------------------------------------------------------- /core/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | %black(%d{ISO8601}) %highlight(%-5level) | %blue(%-20t) | %yellow(%40.40C{40}:%-5.5line): %msg %n 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /core/src/test/java/com/spinyowl/spinygui/core/system/event/listener/SystemWindowRefreshEventListenerTest.java: -------------------------------------------------------------------------------- 1 | package com.spinyowl.spinygui.core.system.event.listener; 2 | 3 | import static com.spinyowl.spinygui.core.node.NodeBuilder.frame; 4 | import static org.mockito.Mockito.doNothing; 5 | import static org.mockito.Mockito.verify; 6 | import static org.mockito.Mockito.when; 7 | import com.spinyowl.spinygui.core.event.WindowRefreshEvent; 8 | import com.spinyowl.spinygui.core.event.processor.EventProcessor; 9 | import com.spinyowl.spinygui.core.node.Frame; 10 | import com.spinyowl.spinygui.core.system.event.SystemWindowRefreshEvent; 11 | import com.spinyowl.spinygui.core.time.TimeService; 12 | import org.junit.jupiter.api.Assertions; 13 | import org.junit.jupiter.api.BeforeEach; 14 | import org.junit.jupiter.api.Test; 15 | import org.junit.jupiter.api.extension.ExtendWith; 16 | import org.mockito.Mock; 17 | import org.mockito.junit.jupiter.MockitoExtension; 18 | 19 | @ExtendWith(MockitoExtension.class) 20 | class SystemWindowRefreshEventListenerTest { 21 | @Mock private EventProcessor eventProcessor; 22 | @Mock private TimeService timeService; 23 | 24 | private SystemEventListener listener; 25 | 26 | @BeforeEach 27 | void setUp() { 28 | listener = 29 | SystemWindowRefreshEventListener.builder() 30 | .eventProcessor(eventProcessor) 31 | .timeService(timeService) 32 | .build(); 33 | } 34 | 35 | @Test 36 | void process_generatesEvent() { 37 | // Arrange 38 | var frame = frame(); 39 | var timestamp = 1D; 40 | 41 | when(timeService.currentTime()).thenReturn(timestamp); 42 | 43 | var sourceEvent = createEvent(frame); 44 | var expectedEvent = 45 | WindowRefreshEvent.builder().source(frame).target(frame).timestamp(timestamp).build(); 46 | doNothing().when(eventProcessor).push(expectedEvent); 47 | 48 | // Act 49 | listener.process(sourceEvent, frame); 50 | 51 | // Verify 52 | verify(timeService).currentTime(); 53 | verify(eventProcessor).push(expectedEvent); 54 | } 55 | 56 | @Test 57 | void process_throwsNPE_ifFrameIsNull() { 58 | var event = createEvent(frame()); 59 | Assertions.assertThrows(NullPointerException.class, () -> listener.process(event, null)); 60 | } 61 | 62 | @Test 63 | void process_throwsNPE_ifEventIsNull() { 64 | var frame = frame(); 65 | Assertions.assertThrows(NullPointerException.class, () -> listener.process(null, frame)); 66 | } 67 | 68 | private SystemWindowRefreshEvent createEvent(Frame frame) { 69 | return SystemWindowRefreshEvent.builder().frame(frame).build(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /demo.complex/.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/ -------------------------------------------------------------------------------- /demo.complex/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-library' 3 | id 'application' 4 | } 5 | 6 | dependencies { 7 | api project(':core') 8 | 9 | api project(':core.backend') 10 | api project(':core.backend.lwjgl.nanovg') 11 | 12 | api group: 'com.spinyowl', name: 'cbchain', version: '1.0.2'; 13 | 14 | api group: 'org.slf4j', name: 'slf4j-api', version: slf4j_version 15 | api group: 'ch.qos.logback', name: 'logback-classic', version: logback_version 16 | 17 | api group: 'org.lwjgl', name: 'lwjgl', version: lwjgl_version 18 | api group: 'org.lwjgl', name: 'lwjgl', version: lwjgl_version, classifier: 'natives-windows' 19 | api group: 'org.lwjgl', name: 'lwjgl', version: lwjgl_version, classifier: 'natives-linux' 20 | api group: 'org.lwjgl', name: 'lwjgl', version: lwjgl_version, classifier: 'natives-macos' 21 | 22 | api group: 'org.lwjgl', name: 'lwjgl-glfw', version: lwjgl_version 23 | api group: 'org.lwjgl', name: 'lwjgl-glfw', version: lwjgl_version, classifier: 'natives-windows' 24 | api group: 'org.lwjgl', name: 'lwjgl-glfw', version: lwjgl_version, classifier: 'natives-linux' 25 | api group: 'org.lwjgl', name: 'lwjgl-glfw', version: lwjgl_version, classifier: 'natives-macos' 26 | 27 | api group: 'org.lwjgl', name: 'lwjgl-opengl', version: lwjgl_version 28 | api group: 'org.lwjgl', name: 'lwjgl-opengl', version: lwjgl_version, classifier: 'natives-windows' 29 | api group: 'org.lwjgl', name: 'lwjgl-opengl', version: lwjgl_version, classifier: 'natives-linux' 30 | api group: 'org.lwjgl', name: 'lwjgl-opengl', version: lwjgl_version, classifier: 'natives-macos' 31 | } 32 | application { 33 | mainClass = "com.spinyowl.spinygui.demo.complex.Main" 34 | } 35 | -------------------------------------------------------------------------------- /demo.complex/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | open module com.spinyowl.spinygui.demo.complex { 2 | requires org.antlr.antlr4.runtime; 3 | requires static lombok; 4 | requires transitive org.slf4j; 5 | requires transitive ch.qos.logback.core; 6 | requires transitive ch.qos.logback.classic; 7 | requires transitive com.spinyowl.cbchain; 8 | requires transitive com.spinyowl.spinygui.core; 9 | requires transitive com.spinyowl.spinygui.core.backend; 10 | requires transitive com.spinyowl.spinygui.core.backend.lwjgl.nanovg; 11 | 12 | requires org.lwjgl; 13 | requires org.lwjgl.natives; 14 | requires org.lwjgl.glfw; 15 | requires org.lwjgl.glfw.natives; 16 | requires org.lwjgl.nanovg; 17 | requires org.lwjgl.nanovg.natives; 18 | requires org.lwjgl.opengl; 19 | requires org.lwjgl.opengl.natives; 20 | } 21 | -------------------------------------------------------------------------------- /demo.complex/src/main/resources/com/spinyowl/spinygui/demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | 7 | 8 | 9 | body 10 | 11 | -------------------------------------------------------------------------------- /demo.simple/.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/ -------------------------------------------------------------------------------- /demo.simple/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-library' 3 | id 'application' 4 | } 5 | 6 | dependencies { 7 | implementation project(':spinygui') 8 | 9 | api group: 'org.slf4j', name: 'slf4j-api', version: slf4j_version 10 | api group: 'ch.qos.logback', name: 'logback-classic', version: logback_version 11 | } 12 | application { 13 | mainClass = "com.spinyowl.spinygui.demo.simple.Main" 14 | } 15 | -------------------------------------------------------------------------------- /demo.simple/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | open module com.spinyowl.spinygui.demo.simple { 2 | requires com.spinyowl.spinygui; 3 | requires static lombok; 4 | requires transitive org.slf4j; 5 | requires transitive ch.qos.logback.core; 6 | requires transitive ch.qos.logback.classic; 7 | } 8 | -------------------------------------------------------------------------------- /demo.simple/src/main/resources/com/spinyowl/spinygui/demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | 7 | 8 | 9 | body 10 | 11 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | version=0.0.1-SNAPSHOT 2 | # code generation 3 | antlr4_version=4.13.1 4 | lombok_version=1.18.32 5 | # logging 6 | slf4j_version=2.0.13 7 | logback_version=1.5.6 8 | # utilities 9 | jsoup_version=1.17.2 10 | classgraph_version=4.8.172 11 | commons_lang_version=3.14.0 12 | commons_io_version=2.16.1 13 | guava_version=33.2.0-jre 14 | sonarqube_version=4.0.0.2929 15 | # graphics and vector 16 | lwjgl_version=3.3.3 17 | joml_version=1.10.5 18 | # testing 19 | junit_version=5.10.2 20 | mockito_version=5.11.0 -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpinyOwl/SpinyGUI/4db014a5fb9b5adb485e100089b31d28c0d45995/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip 4 | networkTimeout=10000 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /logging.properties: -------------------------------------------------------------------------------- 1 | handlers=java.util.logging.ConsoleHandler 2 | .level=ALL 3 | java.util.logging.ConsoleHandler.level=ALL 4 | java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter 5 | java.util.logging.SimpleFormatter.format=[%1$tF %1$tT] [%4$-7s] [%2$s] %5$s%6$s %n 6 | com.spinyowl.spinygui.backend.level=ALL 7 | com.spinyowl.spinygui.backend.glfw.opengl32.level=ALL 8 | com.spinyowl.spinygui.backend.glfw.opengl32.service.level=ALL -------------------------------------------------------------------------------- /lombok.config: -------------------------------------------------------------------------------- 1 | lombok.accessors.chain=false 2 | lombok.accessors.fluent=true 3 | 4 | lombok.equalsandhashcode.callsuper=call 5 | lombok.tostring.callsuper=call 6 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'SpinyGUI' 2 | 3 | include 'spinygui' 4 | 5 | include 'core' 6 | 7 | include 'core.backend' 8 | include 'core.backend.lwjgl.nanovg' 9 | 10 | include 'demo.simple' 11 | include 'demo.complex' -------------------------------------------------------------------------------- /spinygui/.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/ -------------------------------------------------------------------------------- /spinygui/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-library' 3 | } 4 | 5 | dependencies { 6 | api project(':core') 7 | 8 | api project(':core.backend') 9 | api project(':core.backend.lwjgl.nanovg') 10 | 11 | api group: 'com.spinyowl', name: 'cbchain', version: '1.0.2'; 12 | } -------------------------------------------------------------------------------- /spinygui/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | open module com.spinyowl.spinygui { 2 | requires transitive com.spinyowl.spinygui.core; 3 | requires transitive com.spinyowl.spinygui.core.backend; 4 | requires transitive com.spinyowl.spinygui.core.backend.lwjgl.nanovg; 5 | } 6 | --------------------------------------------------------------------------------