Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
211c40b
Add KotlinMultiplatform plan
hannesa2 Sep 26, 2026
178ae69
Created a new Kotlin Multiplatform module and extract 5 genuinely pla…
hannesa2 Sep 26, 2026
d8dd425
Full Entry family migrated to chartLibCore.
hannesa2 Sep 26, 2026
40611cc
DataSet-family groundwork
hannesa2 Sep 27, 2026
da3a643
CI fix
hannesa2 Sep 27, 2026
defeb39
Highlight/IHighlighter moved
hannesa2 Sep 27, 2026
0bd7fe2
Migrate components/ (axes, legend, limit lines)
hannesa2 Sep 27, 2026
8d56dc9
Step A.6: migrate ViewPortHandler/Transformer to chartLibCore
hannesa2 Sep 27, 2026
83a270c
Step A.3 (completion): migrate IDataSet/BaseDataSet/DataSet/ChartData…
hannesa2 Sep 27, 2026
13ba483
Step A.5 (axes + limit lines): migrate ComponentBase/AxisBase/XAxis/Y…
hannesa2 Sep 27, 2026
fd9748e
Step A.5 (continuation): migrate Legend/LegendEntry/Description to ch…
hannesa2 Sep 27, 2026
4495a3c
Step A.7 (module scaffold): create chartLibComposeMultiplatform KMP m…
hannesa2 Sep 27, 2026
e3abdeb
Step A.7 (grid line proof-of-concept): first Compose Multiplatform re…
hannesa2 Sep 27, 2026
38fb590
Step A.3 (continuation, part 2): migrate concrete DataSet family to c…
hannesa2 Sep 27, 2026
fcc1b0b
Step A.3 (continuation, part 3): split Fill class, migrate BarDataSet
hannesa2 Sep 27, 2026
27a2bab
Step A.3 (continuation, part 4): decouple IFillFormatter, migrate Lin…
hannesa2 Sep 27, 2026
b602993
Step A.3 (continuation, part 5): split IShapeRenderer, migrate Scatte…
hannesa2 Sep 27, 2026
6eede79
Step A.7 (continuation): first Compose Multiplatform data renderer
hannesa2 Sep 27, 2026
5e52e36
Step A.8: Compose Multiplatform gesture handling proof-of-concept
hannesa2 Sep 27, 2026
e04a7c1
Step A.9: demoKmp Compose Multiplatform demo app
hannesa2 Sep 28, 2026
6c16678
Step A.10: CI for Kotlin Multiplatform (iOS/Desktop)
hannesa2 Sep 28, 2026
2fe1247
Step A.11: Maven publishing for chartLibCore and chartLibComposeMulti…
hannesa2 Sep 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions .github/workflows/pullrequest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -125,3 +125,49 @@ jobs:
with:
name: Chart-Lint-report
path: ./**/build/reports/lint-results*.html
KotlinMultiplatform:
name: Kotlin Multiplatform (iOS/Desktop)
# iOS simulator targets (chartLibCore/chartLibComposeMultiplatform) require a macOS runner
# with Xcode; on the ubuntu "Check" job these tasks are silently skipped
# (kotlin.native.ignoreDisabledTargets=true), so this job is the only place they actually run.
runs-on: macos-latest
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
submodules: true
- name: Install JDK
uses: actions/setup-java@v6
with:
distribution: "temurin"
java-version: 17
- name: Install Android SDK
uses: hannesa2/action-android/install-sdk@0.17.0
- name: Run chartLibCore/chartLibComposeMultiplatform tests (Android host, Desktop, iOS simulator)
run: ./gradlew :chartLibCore:allTests :chartLibComposeMultiplatform:allTests
- name: Build demoKmp (all targets) and demoKmpAndroid
run: ./gradlew :demoKmp:build :demoKmpAndroid:assembleDebug
- name: Desktop smoke-test (demoKmp:run starts without crashing)
run: |
./gradlew :demoKmp:run --console=plain &
run_pid=$!
sleep 25
if kill -0 "$run_pid" 2>/dev/null; then
echo "demoKmp:run is still running after 25s -> desktop app launched successfully"
kill "$run_pid"
wait "$run_pid" 2>/dev/null || true
else
wait "$run_pid"
exit_code=$?
echo "demoKmp:run exited early with code $exit_code"
exit 1
fi
- name: Archive Kotlin Multiplatform test reports
uses: actions/upload-artifact@v7
if: ${{ always() }}
with:
name: Chart-KotlinMultiplatform-test-report
path: |
chartLibCore/build/reports/tests
chartLibComposeMultiplatform/build/reports/tests
if-no-files-found: warn
1,414 changes: 1,414 additions & 0 deletions KotlinMultiplatform.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import android.Manifest
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Color
import android.graphics.Paint
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
Expand All @@ -21,6 +20,7 @@ import info.appdev.charting.components.YAxis.AxisDependency
import info.appdev.charting.data.CandleData
import info.appdev.charting.data.CandleDataSet
import info.appdev.charting.data.CandleEntryFloat
import info.appdev.charting.utils.PaintStyle

