Repository navigation
Conversation
hannesa2
marked this pull request as draft
September 26, 2026 17:55
…tform-independent classes
* Created a new chartLibCore Kotlin Multiplatform module (com.android.kotlin.multiplatform.library + org.jetbrains.kotlin.multiplatform) targeting androidTarget, jvm("desktop"), and iosX64/iosArm64/iosSimulatorArm64.
* Extracted the 5 genuinely platform-independent classes into its commonMain (same package names, so no call-site changes needed elsewhere): ObjectPool, FSize, PointD, ColorTemplate, highlight.Range, highlight.RangeDouble.
* Fixed the only two real platform couplings found: replaced android.graphics.Color.rgb(...) in ColorTemplate with a common argb() helper, and dropped the JVM-only @synchronized annotations in ObjectPool (common code can't assume JVM's synchronized — the class is not thread-safety-critical against instrumentation tests as run today, but flagging this trade-off).
* Moved ObjectPoolTest into commonTest, converted its JUnit assertions to kotlin.test.
* Registered chartLibCore in settings.gradle.kts and made chartLib depend on it (api(project(":chartLibCore"))).
* Verified: chartLibCore builds/tests pass on Android, desktop-JVM, and all 3 iOS targets; chartLib:test, chartLibCompose:assembleDebug, and the full ./gradlew test all still pass unchanged.
* Designed and added ChartIcon — an expect abstract class in commonMain, actual typealiasd to android.graphics.drawable.Drawable on Android (zero behavior change there) and an empty placeholder on iOS/desktop for now. * Moved all 19 files of the entry hierarchy (BaseEntry, EntryFloat, EntryDouble, deprecated Entry, and all Bar/Pie/Radar/Bubble/Candle Float/Double/deprecated variants) into chartLibCore commonMain, same package names. * Removed Drawable→ChartIcon, dropped @SuppressLint, replaced Build.VERSION toString branching with ::class.simpleName, replaced Timber logging calls, and replaced the Utils.FLOAT_EPSILON/DOUBLE_EPSILON dependency with local common constants. * Flagged breaking change: EntryFloat/Entry no longer implement Parcelable/Serializable (no multiplatform equivalent, no internal usage found — documented as reversible via expect/actual if a consumer needs it back). * Verified: chartLibCore builds on Android/iOS×3/desktop; chartLib:compileDebugKotlin needed zero source changes; full ./gradlew test, chartLibCompose:assembleDebug, app:assembleDebug all pass. * Updated KotlinMultiplatform.md with the detailed change log and remaining scope.
Landed (in chartLibCore, reusable common types): AxisDependency, LegendForm enums (with nested-typealias back-compat on YAxis/Legend), PaintStyle enum (applied to CandleDataSet/ICandleDataSet + renderer/demo conversion), ChartTypeface expect/actual (applied to IDataSet/BaseDataSet/ChartData), DashEffect (designed, not yet wired), consolidated chartDensity/convertDpToPixel, and PointF (Parcelable dropped, moved to commonMain
* Landed: highlight/Highlight.kt (dropped Serializable, switched to the top-level common AxisDependency) and highlight/IHighlighter.kt moved to chartLibCore — small but real, verified-green. * Blocked: All 10 formatter/ files and the remaining 7 highlight/ files (ChartHighlighter, BarHighlighter, HorizontalBarHighlighter, CombinedHighlighter, PieHighlighter, RadarHighlighter, PieRadarHighlighter) transitively depend on ViewPortHandler (Step A.6), AxisBase (Step A.5), IDataSet/ILineDataSet/LineDataProvider (blocked per last slice's finding), or concrete PieChart/RadarChart/PieRadarChartBase view classes.
Moves the viewport geometry engine to commonMain, built on the new common Matrix/RectF value types: - ViewPortHandler moved to chartLibCore as-is, using common Matrix/ RectF; its View? parameter on refresh()/centerViewPort() became a platform-agnostic invalidate callback lambda, and its one Timber log call became a plain println(). - Transformer split into a common TransformerCore base (chartLibCore) holding all matrix/rect math, and a slim Android Transformer subclass (chartLib) that keeps only the dataset-family (generateTransformedValues*) and Path-based (pathValueToPixel(s)) methods that can't move yet. - Added Android-side Matrix/RectF conversion helpers (MatrixAndroid.kt, RectFAndroid.kt) and updated ~20 call sites (Chart/BarChart/ HorizontalBarChart/BarLineChartBase, BarLineChartTouchListener, jobs, axis/bar renderers) to convert at the Canvas-drawing boundary. This unblocks the rest of Step A.3 (IDataSet/BaseDataSet/DataSet/ ChartData family, since IValueFormatter/IFillFormatter no longer need an Android-only ViewPortHandler/LineDataProvider), most of Step A.4 (formatter, highlight), and Step A.5 groundwork. Verified: chartLibCore builds/tests green on Android host, desktop- JVM, and iOS simulator; chartLib:compileDebugKotlin, full ./gradlew test, chartLibCompose:assembleDebug, and app:assembleDebug all pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… to chartLibCore Unblocked by Step A.6 (ViewPortHandler migration), this completes the DataSet-family migration to the multiplatform chartLibCore module: - Move interfaces/datasets/IDataSet.kt, data/BaseDataSet.kt, data/DataSet.kt (incl. nested Rounding enum), and data/ChartData.kt to chartLibCore commonMain, preserving package/class names so no downstream imports change. - Wire up the previously-unused common DashEffect type: IDataSet/BaseDataSet/ Legend/LegendEntry now use DashEffect instead of android.graphics. DashPathEffect for formLineDashEffect, with a new Android-only DashEffectAndroid.kt converter at the one real Canvas boundary in LegendRenderer. Demo app call sites updated accordingly. - IDataSet now imports the top-level common AxisDependency/LegendForm enums directly instead of importing YAxis/Legend for their nested type aliases. - BaseDataSet: drop @ColorInt/@transient annotations, replace android.graphics.Color helpers with new platform-independent ColorTemplate.argb/alpha/red/green/blue helpers, extract the Context-based setColors(IntArray, Context) overload into a chartLib-only extension function (BaseDataSetAndroid.kt), and replace Utils.defaultValueFormatter with a local companion default. - DataSet/ChartData: drop @SuppressLint, java.io.Serializable, and Timber logging (replaced with println), matching the precedent set by EntryFloat/Highlight/ViewPortHandler. - Move the now-unblocked formatter cluster (IValueFormatter, DefaultValueFormatter, StackedValueFormatter, ColorFormatter) to chartLibCore. DefaultValueFormatter/StackedValueFormatter no longer use java.text.DecimalFormat (no KMP equivalent); rewritten against a new shared utils/DecimalFormatting.kt helper with equivalent rounding/grouping behavior, covered by 5 new unit tests. Verified: chartLibCore build/allTests/testAndroidHostTest (Android host, desktop, iOS simulator), chartLib:compileDebugKotlin, full ./gradlew test, chartLibCompose:assembleDebug, app:assembleDebug all green. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…Axis/LimitLine/LimitRange to chartLibCore
Moves the axis-component family and its coupled axis-value formatter
cluster into the platform-agnostic chartLibCore module, following the
same extension-function extraction pattern established in Step A.3/A.6
for Paint/Context-dependent methods.
- ComponentBase: Typeface? -> common ChartTypeface?, Color.BLACK ->
ColorTemplate.BLACK, dropped @ColorInt.
- AxisBase: Color.GRAY -> ColorTemplate.GRAY (new constant),
axisLineDashPathEffect/gridDashPathEffect -> common DashEffect?,
Timber.e -> println, dropped @ColorInt. The Paint-dependent
getLongestLabel(p: Paint?) was extracted into a new Android-only
extension function (chartLib/components/AxisBaseAndroid.kt); the
no-arg longestLabel property (pure string comparison) stayed on the
common class.
- YAxis: Color.GRAY -> ColorTemplate.GRAY, dropped @ColorInt. The two
Paint-dependent methods getRequiredWidthSpace/getRequiredHeightSpace
were extracted into chartLib/components/YAxisAndroid.kt. XAxis needed
no changes.
- LimitLine/LimitRange: Color.rgb(...) -> ColorTemplate.argb(...),
Paint.Style? -> common PaintStyle?, DashPathEffect? -> common
DashEffect?, dropped @ColorInt.
- Circular-dependency slice: AxisBase, IAxisValueFormatter and
DefaultAxisValueFormatter had to move together atomically since
AxisBase.valueFormatter references DefaultAxisValueFormatter
directly, and both formatter types take an AxisBase? parameter.
- DefaultAxisValueFormatter/PercentFormatter: replaced
java.text.DecimalFormat with the shared
chartLibCore/utils/DecimalFormatting.kt formatGroupedDecimal helper.
PercentFormatter's unused DecimalFormat-based constructor overload
(zero callers) was replaced with PercentFormatter(decimalDigits: Int)
- a disclosed breaking change.
- LargeValueFormatter: manual Kotlin rewrite of the
DecimalFormat("###E00") engineering-notation algorithm (exponent
forced to a multiple of 3, mantissa rounded to 3 significant digits,
trailing zeros stripped, carry-over handled). Verified against the
existing ~25-assertion LargeValueFormatterTest (moved to
chartLibCore/commonTest and ported to kotlin.test) - caught and fixed
a floor-division bug during verification.
- Updated renderer call sites (XAxisRenderer, YAxisRenderer,
XAxisRendererHorizontalBarChart, YAxisRendererHorizontalBarChart,
YAxisRendererRadarChart, BarLineChartBase, HorizontalBarChart) for
the DashEffect/PaintStyle conversions and the new extension-function
imports.
Legend/LegendEntry/Description/IMarker/MarkerImage/MarkerView remain in
chartLib for a follow-up slice.
Verified: chartLibCore:allTests, chartLib:compileDebugKotlin, full
./gradlew test, chartLibCompose:assembleDebug, app:assembleDebug all
green.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…artLibCore Completes Step A.5 by moving the remaining portable components/ classes into chartLibCore commonMain. - LegendEntry: trivial move, dropped @ColorInt. - Legend: the bulk of the class (alignment/orientation enums, spacing properties, entries/extraEntries, setCustom/setExtra/resetCustom) needed no changes. The three Paint-dependent methods (getMaximumEntryWidth, getMaximumEntryHeight, and the ~110-line calculateDimensions layout algorithm) were extracted into a new Android-only extension file, chartLib/components/LegendAndroid.kt - the largest extension-function extraction in this migration so far, demonstrating the pattern scales to large multi-branch logic as long as every touched member is public (required switching two internal offset reads from the protected mXOffset/mYOffset fields to the existing public xOffset/yOffset accessors on ComponentBase). - Description: textAlign (android.graphics.Paint.Align?) replaced with a new common chartLibCore/utils/TextAlign.kt enum plus a new chartLib/utils/TextAlignAndroid.kt converter, following the same common-enum-plus-Android-converter pattern already used for PaintStyle/DashEffect. Updated the one render call site in Chart.kt. - LegendRenderer.kt: added the import for the new calculateDimensions extension function at its call site. - IMarker/MarkerImage/MarkerView confirmed permanently Android-only (Canvas/Context/Drawable/RelativeLayout coupling with no separable portable core) and left in chartLib. Step A.5 is now fully complete: all of components/ that has a genuinely portable core lives in chartLibCore. Verified: chartLibCore:allTests, chartLib:compileDebugKotlin, full ./gradlew test, chartLibCompose:assembleDebug, app:assembleDebug all green. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…odule Scaffolds a new chartLibComposeMultiplatform module targeting Android, desktop (jvm), iosArm64 and iosSimulatorArm64, applying the Compose Multiplatform (org.jetbrains.compose 1.12.1) and Compose compiler (org.jetbrains.kotlin.plugin.compose 2.4.10) plugins on top of the same com.android.kotlin.multiplatform.library + org.jetbrains.kotlin.multiplatform setup used by chartLibCore. commonMain depends on chartLibCore (api) plus compose.runtime/compose.foundation/compose.ui. Two toolchain constraints discovered and documented in KotlinMultiplatform.md: - Compose Multiplatform 1.12.1's Android artifacts require compileSdk 37, so this module (only) is bumped to compileSdk 37 while the rest of the repo intentionally stays on 36. - Compose Multiplatform 1.12.x no longer publishes artifacts for the iosX64 (Intel simulator) target, so unlike chartLibCore this module only configures iosArm64()/iosSimulatorArm64(). Only a documented placeholder constant exists so far; no renderer logic has been ported yet. Verified chartLibComposeMultiplatform:build succeeds across all targets, and the full existing verification chain (chartLibCore:allTests, chartLib:compileDebugKotlin, ./gradlew test, chartLibCompose:assembleDebug, app:assembleDebug) remains green. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…nderer slice Ports the axis grid line drawing (XAxisRenderer.renderGridLines/ drawGridLine and YAxisRenderer.renderGridLines/linePath/ transformedPositions) to Compose's DrawScope, as the first real renderer slice out of the ~32-file Step A.7 chunk. This slice was chosen because it needs no concrete chart-type DataSet (LineDataSet/BarDataSet/etc. haven't moved to chartLibCore yet) - only the axis/viewport/transform types already migrated in Step A.5/A.6 (XAxis/YAxis, ViewPortHandler, TransformerCore), making it fully self-contained. Adds chartLibComposeMultiplatform's renderer/AxisGridRenderer.kt with DrawScope.drawXAxisGridLines(...) and DrawScope.drawYAxisGridLines(...), operating purely on chartLibCore types rather than chartLib's Renderer/DataRenderer/AxisRenderer Android base-class hierarchy - confirming Compose renderers will be fresh idiomatic functions on the portable model, not a line-for-line port of the View-based OOP renderer classes. The axis-value-to-pixel math is factored out into plain xAxisGridPixelPositions/yAxisGridPixelPositions functions, separate from the DrawScope drawLine/clipRect calls, so it can be unit-tested without a Compose UI test harness. Added AxisGridRendererTest covering both axes, which now passes on testAndroidHostTest, desktopTest, and iosSimulatorArm64Test. Removes the earlier scaffold placeholder now that real renderer code exists. Full existing verification chain (chartLibCore:allTests, chartLib:compileDebugKotlin, ./gradlew test, chartLibCompose:assembleDebug, app:assembleDebug) remains green. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…hartLibCore Prerequisite work surfaced by the Step A.7 grid-line proof-of-concept: most of the ~30 remaining renderer files need concrete chart-type DataSet classes (LineDataSet/BarDataSet/etc.), which hadn't moved to chartLibCore yet even though the abstract DataSet/BaseDataSet/IDataSet base already had (Step A.3 completion). Moves every concrete DataSet class with no remaining Android-only blocker to chartLibCore commonMain: BarLineScatterCandleBubbleDataSet, LineScatterCandleRadarDataSet, LineRadarDataSet (abstract parents), plus the now fully-portable leaf classes BubbleDataSet, CandleDataSet, PieDataSet, RadarDataSet, and their interfaces. Trivial Android cleanup: @ColorInt annotations dropped, Color.rgb(...)/ Color.WHITE replaced with the existing common ColorTemplate.argb(r,g,b) helper. ILineScatterCandleRadarDataSet.dashPathEffectHighlight switches DashPathEffect -> the common DashEffect type (same pattern as Step A.5), updating LineScatterCandleRadarRenderer's one call site. fillDrawable (Drawable has no portable equivalent) is removed from common code entirely and replaced with an Android-only extension property in the new chartLib/data/LineRadarDataSetAndroid.kt, backed by a WeakHashMap keyed by dataset identity - keeps `dataSet.fillDrawable = ...` call-site syntax unchanged for both LineDataSet (stays Android-only) and RadarDataSet (now common), at the cost of an explicit import at each call site (LineChartRenderer, RadarChartRenderer, three app example activities). Two small disclosed behavior changes from removing the fillDrawable stored property: setting fillColor no longer implicitly clears a previously set fillDrawable, and .copy() no longer propagates fillDrawable to the copy. BarDataSet/LineDataSet (need the heavily Canvas/Paint/Drawable-coupled Fill class, Context-based drawable loading, and IFillFormatter's dependency on the permanently-Android-only LineDataProvider) and ScatterDataSet (needs the Canvas/Paint-coupled IShapeRenderer family) remain Android-only leaves extending the now-common abstract parents, same pattern as IMarker/highlighters in Steps A.4/A.5. Verified: chartLibCore:allTests, chartLib:compileDebugKotlin, full ./gradlew test, chartLibCompose:assembleDebug, app:assembleDebug, and chartLibComposeMultiplatform:build (all targets) all pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- chartLibCore/utils/Fill.kt: portable data-only state (type, color, alpha, gradientColors/gradientPositions, finalColor). Dropped @ColorInt. - chartLib/utils/FillAndroid.kt (new): Android-only Fill.fillRect()/ Fill.fillPath() extension functions (ported verbatim) plus a WeakHashMap-backed Fill.drawable extension property for the Type.DRAWABLE payload, same side-channel pattern as ILineRadarDataSet.fillDrawable. - BarChartRenderer.kt / HorizontalBarChartRenderer.kt: added `import info.appdev.charting.utils.fillRect` (no call-site changes needed). - Moved data/BarDataSet.kt and interfaces/datasets/IBarDataSet.kt to chartLibCore; replaced Color.rgb(...)/Color.BLACK with ColorTemplate.argb(...)/ColorTemplate.BLACK, dropped @ColorInt. BarDataSet is now a fully portable leaf class. - LineDataSet/ScatterDataSet remain Android-only (still blocked on IFillFormatter/LineDataProvider and IShapeRenderer respectively). - No disclosed behavior changes. - Updated KotlinMultiplatform.md with the changelog entry. Verified: chartLibCore:allTests, chartLib:compileDebugKotlin, chartLibCompose:assembleDebug, app:assembleDebug, full ./gradlew test, and chartLibComposeMultiplatform:build (Android/desktop/iosArm64/ iosSimulatorArm64) all pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…eDataSet
- LineDataSet's dashPathEffect/enableDashedLine now use the common DashEffect
type instead of android.graphics.DashPathEffect (reusing Step A.5's
DashEffect/toAndroidDashPathEffect() pattern). Updated
LineChartRenderer.kt's pathEffect assignment accordingly.
- fillFormatter removed from ILineDataSet/LineDataSet entirely (IFillFormatter
depends on the permanently-Android-only LineDataProvider). New
chartLib/data/LineDataSetAndroid.kt defines
`var ILineDataSet<*>.fillFormatter: IFillFormatter?` as a
WeakHashMap-backed extension property, same side-channel pattern as
ILineRadarDataSet.fillDrawable. Added
`import info.appdev.charting.data.fillFormatter` at 6 call sites
(LineChartRenderer.kt + 5 app example activities).
- setCircleColors(colors, context) (ContextCompat-based) moved to the same
LineDataSetAndroid.kt as a generic extension function.
- Moved data/LineDataSet.kt and interfaces/datasets/ILineDataSet.kt to
chartLibCore; dropped @ColorInt/@SuppressLint("RawTypeDataSet"), replaced
Color.WHITE/Color.rgb(...) with ColorTemplate equivalents, replaced
Timber.e(...) warnings with println(...) (matching the existing
chartLibCore warning-logging pattern). LineDataSet is now a fully portable
leaf class.
- Disclosed behavior change: .copy() no longer propagates a custom
fillFormatter to the copy (documented in LineDataSet's class doc, same
category as the earlier fillDrawable copy-non-propagation change).
- ScatterDataSet/IScatterDataSet remain the last Android-only DataSet leaf,
blocked on the IShapeRenderer family + extracting ScatterShape out of the
Android-only ScatterChart.
- Updated KotlinMultiplatform.md with the changelog entry.
Verified: chartLibCore:allTests, chartLib:compileDebugKotlin,
chartLibCompose:assembleDebug, app:assembleDebug, full ./gradlew test, and
chartLibComposeMultiplatform:build (Android/desktop/iosArm64/
iosSimulatorArm64) all pass.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…rDataSet Completes the concrete DataSet family migration (Bar/Line/Scatter/Candle/ Bubble/Radar/Pie) that started as a Step A.7 prerequisite. - Extracted ScatterShape out of the Android-only ScatterChart class into a new top-level, portable chartLibCore/charts/ScatterShape.kt (same package, so ScatterChart.kt just drops the nested enum). Source-breaking: ScatterChart.ScatterShape.X -> ScatterShape.X (5 call sites updated). - shapeRenderer removed from IScatterDataSet/ScatterDataSet entirely (IShapeRenderer is Canvas/Paint-coupled, no portable equivalent). New chartLib/data/ScatterDataSetAndroid.kt defines `var IScatterDataSet.shapeRenderer: IShapeRenderer?` as a WeakHashMap-backed extension property, same side-channel pattern as fillDrawable/fillFormatter. Uses containsKey (not getOrPut) so an explicit null stays null instead of resetting to the default SquareShapeRenderer(). - setScatterShape(shape) moved to the same file as an extension function; getRendererForShape(shape) moved from ScatterDataSet's companion object to a top-level function (source-breaking, unused elsewhere in this repo). - Moved data/ScatterDataSet.kt and interfaces/datasets/IScatterDataSet.kt to chartLibCore. ScatterDataSet is now a fully portable leaf class. - Disclosed behavior change: .copy() no longer propagates a custom shapeRenderer to the copy (documented in ScatterDataSet's class doc, same category as prior fillDrawable/fillFormatter changes). - Updated KotlinMultiplatform.md with the changelog entry. Verified: chartLibCore:allTests, chartLib:compileDebugKotlin, chartLibCompose:assembleDebug, app:assembleDebug, full ./gradlew test, and chartLibComposeMultiplatform:build (Android/desktop/iosArm64/ iosSimulatorArm64) all pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Resume the Compose Multiplatform renderer port now that the concrete DataSet family is fully portable, adding the first chart *data* renderer (previously only axis grid lines existed) using the same "pure pixel-math function + thin DrawScope extension" pattern. - chartLibComposeMultiplatform/.../renderer/BarChartRenderer.kt (new): barPixelRects(dataSet, barWidth, transformer) computes one [left, top, right, bottom] value-space rect per non-stacked entry (mirrors the non-stacked branch of chartLib's buffer.BarBuffer.feed) and maps it to pixel space via TransformerCore.pointValuesToPixel; DrawScope.drawBarChartDataSet(...) draws each rect with drawRect, cycling dataset colors via getColorByIndex(index). - Deliberately out of scope for this slice: stacked bars, animation phases, inverted axis, bar borders/shadows, rounded bars, and the BarData/ChartData container classes (still Android-only) -- this renderer operates directly on a single IBarDataSet plus a caller supplied barWidth, matching how buffer.BarBuffer itself is parameterized, so it doesn't require BarData to be ported first. - New BarChartRendererTest.kt: asserts a positive-value entry's rect maps to the expected content-rect pixels, and that a negative-value entry's rect keeps pixel-space top < bottom (Y-axis inversion handled correctly regardless of value sign). Verified: chartLibComposeMultiplatform:build (all targets, including iosSimulatorArm64Test), chartLibCore:allTests, chartLib:compileDebugKotlin, chartLibCompose:assembleDebug, app:assembleDebug, full ./gradlew test all pass. Updated KotlinMultiplatform.md with the changelog entry. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add pan/pinch-zoom gesture support for Compose Multiplatform charts, replacing chartLib's MotionEvent-based ChartTouchListener/ BarLineChartTouchListener for this proof-of-concept scope. Needed almost no new common code: ViewPortHandler's zoom/translate/refresh/ limitTransAndScale matrix math (bounds-clamped pan/zoom) already lived in chartLibCore from earlier steps. - chartLibComposeMultiplatform/.../gesture/GestureHandling.kt (new): chartGestureMatrix(viewPortHandler, pan, zoom, centroid, ...) copies the current matrixTouch, applies postTranslate(pan) then postScale(zoom, zoom, centroid) -- mirrors BarLineChartTouchListener.performDrag/performZoom combined into one step, since Compose reports incremental per-frame deltas rather than cumulative-since-gesture-start deltas like MotionEvent. Modifier.chartTransformGestures(viewPortHandler, ..., onGesture) wires detectTransformGestures to it and calls ViewPortHandler.refresh(matrix, onGesture, true), which also applies the existing limitTransAndScale bounds clamping. - Deliberately out of scope for this slice: rotation gestures, independent X/Y-only zoom modes, highlight-on-drag, double-tap zoom, fling/deceleration, and OnChartGestureListener callbacks. - New GestureHandlingTest.kt: verifies chartGestureMatrix's pure matrix math directly (pan-only, zoom-about-centroid-only, and disabled-axis cases) without needing a Compose UI test harness or going through ViewPortHandler's stateful clamping. Verified: chartLibComposeMultiplatform:build (all targets, including iosSimulatorArm64Test), chartLibCore:allTests, chartLib:compileDebugKotlin, chartLibCompose:assembleDebug, app:assembleDebug, full ./gradlew test all pass. Updated KotlinMultiplatform.md with the changelog entry, marking Step A.8 complete (as a proof-of-concept). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add a working, runnable Compose Multiplatform demo that ties together every proof-of-concept from Steps A.7/A.8: axis grid lines, a bar chart data renderer, and pan/pinch-zoom gestures, all built exclusively on chartLibComposeMultiplatform (never the Android-only chartLib/ chartLibCompose), and shown identically on Android, Desktop and iOS. Two Gradle modules instead of one, because AGP 9's new KMP DSL forbids combining com.android.application with org.jetbrains.kotlin. multiplatform's androidTarget() in the same project: - demoKmp (new, com.android.kotlin.multiplatform.library, same shape as chartLibComposeMultiplatform): commonMain/DemoKmpApp.kt holds the shared @composable (a 5-entry BarDataSet rendered inside a Canvas via the Step A.7 renderers, with Step A.8's Modifier.chartTransformGestures attached); desktopMain/Main.kt (also runnable via :demoKmp:run); iosMain/MainViewController.kt (ComposeUIViewController-based entry point for a future Xcode wrapper). - demoKmpAndroid (new, plain com.android.application, not multiplatform): MainActivity/AndroidManifest.xml/applicationId, depending on project(":demoKmp") for the shared composable. Both modules registered in settings.gradle.kts. demoKmp's compileSdk bumped to 37 (Compose Multiplatform 1.12.1's Android artifacts require it, matching chartLibComposeMultiplatform); demoKmpAndroid needed the same bump plus its own distinct namespace to avoid an AGP manifest-merger clash with demoKmp. Verified: demoKmpAndroid:assembleDebug, demoKmp:compileKotlinDesktop plus a manual demoKmp:run smoke-test, demoKmp: compileKotlinIosSimulatorArm64, and the full demoKmp:build (all targets, including linking debug/release iosArm64/ iosSimulatorArm64 frameworks) all pass. Full existing chain (chartLibComposeMultiplatform:build, chartLibCore:allTests, chartLib:compileDebugKotlin, chartLibCompose:assembleDebug, app:assembleDebug, ./gradlew test) also still green. Not yet done: an actual Xcode wrapper project embedding MainViewController(), a wasmJsMain target, and richer demo content (multiple chart types/screens). Updated KotlinMultiplatform.md with the changelog entry, marking Step A.9 complete. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add a new KotlinMultiplatform job to .github/workflows/pullrequest.yml on macos-latest, since iOS simulator targets require Xcode and the existing "Check" job (ubuntu-latest) silently skips them via the kotlin.native.ignoreDisabledTargets=true gradle.properties flag added during the earlier CI fix. That means chartLibCore/ chartLibComposeMultiplatform's iOS test coverage, added throughout Steps A.3-A.9, had never actually executed in CI until now. New job steps: - Install JDK 17 + Android SDK (chartLibCore/ chartLibComposeMultiplatform/demoKmp all still have an android target too). - ./gradlew :chartLibCore:allTests :chartLibComposeMultiplatform:allTests -- now genuinely runs the Android host, Desktop, and iOS simulator test suites. - ./gradlew :demoKmp:build :demoKmpAndroid:assembleDebug -- builds every demoKmp target (including linking the iOS frameworks) plus the separate Android app module. - A desktop smoke-test step: launches :demoKmp:run in the background, waits 25s, and asserts the process is still running (i.e. the Compose Desktop window launched without an early crash) before killing it -- a lightweight substitute for a full screenshot job, left as explicitly optional in the plan. - Archives chartLibCore/chartLibComposeMultiplatform test reports as a build artifact for failure diagnosis. Verified locally (this sandbox has Xcode installed): ./gradlew :chartLibCore:allTests :chartLibComposeMultiplatform:allTests passes; the demoKmp:run-then-check-still-alive shell logic was manually confirmed to correctly detect a successfully launched long-lived process. The workflow YAML was validated with python3 -c "import yaml; yaml.safe_load(...)" for syntax correctness (GitHub's own runners couldn't be exercised directly from this environment). Not yet done: a wasmJs target (none of the KMP modules target it yet, so no CI job is needed until one exists) and a real screenshot-diff job for demoKmp. Updated KotlinMultiplatform.md with the changelog entry, marking Step A.10 complete. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…platform
Add com.vanniktech.maven.publish to chartLibCore and
chartLibComposeMultiplatform, mirroring the existing chartLib/chartLibCompose
mavenPublishing { coordinates(...); pom {}; repositories { GitHubPackages } }
pattern. No configure(KotlinMultiplatform(...)) call is needed: the plugin
auto-detects com.android.kotlin.multiplatform.library projects and configures
per-target publications (android/desktop/iosArm64/iosSimulatorArm64[/iosX64])
plus a root kotlinMultiplatform metadata publication automatically.
Fixed two Gradle/plugin interaction bugs surfaced while wiring this up:
- Root gradle.properties' POM_ARTIFACT_ID=chart raced with each module's own
coordinates(artifactId = ...) call for KMP projects, because the plugin's
automatic pomFromGradleProperties() and our explicit coordinates() call
both rename every per-target publication's artifactId in afterEvaluate, and
the KMP rename logic requires the *current* artifactId to still start with
"$projectName-". Fixed with per-module gradle.properties files
(chartLibCore/gradle.properties, chartLibComposeMultiplatform/gradle.properties)
overriding POM_ARTIFACT_ID so both renames agree.
- Applying id("com.vanniktech.maven.publish") version "0.37.0" independently
in 4 sibling subprojects made Gradle load the plugin under different
classloaders for some project pairs, breaking its shared Maven Central
BuildService. Fixed by declaring the versioned plugin once (apply false) in
the root build.gradle.kts's new plugins {} block and referencing it without
a version in all four subprojects.
demoKmp/demoKmpAndroid remain unpublished (demo apps, not libraries).
Verified via ./gradlew :chartLib:publishToMavenLocal :chartLibCompose:publishToMavenLocal
:chartLibCore:publishToMavenLocal :chartLibComposeMultiplatform:publishToMavenLocal
-PRELEASE_SIGNING_ENABLED=false (all four succeed with correct per-target
artifact IDs), plus the full existing verification chain (chartLibCore:allTests,
chartLibComposeMultiplatform:allTests, chartLib:compileDebugKotlin,
chartLibCompose:assembleDebug, app:assembleDebug, demoKmp:build,
demoKmpAndroid:assembleDebug, full ./gradlew test).
Updates KotlinMultiplatform.md's Step A.11 changelog entry and detailed
reference section, marking it [x] — this was the final remaining step from
the original KMP plan.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
hannesa2
force-pushed
the
KotlinMultiplatform
branch
from
September 28, 2026 14:51
3ad13e4 to
2fe1247
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.