-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Open
Description
Hi , i want to show a small button on bottom of price scale.. and when clicked i wanna call chart.fitcontent method, what is the procedure ?

also does hittest method works for price pane view ? i want to detect hittest and display cursor pointer to the button
, i created button but not able to click it
import type {
ISeriesPrimitive,
IPrimitivePaneView,
IPrimitivePaneRenderer,
ISeriesPrimitiveAxisView,
PrimitivePaneViewZOrder,
Coordinate,
MouseEventParams
} from 'lightweight-charts';
import { PluginBase } from '../../tvplugin/plugin-base';
import { getUid } from '@/lib/helper/uid';
export class AutoButtonPlugin extends PluginBase implements ISeriesPrimitive {
myChart: any;
mySeries: any;
uid: string;
_priceAxisViews: AutoScaleAxisView[];
_priceAxisPaneViews: AutoScaleButtonPaneView[];
_clickHandler: (param: MouseEventParams) => void;
constructor(chart: any, series: any, paneIndex?: number, grid?: string) {
super();
this.uid = getUid('autobutton');
this.myChart = chart;
this.mySeries = series;
this._priceAxisViews = [new AutoScaleAxisView(this)];
this._priceAxisPaneViews = [new AutoScaleButtonPaneView(this)];
this._clickHandler = (param: MouseEventParams) => this._handleClick(param);
this.myChart.subscribeClick(this._clickHandler);
}
_handleClick(param: MouseEventParams) {
if (!param.point) return;
console.log('Click detected at:', param.point.x, param.point.y);
const paneView = this._priceAxisPaneViews[0];
if (paneView) {
console.log('Button rect:', paneView._buttonRect);
// For price axis pane views, coordinates need conversion
const chartSize = this.myChart?.paneSize();
const priceScaleWidth = this.myChart?.priceScale()?.width() || 60;
if (chartSize) {
// Convert chart coordinates to price axis coordinates
const priceAxisX = param.point.x - (chartSize.width - priceScaleWidth);
console.log('Price axis X:', priceAxisX, 'Chart width:', chartSize.width, 'Price scale width:', priceScaleWidth);
const hitResult = paneView.hitTest(priceAxisX, param.point.y);
console.log('Hit result:', hitResult);
if (hitResult) {
console.log('Button hit detected!');
this.handleAutoScale();
}
}
}
}
remove() {
if (this.myChart && this._clickHandler) {
this.myChart.unsubscribeClick(this._clickHandler);
}
super.remove();
}
handleAutoScale() {
alert('Auto scale button clicked!');
if (this.mySeries && this.myChart) {
this.mySeries.priceScale().applyOptions({
autoScale: true
});
this.myChart.timeScale().fitContent();
}
}
getCurrentTheme(): 'light' | 'dark' {
const chartOptions = this.myChart?.options();
const bgColor = chartOptions?.layout?.background?.color;
if (bgColor === '#FFFFFF' || bgColor === 'white') {
return 'light';
}
return 'dark';
}
paneViews() {
return [];
}
priceAxisViews() {
return [];
}
priceAxisPaneViews() {
return this._priceAxisPaneViews;
}
updateAllViews() {
this._priceAxisViews.forEach((view) => view.update());
this._priceAxisPaneViews.forEach((view) => view.update());
}
}
class AutoScaleAxisView implements ISeriesPrimitiveAxisView {
_source: AutoButtonPlugin;
_pos: Coordinate | null = null;
_isClicked: boolean = false;
constructor(source: AutoButtonPlugin) {
this._source = source;
}
update(): void {
const chartSize = this._source.myChart?.paneSize();
if (chartSize) {
this._pos = (chartSize.height - 8) as Coordinate;
}
}
coordinate(): Coordinate {
return this._pos ?? -1 as Coordinate;
}
text(): string {
return 'A';
}
textColor(): string {
const theme = this._source.getCurrentTheme();
if (this._isClicked) {
return '#FFFFFF';
}
return theme === 'light' ? '#191919' : '#E2E8F0';
}
backColor(): string {
const theme = this._source.getCurrentTheme();
if (this._isClicked) {
return theme === 'light' ? '#0EA5E9' : '#38BDF8';
}
return theme === 'light' ? '#F8FAFC' : '#334155';
}
visible(): boolean {
return true;
}
tickVisible(): boolean {
return false;
}
handleClick(): void {
this._isClicked = true;
this._source.handleAutoScale();
setTimeout(() => {
this._isClicked = false;
this._source.requestUpdate();
}, 150);
}
}
class AutoScaleButtonPaneView implements IPrimitivePaneView {
_source: AutoButtonPlugin;
_buttonRect: { x: number; y: number; width: number; height: number } | null = null;
constructor(source: AutoButtonPlugin) {
this._source = source;
}
zOrder(): PrimitivePaneViewZOrder {
return 'top';
}
update(): void {
const chartSize = this._source.myChart?.paneSize();
const priceScaleWidth = this._source.myChart?.priceScale()?.width() || 60;
if (chartSize) {
this._buttonRect = {
x: priceScaleWidth - 25,
y: chartSize.height - 20,
width: 20,
height: 15
};
}
}
hitTest(x: number, y: number) {
if (!this._buttonRect) return null;
if (
x >= this._buttonRect.x &&
x <= this._buttonRect.x + this._buttonRect.width &&
y >= this._buttonRect.y &&
y <= this._buttonRect.y + this._buttonRect.height
) {
return {
cursorStyle: 'pointer',
externalId: this._source.uid,
};
}
return null;
}
renderer() {
return new AutoScaleButtonRenderer(this._buttonRect, this._source);
}
}
class AutoScaleButtonRenderer implements IPrimitivePaneRenderer {
_buttonRect: { x: number; y: number; width: number; height: number } | null;
_source: AutoButtonPlugin;
constructor(buttonRect: { x: number; y: number; width: number; height: number } | null, source: AutoButtonPlugin) {
this._buttonRect = buttonRect;
this._source = source;
}
draw(target: any) {
if (!this._buttonRect) return;
target.useBitmapCoordinateSpace((scope: any) => {
const ctx = scope.context;
const { horizontalPixelRatio, verticalPixelRatio } = scope;
const x = this._buttonRect!.x * horizontalPixelRatio;
const y = this._buttonRect!.y * verticalPixelRatio;
const width = this._buttonRect!.width * horizontalPixelRatio;
const height = this._buttonRect!.height * verticalPixelRatio;
const radius = 3 * horizontalPixelRatio;
const theme = this._source.getCurrentTheme();
const axisView = this._source._priceAxisViews[0];
const bgColor = axisView?.backColor() || (theme === 'light' ? '#F8FAFC' : '#334155');
const textColor = axisView?.textColor() || (theme === 'light' ? '#191919' : '#E2E8F0');
const borderColor = theme === 'light' ? '#E2E8F0' : '#475569';
ctx.fillStyle = bgColor;
ctx.beginPath();
ctx.roundRect(x, y, width, height, radius);
ctx.fill();
ctx.strokeStyle = borderColor;
ctx.lineWidth = 1 * horizontalPixelRatio;
ctx.beginPath();
ctx.roundRect(x, y, width, height, radius);
ctx.stroke();
ctx.fillStyle = textColor;
ctx.font = `bold ${10 * verticalPixelRatio}px -apple-system, BlinkMacSystemFont, 'Segoe UI', Arial, sans-serif`;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('A', x + width / 2, y + height / 2);
});
}
}
Metadata
Metadata
Assignees
Labels
No labels