{"id":18017005,"url":"https://github.com/fabmax/calculator","last_synced_at":"2025-03-26T19:33:50.447Z","repository":{"id":69857480,"uuid":"55450424","full_name":"fabmax/calculator","owner":"fabmax","description":"An Android calculator implemented in Kotlin using OpenGL","archived":false,"fork":false,"pushed_at":"2016-04-11T21:09:34.000Z","size":365,"stargazers_count":13,"open_issues_count":0,"forks_count":4,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-22T07:24:09.782Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fabmax.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-04-04T22:37:51.000Z","updated_at":"2021-09-14T02:27:45.000Z","dependencies_parsed_at":"2023-02-21T16:16:16.974Z","dependency_job_id":null,"html_url":"https://github.com/fabmax/calculator","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fabmax%2Fcalculator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fabmax%2Fcalculator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fabmax%2Fcalculator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fabmax%2Fcalculator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fabmax","download_url":"https://codeload.github.com/fabmax/calculator/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245722804,"owners_count":20661828,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-30T04:19:54.071Z","updated_at":"2025-03-26T19:33:50.423Z","avatar_url":"https://github.com/fabmax.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Calculator\nAn Android calculator app with my own OpenGL-based UI. The app is also available on\n[Google Play](https://play.google.com/store/apps/details?id=de.fabmax.calc)!\n\nI did this as a little\nexperiment to see how fully animated orientation changes and true 3D layouts could\nwork. However I'm not a UX guy so this might be complete rubbish :)\n\nI tried to make this as Material-Design-ish as possible, so the app looks pretty\nstandard at first. However, rotate the screen or hit the view button and you should\nsee the difference...\n\nFor the OpenGL rendering I'm using my own [LightGL](https://github.com/fabmax/LightGL)\nengine. If you clone this repo don't forget to make a ``git submodule init`` and\n``update`` to get the LightGL code.\n\nThe app itself is written in [Kotlin](https://kotlinlang.org), which offers super-cool\nlanguage features. E.g. I use the\n[Builder pattern](https://kotlinlang.org/docs/reference/type-safe-builders.html)\nto define my UI layouts. For example a simple layout looks like this:\n``` kotlin\nval exampleLayout = layout(context) {\n    bounds(rw(-0.5f), rh(-0.5f), rw(1.0f), rh(1.0f))\n\n    button {\n        init {\n            color = Color.RED\n            text = \"Button 1\"\n            onClickListener = { -\u003e Log.d(\"Layout\", \"Button 1 pressed\") }\n        }\n        port {\n            bounds(dp(32f), dp(32f), rw(1f) - dp(64f), rh(.5f) - dp(48f))\n        }\n        land {\n            bounds(dp(32f), dp(32f), rw(.5f) - dp(48f), rh(1f) - dp(64f))\n        }\n    }\n\n    button {\n        init {\n            color = Color.GREEN\n            text = \"Button 2\"\n            onClickListener = { -\u003e Log.d(\"Layout\", \"Button 2 pressed\") }\n        }\n        port {\n            bounds(dp(32f), rh(.5f) + dp(16f), rw(1f) - dp(64f), rh(.5f) - dp(48f))\n        }\n        land {\n            bounds(rw(.5f) + dp(16f), dp(32f), rw(.5f) - dp(48f), rh(1f) - dp(64f))\n        }\n    }\n}\n```\nYou could actually paste this code into MainActivity.kt and it would work showing\na red and a green button with a nice layout transition between portrait and\nlandscape orientation. Sweet!\n\nBut wait what's happening here? First of all a top-level layout is created which\ncontains the buttons. The first bounds() statement may look a little odd but it simply\nmakes the layout fill the screen: The arguments of bounds() are (x, y, width, height).\nInitially the coordinate origin is in the center of the screen, ``rw(.5f)`` and ``rh(.5f)``\nreturn a ``SizeSpec`` which translate to a relative width and height of 50%, hence the\nlayout's upper left corner is moved to the upper left corner of the screen. Width\nand height are set to 100%.\n\nOnce the layout is created we can add the buttons to it. Within the ``init { }`` blocks\nthe button properties are set. Finally, within the ``port { }`` and ``land { }`` blocks\nthe button bounds for portrait and landscape orientations are defined. Bounds are\nalways relative to the next higher parent. Besides rw() and rh() for relative sizes\nthere is also dp() which defines a pixel perfect absolute size with the usual Android\ndp units. Moreover, ``SizeSpec``s can be chained together with + and -.\n\nThere is also a second bounds() method with two additional parameters for z and depth.\nPositive z values will make UI elements appear nearer, negative values farther away.\nThe default z value used by the 4 parameter version is 0. Also keep in mind that,\nbecause of the perspective camera, layouts are only pixel-perfect at z = 0.\n\nTo see how all this works syntax-wise take a look into the Kotlin docs.\n\n## Limitations / Issues:\nThis is only a proof-of-concept I made to see if and how animated layout changes can be\ndone, so there are a few pretty major ones:\n* Currently the only available UI elements are buttons and a very specific calculator\n  text panel.\n* There are no layout managers, component bounds always have to be explicitly specified.\n  The chainable rw(), rh() and dp() methods are surprisingly flexible but obviously they\n  can't replace a true layout manager like ``RelativeLayout``.\n* There is no clipping done for the individual UI elements, hence a UI element can paint\n  anywhere not only within its bounds.\n* The code is not really optimized for performance. The engine itself is pretty fast\n  (though written entirely in Java). However the way the UI elements are composed isn't\n  optimized for draw calls or anything. Moreover especially the shadow computation is\n  pretty expensive so this will eat up your battery in no-time (and also doesn't work\n  very well on slower devices).\n* Touch input is very basic.\n* The system screen orientation is locked to portrait, otherwise the orientation change\n  animation would be interrupted by the system screen orientation change.\n* I haven't really cared for the integration of Android ressources (strings, dimens,\n  etc.) but it should be pretty straight-forward to do so.\n* The code is a bit of a mess - I'm still new to Kotlin and still working on getting\n  a feeling for good style.\n\n\n## License:\n```\nCopyright 2016 Max Thiele\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n   http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffabmax%2Fcalculator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffabmax%2Fcalculator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffabmax%2Fcalculator/lists"}