class CandleStickChartActivity : DemoBase(), OnSeekBarChangeListener {

Expand Down Expand Up @@ -108,9 +108,9 @@ class CandleStickChartActivity : DemoBase(), OnSeekBarChangeListener {
set1.shadowColor = Color.DKGRAY
set1.shadowWidth = 0.7f
set1.decreasingColor = Color.BLUE
set1.decreasingPaintStyle = Paint.Style.FILL
set1.decreasingPaintStyle = PaintStyle.FILL
set1.increasingColor = Color.rgb(122, 242, 84)
set1.increasingPaintStyle = Paint.Style.STROKE
set1.increasingPaintStyle = PaintStyle.STROKE
set1.neutralColor = Color.BLUE

//set1.setHighlightLineWidth(1f);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import info.appdev.charting.components.YAxis
import info.appdev.charting.data.EntryFloat
import info.appdev.charting.data.LineData
import info.appdev.charting.data.LineDataSet
import info.appdev.charting.data.fillFormatter
import info.appdev.charting.formatter.IFillFormatter
import info.appdev.charting.interfaces.dataprovider.LineDataProvider
import info.appdev.charting.interfaces.datasets.ILineDataSet
Expand Down
6 changes: 4 additions & 2 deletions app/src/main/kotlin/info/appdev/chartexample/DataTools.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ package info.appdev.chartexample

import android.content.Context
import android.graphics.Color
import android.graphics.DashPathEffect
import info.appdev.charting.utils.DashEffect
import androidx.core.content.ContextCompat
import info.appdev.charting.charts.LineChart
import info.appdev.charting.data.EntryFloat
import info.appdev.charting.data.LineData
import info.appdev.charting.data.LineDataSet
import info.appdev.charting.data.fillDrawable
import info.appdev.charting.data.fillFormatter
import info.appdev.charting.formatter.IFillFormatter
import info.appdev.charting.interfaces.dataprovider.LineDataProvider
import info.appdev.charting.interfaces.datasets.ILineDataSet
Expand Down Expand Up @@ -214,7 +216,7 @@ class DataTools {

// customize legend entry
lineDataSet01.formLineWidth = 1f
lineDataSet01.formLineDashEffect = DashPathEffect(floatArrayOf(10f, 5f), 0f)
lineDataSet01.formLineDashEffect = DashEffect(floatArrayOf(10f, 5f), 0f)
lineDataSet01.formSize = 15f

// text size of values
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import info.appdev.charting.components.YAxis
import info.appdev.charting.data.EntryFloat
import info.appdev.charting.data.LineData
import info.appdev.charting.data.LineDataSet
import info.appdev.charting.data.fillFormatter
import info.appdev.charting.formatter.IFillFormatter
import info.appdev.charting.interfaces.dataprovider.LineDataProvider
import info.appdev.charting.interfaces.datasets.ILineDataSet
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import info.appdev.chartexample.notimportant.DemoBase
import info.appdev.charting.data.EntryFloat
import info.appdev.charting.data.LineData
import info.appdev.charting.data.LineDataSet
import info.appdev.charting.data.fillDrawable
import info.appdev.charting.data.fillFormatter
import info.appdev.charting.formatter.IFillFormatter
import info.appdev.charting.interfaces.dataprovider.LineDataProvider
import info.appdev.charting.interfaces.datasets.ILineDataSet
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@ import info.appdev.chartexample.DataTools.Companion.getValues
import info.appdev.chartexample.custom.CustomScatterShapeRenderer
import info.appdev.chartexample.databinding.ActivityScatterchartBinding
import info.appdev.chartexample.notimportant.DemoBase
import info.appdev.charting.charts.ScatterChart
import info.appdev.charting.charts.ScatterShape
import info.appdev.charting.components.Legend
import info.appdev.charting.data.EntryFloat
import info.appdev.charting.data.ScatterData
import info.appdev.charting.data.ScatterDataSet
import info.appdev.charting.data.setScatterShape
import info.appdev.charting.data.shapeRenderer
import info.appdev.charting.highlight.Highlight
import info.appdev.charting.interfaces.datasets.IScatterDataSet
import info.appdev.charting.listener.OnChartValueSelectedListener
Expand Down Expand Up @@ -99,10 +101,10 @@ class ScatterChartActivity : DemoBase(), OnSeekBarChangeListener, OnChartValueSe

// create a dataset and give it a type
val set1 = ScatterDataSet(values1, "DS 1")
set1.setScatterShape(ScatterChart.ScatterShape.SQUARE)
set1.setScatterShape(ScatterShape.SQUARE)
set1.color = ColorTemplate.COLORFUL_COLORS[0]
val set2 = ScatterDataSet(values2, "DS 2")
set2.setScatterShape(ScatterChart.ScatterShape.CIRCLE)
set2.setScatterShape(ScatterShape.CIRCLE)
set2.scatterShapeHoleColor = ColorTemplate.COLORFUL_COLORS[3]
set2.scatterShapeHoleRadius = 3f
set2.color = ColorTemplate.COLORFUL_COLORS[1]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package info.appdev.chartexample

import android.annotation.SuppressLint
import android.graphics.Color
import android.graphics.DashPathEffect
import info.appdev.charting.utils.DashEffect
import android.graphics.Typeface
import android.os.Bundle
import android.view.Menu
Expand All @@ -23,6 +23,7 @@ import info.appdev.charting.components.LimitLine.LimitLabelPosition
import info.appdev.charting.data.EntryFloat
import info.appdev.charting.data.LineData
import info.appdev.charting.data.LineDataSet
import info.appdev.charting.data.fillDrawable
import info.appdev.charting.highlight.Highlight
import info.appdev.charting.interfaces.datasets.ILineDataSet
import info.appdev.charting.listener.ChartTouchListener.ChartGesture
Expand Down Expand Up @@ -272,7 +273,7 @@ class SpecificPositionsLineChartActivity : DemoBase(), OnSeekBarChangeListener,
set11.valueTextSize = 9f
set11.isDrawFilled = true
set11.formLineWidth = 1f
set11.formLineDashEffect = DashPathEffect(floatArrayOf(10f, 5f), 0f)
set11.formLineDashEffect = DashEffect(floatArrayOf(10f, 5f), 0f)
set11.formSize = 15f
if (getSDKInt() >= 18) {
// fill drawable only supported on api level 18 and above
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import info.appdev.charting.data.EntryDouble
import info.appdev.charting.data.EntryFloat
import info.appdev.charting.data.LineData
import info.appdev.charting.data.LineDataSet
import info.appdev.charting.data.fillFormatter
import info.appdev.charting.formatter.IFillFormatter
import info.appdev.charting.interfaces.dataprovider.LineDataProvider
import info.appdev.charting.interfaces.datasets.ILineDataSet
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import info.appdev.chartexample.DataTools.Companion.getValues
import info.appdev.charting.charts.ScatterChart.ScatterShape
import info.appdev.charting.charts.ScatterShape
import info.appdev.charting.data.BarData
import info.appdev.charting.data.BarDataSet
import info.appdev.charting.data.BarEntryFloat
Expand All @@ -20,6 +20,7 @@ import info.appdev.charting.data.PieDataSet
import info.appdev.charting.data.PieEntryFloat
import info.appdev.charting.data.ScatterData
import info.appdev.charting.data.ScatterDataSet
import info.appdev.charting.data.setScatterShape
import info.appdev.charting.interfaces.datasets.IBarDataSet
import info.appdev.charting.interfaces.datasets.ILineDataSet
import info.appdev.charting.interfaces.datasets.IScatterDataSet
Expand Down
11 changes: 11 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,17 @@ buildscript {
}
}

plugins {
// Declared once here (without applying) so every module that publishes
// (chartLib, chartLibCompose, chartLibCore, chartLibComposeMultiplatform) resolves
// the exact same plugin classloader instance. Applying the same versioned plugin
// id independently in 3+ sibling subprojects can otherwise make Gradle load it under
// different classloaders, which breaks the plugin's shared Maven Central build service
// ("Cannot set the value of task ... property 'buildService' ... loaded with
// InstrumentingVisitableURLClassLoader...").
id("com.vanniktech.maven.publish") version "0.37.0" apply false
}

allprojects {
repositories {
google()
Expand Down
3 changes: 2 additions & 1 deletion chartLib/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import java.net.URI
plugins {
id("com.android.library")
id("maven-publish")
id("com.vanniktech.maven.publish") version "0.37.0"
id("com.vanniktech.maven.publish")
}

android {
Expand Down Expand Up @@ -55,6 +55,7 @@ dependencies {
lintChecks(project(":lint")) // applies locally
lintPublish(project(path = ":lint", configuration = "lintJar")) // embeds in published AAR

api(project(":chartLibCore"))
implementation("androidx.annotation:annotation:1.10.0")
implementation("androidx.core:core:1.16.0")
implementation("androidx.activity:activity-ktx:1.10.1")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import info.appdev.charting.highlight.BarHighlighter
import info.appdev.charting.highlight.Highlight
import info.appdev.charting.interfaces.dataprovider.BarDataProvider
import info.appdev.charting.renderer.BarChartRenderer
import info.appdev.charting.utils.copyInto
import info.appdev.charting.utils.toCommonRectF
import timber.log.Timber
import java.util.Locale

Expand Down Expand Up @@ -178,7 +180,9 @@ open class BarChart : BarLineChartBase<BarData>, BarDataProvider {

outputRect.set(left, top, right, bottom)

getTransformer(set.axisDependency).rectValueToPixel(outputRect)
val commonRect = outputRect.toCommonRectF()
getTransformer(set.axisDependency).rectValueToPixel(commonRect)
commonRect.copyInto(outputRect)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Matrix
import android.graphics.Paint
import android.graphics.RectF
import android.util.AttributeSet
Expand All @@ -15,6 +14,8 @@ import info.appdev.charting.components.Legend.LegendVerticalAlignment
import info.appdev.charting.components.XAxis.XAxisPosition
import info.appdev.charting.components.YAxis
import info.appdev.charting.components.YAxis.AxisDependency
import info.appdev.charting.components.getRequiredHeightSpace
import info.appdev.charting.components.getRequiredWidthSpace
import info.appdev.charting.data.BarLineScatterCandleBubbleData
import info.appdev.charting.data.BaseEntry
import info.appdev.charting.data.EntryFloat
Expand All @@ -37,6 +38,8 @@ import info.appdev.charting.utils.PointF.Companion.getInstance
import info.appdev.charting.utils.PointF.Companion.recycleInstance
import info.appdev.charting.utils.Transformer
import info.appdev.charting.utils.convertDpToPixel
import info.appdev.charting.utils.toAndroidRectF
import info.appdev.charting.utils.Matrix
import timber.log.Timber
import kotlin.math.abs
import kotlin.math.max
Expand Down Expand Up @@ -242,7 +245,7 @@ abstract class BarLineChartBase<T : BarLineScatterCandleBubbleData<IBarLineScatt
// Y-axis labels could have changed in size affecting the offsets
if (this.isAutoScaleMinMax) {
calculateOffsets()
viewPortHandler.refresh(viewPortHandler.matrixTouch, this, false)
viewPortHandler.refresh(viewPortHandler.matrixTouch, { invalidate() }, false)
}

xAxisRenderer.renderAxisLine(canvas)
Expand Down Expand Up @@ -277,7 +280,7 @@ abstract class BarLineChartBase<T : BarLineScatterCandleBubbleData<IBarLineScatt

if (this.isClipDataToContentEnabled) {
// make sure the data cannot be drawn outside the content-rect
canvas.clipRect(viewPortHandler.contentRect)
canvas.clipRect(viewPortHandler.contentRect.toAndroidRectF())
}

dataRenderer!!.drawData(canvas)
Expand Down Expand Up @@ -322,7 +325,7 @@ abstract class BarLineChartBase<T : BarLineScatterCandleBubbleData<IBarLineScatt

if (this.isClipValuesToContentEnabled) {
clipRestoreCount = canvas.save()
canvas.clipRect(viewPortHandler.contentRect)
canvas.clipRect(viewPortHandler.contentRect.toAndroidRectF())

dataRenderer?.drawValues(canvas)

Expand Down Expand Up @@ -541,11 +544,11 @@ abstract class BarLineChartBase<T : BarLineScatterCandleBubbleData<IBarLineScatt
if (mDrawGridBackground) {
// draw the grid background

c.drawRect(viewPortHandler.contentRect, mGridBackgroundPaint!!)
c.drawRect(viewPortHandler.contentRect.toAndroidRectF(), mGridBackgroundPaint!!)
}

if (this.isDrawBordersEnabled) {
c.drawRect(viewPortHandler.contentRect, mBorderPaint!!)
c.drawRect(viewPortHandler.contentRect.toAndroidRectF(), mBorderPaint!!)
}
}

Expand Down Expand Up @@ -596,7 +599,7 @@ abstract class BarLineChartBase<T : BarLineScatterCandleBubbleData<IBarLineScatt
val center = viewPortHandler.contentCenter

viewPortHandler.zoomIn(center.x, -center.y, mZoomMatrixBuffer)
viewPortHandler.refresh(mZoomMatrixBuffer, this, false)
viewPortHandler.refresh(mZoomMatrixBuffer, { invalidate() }, false)

recycleInstance(center)

Expand All @@ -614,7 +617,7 @@ abstract class BarLineChartBase<T : BarLineScatterCandleBubbleData<IBarLineScatt
val center = viewPortHandler.contentCenter

viewPortHandler.zoomOut(center.x, -center.y, mZoomMatrixBuffer)
viewPortHandler.refresh(mZoomMatrixBuffer, this, false)
viewPortHandler.refresh(mZoomMatrixBuffer, { invalidate() }, false)

recycleInstance(center)

Expand All @@ -630,7 +633,7 @@ abstract class BarLineChartBase<T : BarLineScatterCandleBubbleData<IBarLineScatt
*/
fun resetZoom() {
viewPortHandler.resetZoom(mZoomMatrixBuffer)
viewPortHandler.refresh(mZoomMatrixBuffer, this, false)
viewPortHandler.refresh(mZoomMatrixBuffer, { invalidate() }, false)

// Range might have changed, which means that Y-axis labels
// could have changed in size, affecting Y-axis size.
Expand All @@ -648,7 +651,7 @@ abstract class BarLineChartBase<T : BarLineScatterCandleBubbleData<IBarLineScatt
*/
fun zoom(scaleX: Float, scaleY: Float, x: Float, y: Float) {
viewPortHandler.zoom(scaleX, scaleY, x, -y, mZoomMatrixBuffer)
viewPortHandler.refresh(mZoomMatrixBuffer, this, false)
viewPortHandler.refresh(mZoomMatrixBuffer, { invalidate() }, false)

// Range might have changed, which means that Y-axis labels
// could have changed in size, affecting Y-axis size.
Expand Down Expand Up @@ -676,7 +679,7 @@ abstract class BarLineChartBase<T : BarLineScatterCandleBubbleData<IBarLineScatt

val save = mZoomMatrixBuffer
viewPortHandler.zoom(scaleX, scaleY, center.x, -center.y, save)
viewPortHandler.refresh(save, this, false)
viewPortHandler.refresh(save, { invalidate() }, false)
}

/**
Expand Down Expand Up @@ -715,7 +718,7 @@ abstract class BarLineChartBase<T : BarLineScatterCandleBubbleData<IBarLineScatt
fun fitScreen() {
val save = mFitScreenMatrixBuffer
viewPortHandler.fitScreen(save)
viewPortHandler.refresh(save, this, false)
viewPortHandler.refresh(save, { invalidate() }, false)

calculateOffsets()
postInvalidate()
Expand Down Expand Up @@ -1301,9 +1304,9 @@ abstract class BarLineChartBase<T : BarLineScatterCandleBubbleData<IBarLineScatt
//Restoring old position of chart.

getTransformer(AxisDependency.LEFT).pointValuesToPixel(mOnSizeChangedBuffer)
viewPortHandler.centerViewPort(mOnSizeChangedBuffer, this)
viewPortHandler.centerViewPort(mOnSizeChangedBuffer) { invalidate() }
} else {
viewPortHandler.refresh(viewPortHandler.matrixTouch, this, true)
viewPortHandler.refresh(viewPortHandler.matrixTouch, { invalidate() }, true)
}
}

Expand Down
Loading
Loading