We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
0.14.0-dev.2568+42dac40b3
0.14.0-dev.322+e50d59e
VSCode Version: 1.96.2 Commit: fabdb6a30b49f79a7aba0f2ad9df9b399473380f Date: 2024-12-19T10:22:47.216Z Electron: 32.2.6 ElectronBuildId: 10629634 Chromium: 128.0.6613.186 Node.js: 20.18.1 V8: 12.8.374.38-electron.0 OS: Linux x64 6.12.6-arch1-1
ziglang.vscode-zig 0.6.3
const std = @import("std"); const eql = std.mem.eql; const bufPrint = std.fmt.bufPrint; const account = @import("object_account.zig"); const bar = @import("../bar.zig"); const Period = bar.Period; const gui = @import("deps/gui/gui.zig"); const Window = gui.Window; const Color = gui.Color; const TextStyle = gui.TextStyle; const CrossHair = @import("object_crosshair.zig"); const horizontalline = @import("object_horizontalline.zig"); const trendchannel = @import("object_trendchannel.zig"); const overlay = @import("object_overlay.zig"); pub const ChartView = @This(); base_: gui.ViewBase, _rate_axis_width_: f32 = 0, // percentから計算する _date_axis_height_: f32 = 0, // rate_widthから計算する _rect_chart: gui.Rect, // OHLC描画エリア _rect_rate: gui.Rect, // プライス描画エリア _rect_date: gui.Rect, // 日時描画エリア _origin_x_slide: f32 = 0, // right_margin |<- r_chart.rigtht() -> 無限大 _bar_width_: f32 = 0, // 一本の幅 _right_bar_index: i32 = 0, // rangeの右端 描画領域のOHLC番号 負はあり得る? _left_bar_index: i32 = 0, // rangeの左端 描画領域のOHLC番号 負はあり得る? _origin_sub_time: ?bar.MTime = null, // period拡大縮小基準バー時刻 origin_xを移動させる _origin_sub_x: f32 = 0, // period拡大縮小基準位置 r_chart.right()からの距離 _origin_sub_out: bool = false, // seriesの範囲外にきちゃう _v_scale: f32 = 0, // 縦縮尺 実値(プライスの差額)に何倍を掛ければChartの⊿Yになるか _area_price_highest: f64 = 0, // 表示バー中の最高値 _area_price_lowest: f64 = 0, // 表示バー中の最安値 _area_price_center: f64 = 0, // 描画を中央中心にするために _pitchMajor: f64 = 0, // _pitchMinor: f64 = 0, // _rate_digits: u3 = 4, // 値軸の少数点以下桁数 _fixed_v_scale: f32 = 0, // 他のChartと比較する用の固定スケール _use_max_v_scale: bool = false, // ObjOverlaysのMaxVScaleを使う _series_: ?*bar.OHLCSeries = null, // 本体 _bar_type: BarType, _reverse: bool = false, // 天地反転 _style: Style = Style{}, mouse_mode: MouseMode = .default, // position は常にリアルタイム表示 obj_account: account.ObjAccount, is_show_trades: bool = false, // objects こっちにobject_editorを持ってくるとui.MouseEventが入ってしまうからダメ // context_menu: []menu.MenuItem, is_show_objects: bool = false, _temp_crosshair: CrossHair = .{}, obj_horizontal_lines: horizontalline.ObjHorizontalLine, obj_trend_channels: trendchannel.ObjTrendChannel, is_show_overlay: bool = false, obj_overlays: overlay.ObjOverlays, // datetimeは_series_に合わせる // obj_virtical_lines: []VerticalLine, // obj_temp_vertical_line: VerticalLine, // obj_zigzags: []ZigZag, // obj_temp_zigzag: ZigZag, // is_show_memos: bool, // memos: []&ObjMemo, // memo_editor: MemoEditor = MemoEditor{}, // context_menu: , pub const BarType = enum{ ohlc, line, bar_ }; pub const Style = struct{ base: gui.Style = .{}, // draw用 // // color_text_white gx.Color = Color.rgb(76, 76, 76) // txt_cfg gx.TextCfg = gx.TextCfg{} // cfg_no_data gx.TextCfg = gx.TextCfg{ // size: 20 // color: gx.white // align: .center // vertical_align: .middle // } bg_color: Color = Color.rgb(5, 5, 5), //背景 bar_color: Color = Color.rgba(43, 255, 54, 255), //43 255 54 bar_color_body_up: Color = Color.rgb(5, 5, 5), // 陽線実体部 bar_color_body_down: Color = Color.rgba(255, 255, 255, 205), // // grid // color_grid_rate_major = $if android { Color.rgb(51, 51, 51) } $else { Color.rgb(42, 42, 42) } // 太線 // color_grid_rate_minor = $if android { Color.rgb(73, 73, 73) } $else { Color.rgb(70, 70, 70) } // color_grid_date_major = $if android { Color.rgb(58, 58, 58) } $else { Color.rgb(45, 45, 45) } // color_grid_date_minor = $if android { Color.rgb(79, 79, 79) } $else { Color.rgb(76, 76, 76) } color_grid_rate_major: Color = Color.rgb(42, 42, 42), // 太線 color_grid_rate_minor: Color = Color.rgb(70, 70, 70), // 細線 color_grid_date_major: Color = Color.rgb(45, 45, 45), color_grid_date_minor: Color = Color.rgb(76, 76, 76), fntRate: TextStyle = .{ .size = 13, .color = Color.rgb(240, 240, 240), .v_align = .center }, fntDate: TextStyle = .{ .size = 13, .color = Color.rgba(255, 255, 255, 255), .h_align = .center, .v_align = .top, }, // // トレンドライン/水平ライン // obj_line_color = $if android { Color.rgba(0, 100, 255, 200) } $else { Color.rgba(0, 50, 255, 200) } // // // tmp_zigzag_color gx.Color = Color.rgba(231, 70, 70, 190) }; pub const Param = struct { base: gui.ViewBase.Param = .{ .width = 300, .height = 200, .width_min = 150, .height_min = 100, }, id: ?[]const u8 = null, style: Style = .{}, bar_type: BarType = .ohlc, }; pub fn init(p: Param) !gui.IView { const self = try Window.allocator_.create(ChartView); try Window.putNamedView(p.id, self); self.* = ChartView{ .base_ = gui.ViewBase.init(p.base), ._style = p.style, ._bar_type = p.bar_type, ._rect_chart = gui.Rect.init(0, 0, 0, 0), ._rect_rate = gui.Rect.init(0, 0, 0, 0), ._rect_date = gui.Rect.init(0, 0, 0, 0), ._bar_width_ = 4, .obj_overlays = overlay.ObjOverlays.init(Window.allocator_), .obj_account = .{}, .obj_horizontal_lines = horizontalline.ObjHorizontalLine.init(Window.allocator_), .obj_trend_channels = trendchannel.ObjTrendChannel.init(Window.allocator_), }; return .{ .impl = @alignCast(@ptrCast(self)), .type_name = @typeName(@This()), .setPosFn = setPos, // self.base_.setPosFn, .setSizeFn = setSize, // self.base_.setSizeFn, // .getRectFn = self.base_.getRectFn, .getRectFn = getRect, .setPaddingFn = self.base_.setPaddingFn, .getContentRectFn = self.base_.getContentRectFn, .setVisibilityFn = self.base_.setVisibilytyFn, .getVisibilityFn = self.base_.getVisibilytyFn, .isVisibleFn = self.base_.isVisibleFn, .setReadonlyFn = self.base_.setReadonlyFn, .isReadonlyFn = self.base_.isReadonlyFn, // .pointInsideFn = self.base_.pointInsideFn, .layoutFn = self.base_.layoutFn, .deinitFn = deinit, .drawFn = draw, .onEventFn = onEvent, }; } /// series は参照なので、元の方が解放する責任を持つ pub fn deinit(impl: *anyopaque) void{ // std.log.debug("ChartView.deinit()\n", .{}); var self: *ChartView = @alignCast(@ptrCast(impl)); self.obj_horizontal_lines.deinit(); self.obj_trend_channels.deinit(); self.obj_overlays.deinit(); // if (self._series_) |sr| sr.deinit(); // ただ、これ、元の場所でもfree()するとダブルフリーになる // std.log.debug(.info, "ChartView.deinit() called.\n",.{}); Window.allocator_.destroy(self); } //---------------- pub fn isNoData(self: ChartView) bool { return self._series_ == null or self._series_.size() < 1; } /// series を解放する責任はChartViewは持たないので、呼び出し側で管理すること pub fn setSeries(self: *ChartView, series: ?*bar.OHLCSeries) void{ self._series_ = series; self.setRatePitch(); self._origin_x_slide = 0; // periodを変えたときは位置はリセットしたいから try self.moveToOriginSub(); self.setWindowRange(); // setWindowRange()はseries[n]の設定なのでここでも必要 // TODO Symbolごとにオブジェクトを切り替える // self.obj_horizontal_lines // self.obj_trend_channels // self.obj_overlays } /// 基準線に合わせる origin_sub_x の位置を同じ場所になるようにslideを動かす fn moveToOriginSub(self: *ChartView) !void{ if (self._origin_sub_time == null) return; // n := self.barN(self._origin_sub_time) or { // // 基準線の時刻のBarが見つからない=足りない場合 // self._origin_sub_out = true // if err !is pr.OHLCSeriesError { // panic("${err.type_name()} is not handled.") // } // switch unsafe{ pr.OHLCSeriesErrorCode(err.code()) } { // .lack_older { // 後ろなら目一杯後ろに移動させる // x := self.getX(self._series_.size()) // dx := self._origin_sub_x - x // self._origin_x_slide += dx // } // .lack_newer { // TODO // } // else { // panic("$err: This should never happen.") // } // } // return err // } // self._origin_sub_out = false // dx := self._origin_sub_x - self.getX(n) // self._origin_x_slide += dx } // scale & origin ---------------------------- const rate_axis_pct: f32 = 0.08; // デバイス画面幅を基準とし全てのサイズを決定していく。 const height_oscillator_pct: f32 = 0.20; // 占める比率% const v_margin: f32 = 5; // 上下端から最高値安値までの余白 const min_rate_axis_width: f32 = 35; const right_margin: f32 = 4; // バーの右端の余白幅 TODO 足2本分にする pub fn setPos(impl: *anyopaque, x: f32, y: f32) void{ // std.log.debug("ChartView.setPos({}, {})\n", .{x, y}); var self: *ChartView = @alignCast(@ptrCast(impl)); self.base_.rect_.setPos(x, y); // std.log.debug("{d}\n",.{self.base_.rect_.y}); self.initRects(); } pub fn setSize(impl: *anyopaque, width: f32, height: f32) void{ // std.log.debug("ChartView.setSize({}, {})\n", .{width, height}); var self: *ChartView = @alignCast(@ptrCast(impl)); self.base_.rect_.setSize(width, height); self.initRects(); self.setWindowRange(); // 右側位置は変わらず、左側だけ広く/狭くなる self.setSubOrigin(self._origin_sub_time); } // Layoutで必要になる pub fn getRect(impl: *anyopaque) gui.Rect { var self: *ChartView = @alignCast(@ptrCast(impl)); return self.base_.rect_.getRect(); } /// TODO Widgetサイズから計算 TODO resizableに fn initRects(self: *ChartView) void{ self._rate_axis_width_ = min_rate_axis_width; self._date_axis_height_ = self._rate_axis_width_ / 3; // ローソク領域 self._rect_chart = gui.Rect.init( self.base_.rect_._x_, self.base_.rect_._y_, self.base_.rect_.width() - self._rate_axis_width_, self.base_.rect_.height() - self._date_axis_height_); // std.log.debug("ChartView._rect_chart: {}\n", .{self._rect_chart}); // プライス領域 self._rect_rate = gui.Rect.init( self._rect_chart.right(), self._rect_chart.top(), self._rate_axis_width_, self._rect_chart.height()); // std.log.debug("ChartView._rect_rate: {}\n", .{self._rect_rate}); // 日付領域 self._rect_date = gui.Rect.init( self._rect_chart.left(), self._rect_chart.bottom(), self._rect_chart.width(), self._date_axis_height_); // std.log.debug("ChartView._rect_date: {}\n", .{self._rect_date}); } //---------------------------------------------- // 仕様 // originX // series[0]とイコールの場所 x // 原点(originX)を動かすの // series初期化時 setSeries() // move_mouse() scroll() // sub_origin_x // 描画範囲 // r_chartの右端から左端まで // それ以外は描画しない // setWindowRange() // series[n]を表す // right_bar_index = series[n] // left_bar_indes = series[n + 描画範囲の本数] /// この描画原点を基準にしてBarを配置、Scrollはこれを動かして描画範囲が変える inline fn originX(self: ChartView) f32 { return self._rect_chart.right() - right_margin + self._origin_x_slide; } /// 原点を移動させる // TODO 過去足追加機能 // TODO スクロール時右端不十分 pub fn offsetOrigin(self: *ChartView, dx: f32) void{ const xw = self._origin_sub_x - self._origin_x_slide; self._origin_x_slide += dx; if (dx > 0) { // 過去、左端 const ss = if (self._series_) |sr| sr.size() else 0; if (self.originX() - (self._bar_width_ * @as(f32, @floatFromInt(ss))) > 0) { self._origin_x_slide -= dx; } } else if (dx < 0) { // 未来、右端 TODO スクロール時 const right = self._rect_chart.right() - right_margin; if (right > self.originX()) { // 右端よりさらに右に原点が位置する場合は self._origin_x_slide -= dx; } } if (self._origin_sub_time != null) { self._origin_sub_x = self._origin_x_slide + xw; } self.setWindowRange(); } /// 基準描画位置移動 指定されたx位置にそのtimeを持っていく pub fn setSubOrigin(self: *ChartView, time: ?bar.MTime) void{ self._origin_sub_time = time; // if (self._origin_sub_time == null) { // self._origin_sub_x = 0; // } else { // const n = self.barN(self._origin_sub_time) or -1; // self._origin_sub_x = self.getX(n); // } // // println("${@FN}: origin_sub_x=${self._origin_sub_x}") if (self._origin_sub_time) |ost| { var n: i32 = -1; if (self.barN(ost)) |val| { n = @intCast(val); } else |_| {} self._origin_sub_x = self.getX(n); } else { self._origin_sub_x = 0; } } pub fn clearSubOrigin(self: *ChartView) void{ self._origin_sub_time = null; self._origin_sub_x = 0; } /// 直近が右端にあるか inline fn isRight(self: ChartView) bool { return self.originX() == (self._rect_chart.right() - right_margin); } /// 描画範囲の分割数 TODO 端末ごとの差異をどう吸収するか? inline fn areaNum(self: ChartView) i32 { return @intFromFloat(self._rect_chart.right() / self._bar_width_); } /// この範囲のOHLCインデックスを描画する inline fn setWindowRange(self: *ChartView) void{ // 原点の位置からチャートの右端までの距離を本数で表す self._right_bar_index = @as(i32, @intFromFloat(((self.originX() - self._rect_chart.right()) / self._bar_width_))); self._left_bar_index = self._right_bar_index - @as(i32, @intFromFloat((right_margin / self._bar_width_))) + self.areaNum(); // forループ用だから+1 // self._range = range(self._right_bar_index, self._left_bar_index + 1); // println("${self.originX()} ${self._left_bar_index} - ${self._right_bar_index}") self.calcVScale(); // ここだとtempbarが新しくなったときに検出できない } //---------------------------------------------- // TODO draw()内で毎回setVScale()をやるのは無駄なので fn needRescale(self: ChartView) bool { _=self; // highest-lowest が変わっていない // 表示範囲のbarが変わっていない // bidaskが変わっていない return true; } // TODO Ask fn calcVScale1(self: *ChartView) void{ const sr = self._series_ orelse return; self._area_price_highest = std.math.f64_min; // TODO bidask self._area_price_lowest = std.math.f64_max; var i: i32 = self._right_bar_index; while (i <= self._left_bar_index) : (i += 1) { // std.log.debug("{}\n", .{i}); // if (i == self._left_bar_index + 1) break; if (i < 0 or i >= sr.size()) continue; const b = sr.get(@as(usize, @intCast(i))) catch continue; if (b.isZero()) continue; if (b.high > self._area_price_highest) self._area_price_highest = b.high; if (b.low < self._area_price_lowest) self._area_price_lowest = b.low; } // std.log.debug("{d:.5}, {d:.5}\n", .{self._area_price_highest, self._area_price_lowest}); //直近表示している場合だけ if (self.isRight()) { if (sr.bidask) |ba| { if (!ba.isNull()) { // TODO Askはをのバーが終わるまでは最高値で固定にしたい if (ba.ask_ > self._area_price_highest) self._area_price_highest = ba.ask_; if (ba.bid_ < self._area_price_lowest) self._area_price_lowest = ba.bid_; } } } self._area_price_center = (self._area_price_lowest + self._area_price_highest) / 2; const height = self._rect_chart.height() - (v_margin * 2); self._v_scale = @floatCast(height / (self._area_price_highest - self._area_price_lowest)); // std.log.debug("ChartView.height()={d:.5}, ._v_scale={d:.5}\n", .{height, self._v_scale}); // 指定スケール有り if (self._fixed_v_scale != 0) { self._v_scale = self._fixed_v_scale; return; } // // TODO overlaysのスケールも加味して決める // if (self._use_max_v_scale) { // self._v_scale = self.obj_overlays.getMaxVScale(); // return; // } // メインのSeriesのVScaleをoverlayのscaleにセットする for (self.obj_overlays.list.items) |*ol| { ol.setVScale(self._right_bar_index, self._left_bar_index, height, self._area_price_highest, self._area_price_lowest); } } fn calcVScale(self: *ChartView) void{ // 指定スケール有り if (self._fixed_v_scale != 0) { self._v_scale = self._fixed_v_scale; return; } const sr = self._series_ orelse return; self._area_price_highest = std.math.floatMin(f64); // TODO bidask self._area_price_lowest = std.math.floatMax(f64); var i: i32 = self._right_bar_index; while (i <= self._left_bar_index) : (i += 1) { // std.log.debug("{}\n", .{i}); // if (i == self._left_bar_index + 1) break; if (i < 0 or i >= sr.size()) continue; const b = sr.get(@as(usize, @intCast(i))) catch continue; if (b.isZero()) continue; if (b.high > self._area_price_highest) self._area_price_highest = b.high; if (b.low < self._area_price_lowest) self._area_price_lowest = b.low; } // std.log.debug("{d:.5}, {d:.5}\n", .{self._area_price_highest, self._area_price_lowest}); //直近表示している場合だけ if (self.isRight()) { if (sr.bidask) |ba| { if (!ba.isNull()) { // TODO Askはをのバーが終わるまでは最高値で固定にしたい if (ba.ask_ > self._area_price_highest) self._area_price_highest = ba.ask_; if (ba.bid_ < self._area_price_lowest) self._area_price_lowest = ba.bid_; } } } self._area_price_center = (self._area_price_lowest + self._area_price_highest) / 2; const height = self._rect_chart.height() - (v_margin * 2); self._v_scale = @floatCast(height / (self._area_price_highest - self._area_price_lowest)); // std.log.debug("ChartView.height()={d:.5}, ._v_scale={d:.5}\n", .{height, self._v_scale}); // TODO overlaysのスケールも加味して決める if (self._use_max_v_scale) { self.obj_overlays.calcVScales(self._right_bar_index, self._left_bar_index, height); const max = self.obj_overlays.getMaxVScale(); if (max > self._v_scale) self._v_scale = max; return; } // overlayのscaleに、メインのSeriesのVScaleをセットする for (self.obj_overlays.list.items) |*ol| { ol.setVScale(self._right_bar_index, self._left_bar_index, height, self._area_price_highest, self._area_price_lowest); } } //---------------------------------------------- /// _series中本目 FIXME 週足だとバグ pub inline fn barN(self: ChartView, dt: bar.MTime) !usize { return self._series_.?.binarySearch(dt); } /// _rect_chart内 pub inline fn getX(self: ChartView, bar_index: i32) f32 { return self.originX() - (@as(f32, @floatFromInt(bar_index)) * self._bar_width_) - self._bar_width_; } /// _rect_chart内 /// スケール可変のため、真ん中を基準にするよう pub inline fn getY(self: ChartView, price: f64) f32 { const vm = self._rect_chart.verticalMid(); const pac: f32 = @floatCast((self._area_price_center - price) * self._v_scale); return if (!self._reverse) vm + pac else vm - pac; } // draw ----------------------------------------- /// メイン描画 pub fn draw(impl: *anyopaque) void { var self: *ChartView = @alignCast(@ptrCast(impl)); self.base_.drawBackground(Color.rgb(5, 5, 5)); if (self._series_ == null or self._series_.?.size() < 1) { self.drawNone(); } else { // std.log.debug("{}\n", .{self.draw_test_num}); // self.draw_test_num += 1; // on_new_bar で検出する術がないので、ここに置いてる if (self.needRescale()) self.calcVScale(); // std.log.debug("base.rect_: {}\n", .{self.base_.rect_}); Window.clipOn(self.base_.rect_); self.drawDateGrid(); self.drawRateGrid(); // std.log.debug("_rect_chart: {}\n", .{self._rect_chart}); Window.clipOn(self._rect_chart); // self.drawSubOrigin() if (self.is_show_objects) { // self.draw_obj_zigzag() self.obj_trend_channels.draw(self); self.obj_horizontal_lines.draw(self); // self.draw_obj_v_line() } self.obj_trend_channels.drawTemp(self); self.obj_horizontal_lines.drawTemp(self); // self.draw_obj_temp_v_line() // self.draw_obj_temp_zigzag() self._temp_crosshair.draw(self) catch |err| { std.log.err("{}", .{err}); }; self.drawBidAsk(); self.drawOhlc(); if (self.is_show_overlay) self.obj_overlays.draw(self.*); // if self.is_show_memos self.draw_memo() self.obj_account.drawOrders(self); if (self.is_show_trades) self.obj_account.drawTrades(self); self.drawTitle(); Window.clipOff(); } self.drawFrame(); } // cut chart rect // fn clip_on_chart(self: ChartView) void{ // s := self.get_context().scale // sgl.scissor_rect(int(self._x_ * s), int(self._y_ * s), int(self._rect_chart.right()() * s), int(self._rect_chart.bottom()() * s), true) // } fn drawFrame(self: ChartView) void{ // const w = self.base_.window_; const r = self._rect_rate; const d = self._rect_date; const c = Color.rgb(128, 128, 128); // global にして統一する Window.drawLine(r.left(), r.top(), r.left(), d.top(), c); // TODO remove gx. Window.drawLine(d.left(), d.top(), d.right(), d.top(), c); } fn drawTitle(self: ChartView) void{ const sr = self._series_ orelse return; // const v = self.base_; var buf: [10]u8 = undefined; const s = bufPrint(&buf, "{s} {}", .{sr.symbol_, sr.period_}) catch return; Window.drawText(0, 0, s, self._style.fntRate); } fn drawNone(self: ChartView) void{ const r = self.base_.rect_; // std.log.debug("{d:.0}, {d:.0}\n", .{r.horizontalMid(), r.verticalMid()}); Window.drawText(r.horizontalMid(), r.verticalMid(), "No Data", .{ .h_align = .center, .v_align = .center, .size = 25, .color = Color.rgb(220, 220, 220) }); } //---- /// 細線 inline fn ld(self: ChartView, x: f32) void{ // const v = self.base_; Window.drawLine(x, self._rect_chart.top(), x, self._rect_date.top(), self._style.color_grid_date_major); } /// 太線 inline fn ll(self: ChartView, x: f32) void{ // const v = self.base_; Window.drawLine(x, self._rect_chart.top(), x, self._rect_date.top(), self._style.color_grid_date_minor); } inline fn tx(self: ChartView, x: f32, comptime format: []const u8, args: anytype) void{ // const v = self.base_; var buf: [10]u8 = undefined; const s = bufPrint(&buf, format, args) catch return; Window.drawText(x, self._rect_date.top(), s, self._style.fntDate); } inline fn in(comptime T: type, val: T, src: []const T) bool{ for (src) |s| if (s == val) return true; return false; } /// 日付軸 fn drawDateGrid(self: ChartView) void{ const sr = self._series_ orelse return; var i: i32 = self._right_bar_index; while (i <= self._left_bar_index) : (i += 1) { if (i < 0 or i >= sr.size() - 1) continue; // FIXME integer overflow const oi = sr.get(@as(usize, @intCast(i))) catch continue; const oii = sr.get(@as(usize, @intCast(i + 1))) catch continue; const dt = oi.dt_; const dt1 = oii.dt_; const x = self.getX(i); switch (sr.period_) { .m1 => { // 15分毎に細線 // if (dt.market_.minute_ in [15, 30, 45]) { ld(c, x); } if (in(u8, dt.market_.minute_, &[_]u8{15, 30, 45})) { self.ld(x); } if (dt.market_.hour_ != dt1.market_.hour_) { self.ll(x); self.tx(x, "{}", .{dt.local_.hour_}); } }, .m5 => { // 1時間ごとに太線、30分毎に細線 if (dt.market_.minute_ == 0) { self.ll(x); if (dt.market_.hour_ != 0) { self.tx(x, "{}", .{dt.local_.hour_}); } } else if (dt.market_.minute_ == 30) { self.ld(x); } if (dt.market_.day_ != dt1.market_.day_) { self.ll(x); self.tx(x, "{}{s}", .{dt.local_.day_, dt.local_.week_.estr()}); } }, .m15 => { // 2時間おきに細線 4時間おきに太線 if (dt.market_.minute_ == 0) { if (dt.market_.hour_ % 4 == 0) { self.ll(x); if (dt.market_.hour_ == 0) { } else { self.tx(x, "{}", .{dt.local_.hour_}); } } else { self.ld(x); } } // 株のため if (dt.market_.day_ != dt1.market_.day_) { self.ll(x); self.tx(x, "{}{s}", .{dt.local_.day_, dt.local_.week_.estr()}); } }, .m30 => { // 4時間おきに細線 12時間おきに太線 if (dt.market_.minute_ == 0) { if (dt.market_.hour_ == 0) { self.ll(x); // self.tx(x, "$dt.local_.day_$dt.local_.week_.estr()") } else if (dt.market_.hour_ % 4 == 0) { self.ld(x); if (dt.market_.hour_ % 8 == 0) { self.tx(x, "{}", .{dt.local_.hour_}); } } } // 株のため if (dt.market_.day_ != dt1.market_.day_) { self.ll(x); self.tx(x, "{}{s}", .{dt.local_.day_, dt.local_.week_.estr()}); } }, .h1 => { // 1日おきに太線 8時間おきに細線 4時間ごとに線、8時間毎に文字 if (dt.market_.minute_ == 0) { if (dt.market_.hour_ == 0) { self.ll(x); // self.tx(x, "$dt.local_.day_$dt.local_.week_.estr()") } else if (dt.market_.hour_ % 8 == 0) { self.ld(x); } } // 株のため if (dt.market_.day_ != dt1.market_.day_) { self.ll(x); self.tx(x, "{}{s}", .{dt.local_.day_, dt.local_.week_.estr()}); // TODO 現物株の場合はweekを省きたい } }, .h4 => { // 月曜に太線 日毎に細線 if (dt.market_.day_ != dt1.market_.day_) { if (dt.market_.nweek_ != dt1.market_.nweek_) { self.ll(x); self.tx(x, "{}/{}", .{dt.local_.month_, dt.local_.day_}); } else { self.ld(x); } } }, .d => { // 月ごとに太線、週ごとに細線 if (dt.market_.month_ != dt1.market_.month_) { self.ll(x); // if (dt.market_.month_ in [1, 7]) { if (in(u8, dt.market_.month_, &[_]u8{1, 7})) { self.tx(x, "{}/{}/{}", .{dt.market_.year_, dt.market_.month_, dt.local_.day_}); } else { self.tx(x, "{}/{}", .{dt.market_.month_, dt.local_.day_}); } } else if (dt.market_.nweek_ != dt1.market_.nweek_) { self.ld(x); // TODO 15日前後だけを書きたい } }, .w => { // 四半期ごとに太線、月ごとに細線 if (dt.market_.month_ != dt1.market_.month_) { self.ld(x); // if (dt.market_.month_ in [1, 4, 7, 10]) { if (in(u8, dt.market_.month_, &[_]u8{1, 4, 7, 10})) { self.ll(x); if (dt.market_.month_ == 1) { self.tx(x, "{}", .{dt.market_.year_}); } else { self.tx(x, "{}", .{dt.market_.month_}); } } } }, .m => { // 1月に太線、7月に細線 if (dt.market_.month_ == 1) { self.ll(x); self.tx(x, "{}", .{dt.market_.year_}); } else if (dt.market_.month_ == 7) { self.ld(x); } }, .q => { // 5年に太線、1年に細線 if (dt.market_.month_ == 1) { if (@mod(dt.market_.year_, 5) == 0) { self.ll(x); self.tx(x, "{}", .{dt.market_.year_}); } else { self.ld(x); } } }, .y => { // 10年ごとに太線、5年毎に細線 if (@mod(dt.market_.year_, 5) == 0) { if (@mod(dt.market_.year_, 10) == 0) { self.ll(x); self.tx(x, "{}", .{dt.market_.year_}); } else { self.ld(x); } } } } } } const cfg_date_sub = gui.TextStyle{ .size = 13, .color = Color.rgb(255, 255, 255), .h_align = .center, .vl_align = .bottom, }; //---- /// 基準線 fn drawSubOrigin(self: ChartView) void{ // const v = self.base_; if (self._origin_sub_x <= 0) return; // セットされていない場合 if (self._origin_sub_out) return; // seriesの範囲外にある場合 const x = self._origin_sub_x; Window.drawLine(x , 0, x, self._rect_chart.bottom(), Color.rgba(0, 0, 255, 100)); // TODO remove gx. // 中央揃え const s = self._origin_sub_time.?.str_iso8601_local_short(); Window.drawText(x, self._rect_chart.bottom(), s, cfg_date_sub); } //---- fn match(symb: []const u8, group: []const []const u8) bool { for (group) |item| if (eql(u8, symb, item)) return true; return false; } /// symbolセット時に一度だけ実行させること TODO 未完 fn setRatePitch(self: *ChartView) void { var major: f64 = 0.0; var minor: f64 = 0.0; if (self._series_) |sr| { if (match(sr.symbol_, &[_][]const u8{"GBPUSD", "EURUSD", "AUDUSD", "USDCHF", "NZDUSD", "USDCAD"})) { switch (sr.period_) { .m1 => { major = 0.0005; minor = 0.001; }, .m5, .m15 => { major = 0.001; minor = 0.005; }, .m30, .h1 => { major = 0.002; minor = 0.01; }, .h4 => { major = 0.005; minor = 0.01; }, .d => { major = 0.01; minor = 0.05; }, .w => { major = 0.02; minor = 0.1; }, .m => { major = 0.05; minor = 0.1; }, else => { major = 0.05; minor = 0.1; } } self._rate_digits = 4; } else if (match(sr.symbol_, &[_][]const u8{"USDJPY", "EURJPY", "GBPJPY", "AUDJPY", "NZDJPY"})) { switch (sr.period_) { .m1 => { major = 0.05; minor = 0.1; }, .m5, .m15 => { major = 0.1; minor = 0.5; }, .m30, .h1 => { major = 0.2; minor = 1.0; }, .h4 => { major = 0.5; minor = 1.0; }, .d => { major = 1.0; minor = 5.0; }, .w => { major = 2.0; minor = 10.0; }, .m => { major = 5.0; minor = 10.0; }, else => { major = 5.0; minor = 10.0; }, } self._rate_digits = 2; } else if (match(sr.symbol_, &[_][]const u8{"CL", "XAUUSD"})) { switch (sr.period_) { .m1 => { major = 5; minor = 10; }, .m5, .m15 => { major = 10; minor = 50; }, .m30, .h1 => { major = 20; minor = 100; }, .h4 => { major = 50; minor = 100; }, .d => { major = 100; minor = 500; }, .w => { major = 200; minor = 1000; }, .m => { major = 500; minor = 1000; }, else => { major = 5000; minor = 10000; }, } self._rate_digits = 0; } else if (match(sr.symbol_, &[_][]const u8{"DOW", "NSDQ"})) { switch (sr.period_) { .m1 => { major = 50; minor = 100; }, .m5, .m15 => { major = 100; minor = 500; }, .m30, .h1 => { major = 200; minor = 1000; }, .h4 => { major = 500; minor = 1000; }, .d => { major = 1000; minor = 5000; }, .w => { major = 2000; minor = 10000; }, .m => { major = 5000; minor = 10000; }, else => { major = 50000; minor = 100000; }, } self._rate_digits = 0; } } self._pitchMajor = major; self._pitchMinor = minor; } /// 0.0021 -> 3桁 /// 1.0021 -> 1桁 /// 1300.0 -> -3桁 TODO バグあり fn rateDigit(d: f64) i8{ if (d < 1) { var n = d; var count: i8 = 0; while (n < 1):(count +=1){ n *= 10; // std.log.debug("{d:.5}\n",.{n}); } return count; } else { var n = d; var count: i8 = 0; // 10 で剰余してあまりあれば行き過ぎた while (@mod(n, 10) == 0):(count +=1){ n /= 10; } // FIX 1.3とか12001とかが処理できないバグ return -count - 1; } // fn rate_digit(d f64) int{ // /* // 0.0021 -> 3桁 // 1.0021 -> 1桁 // 1300.0 -> -3桁 // */ // s := '$d' // if s[0] == `0` { // for i in 2 .. s.len { // .以降 // if s[i] == `0` { // continue // } // return i - 1 // } // } else { // mut dot := s.index('.') or { -1 } // if dot == -1 { // 整数 // for { // if s[s.len + dot] != `0` { // return dot // } // dot-- // } // return dot // } else { // // TODO ここ未完成 // for i := dot - 1; i > 0; i-- { // if s[i] == `0` { // i-- // continue // } // return -i // } // } // } // return 0 // } } /// value を digit 桁で丸める fn round2(value: f64, digit: i32) f64 { const p = std.math.pow(f64, 10.0, @as(f64, @floatFromInt(digit))); return std.math.round(value * p) / p; } // TODO 統合する fn drawRateGrid(self: ChartView) void{ _ = self._series_ orelse return; const highest = self._area_price_highest; const lowest = self._area_price_lowest; const keta = rateDigit(self._pitchMajor); const pw = std.math.pow(f64, 10.0, @as(f64, @floatFromInt(keta))); var level = round2(lowest, keta - 1); level -= self._pitchMajor * 5; // TODO 文字高より狭い場合は文字を間引く const pitch_height = self._pitchMajor * self._v_scale; const ommit = pitch_height < 13; //self._style.fntRate.size; // println("${pitch}, ${self._v_scale}, ${self._style.fntRate.size}, $pitch_height, $ommit") while (level <= highest) { level += self._pitchMajor; if (level < lowest) continue; const y = self.getY(level); // TODO ここで範囲外に出ちゃうことを気にしなくて良いようにしたい if (y > self._rect_chart.top() and y < self._rect_chart.bottom()) { const lv = std.math.round(level * pw); const ph = std.math.round(self._pitchMinor * pw); const mj = @mod(lv, ph); const color = if (mj == 0.0) // 細線 self._style.color_grid_rate_minor else self._style.color_grid_rate_major; Window.drawLine(self._rect_chart.left(), y, self._rect_chart.right(), y, color); // 間隔が狭すぎるときは間引く if (mj != 0.0 and ommit) continue; var buf: [10:0]u8 = undefined; const s = bufPrint(&buf, "{[0]d:.[1]}", .{level, self._rate_digits}) catch continue; // TODO etc. "{d: .2}" // TODO 細線は色を変えるかサイズを小さくする Window.drawText(self._rect_chart.right(), y, s, self._style.fntRate); } } // 最高値安値 const rr = self._rect_rate; const tr = self._style.fntRate; var buf: [10:0]u8 = undefined; var s = bufPrint(&buf, "{[0]d:.[1]}", .{self._area_price_highest, self._rate_digits}) catch return; Window.drawText(rr.left(), rr.top() + tr.size / 2, s, tr); s = bufPrint(&buf, "{[0]d:.[1]}", .{self._area_price_lowest, self._rate_digits}) catch return; Window.drawText(rr.left(), rr.bottom() - tr.size / 2, s, tr); } /// Deprecated? fn ratePitch(h: f64, l: f64, num: i32) f64{ const dif = h - l; const v = dif / @as(f64, @floatFromInt(num)); const d = rateDigit(v); // TODO const p = round2(v, d); // 5か2か1にする // numも絡めて調整、したい.. const b = 5 * std.math.pow(f64, 10.0, @as(f64, @floatFromInt(-d))); // TODO var pp = b * std.math.round(p / b); // TODO if (pp == 0.0) pp = p; if (pp < 0.0005) pp = 0.0005; // TODO gbpusdのみ return pp; } /// Deprecated 値軸 fn _drawRateGrid(self: ChartView) void{ const sr = self._series_ orelse return; // if (self._reverse) // Window.drawText(self._rect_chart.horizontalMid(), self._rect_chart.verticalMid(), "Reverse", cfg_rev); if (eql(u8, sr.symbol_, "GBPUSD")) { self.drawRateGridFixed(); return; } const n = 7; const highest = self._area_price_highest; const lowest = self._area_price_lowest; const pitch = ratePitch(highest, lowest, n); // BUG LEAK if (pitch == 0.0) return; const keta = rateDigit(pitch); var level = round2(lowest, keta - 1); level -= pitch * n; // c1 := $if linux {color_date_grid_1l} $else {color_date_grid_1a} while (level <= highest) { // level = util.round(f64(level + pitch), keta) level += pitch; if (level < lowest) continue; const y = self.getY(level); // TODO ここで範囲外に出ちゃうことを気にしなくて良いようにしたい if (y > 0 and y < self._rect_chart.bottom()) { // Window.drawLine(self.0, y, self._rect_chart.right(), y, self._style.color_grid_date_major); Window.drawLine(self._rect_chart.left(), y, self._rect_chart.right(), y, self._style.color_grid_date_major); // const s = "${level:.4f}"; // sy := y // - self._style.fntRate.size / 2 // Window.drawText(self._rect_chart.right(), int(y), s, self._style.fntRate); // 間隔が狭すぎるときは間引く // if (mj != 0.0 and ommit) continue; var buf: [10:0]u8 = undefined; const s = bufPrint(&buf, "{d:.4}", .{level}) catch continue; // TODO 細線は色を変えるかサイズを小さくする Window.drawText(self._rect_chart.right(), y, s, self._style.fntRate); } } // const rh = ((self._area_price_highest / self._area_price_lowest) - 1) * 100; // // const rl = (1 - (self._area_price_lowest / self._area_price_highest)) * 100; // var s = "${rh:.1}-${rl:.1}%"; // if (rh >= 10.0) s = "${rh:.0}-${rl:.0}%"; // // const y = self._rect_chart.bottom() + self._style.fntRate.size / 2; // // Window.drawText(self._rect_chart.right(), y, s, self._style.fntRate); } /// Deprecated GBPUSDだけ特別 fn drawRateGridFixed(self: ChartView) void{ const sr = self._series_ orelse return; // const v = self.base_; const highest = self._area_price_highest; const lowest = self._area_price_lowest; var pitchS: f64 = 0; var pitchL: f64 = 0; switch (sr.period_) { .m1 => { pitchS = 0.0005; pitchL = 0.001; }, .m5, .m15 => { pitchS = 0.001; pitchL = 0.005; }, .m30, .h1 => { pitchS = 0.002; pitchL = 0.01; }, .h4 => { pitchS = 0.005; pitchL = 0.01; }, .d => { pitchS = 0.01; pitchL = 0.05; }, .w => { pitchS = 0.02; pitchL = 0.1; }, .m => { pitchS = 0.05; pitchL = 0.1; }, else => { pitchS = 0.05; pitchL = 0.1; } } // std.log.debug("{}, {}\n", .{pitchS, pitchL}); const keta = rateDigit(pitchS); const pw = std.math.pow(f64, 10.0, @as(f64, @floatFromInt(keta))); var level = round2(lowest, keta - 1); level -= pitchS * 5; // TODO 文字高より狭い場合は文字を間引く const pitch_height = pitchS * self._v_scale; const ommit = pitch_height < 13; //self._style.fntRate.size; // println("${pitch}, ${self._v_scale}, ${self._style.fntRate.size}, $pitch_height, $ommit") while (level <= highest) { level += pitchS; if (level < lowest) continue; const y = self.getY(level); // TODO ここで範囲外に出ちゃうことを気にしなくて良いようにしたい if (y > self._rect_chart.top() and y < self._rect_chart.bottom()) { const lv = std.math.round(level * pw); const ph = std.math.round(pitchL * pw); const mj = @mod(lv, ph); const color = if (mj == 0.0) // 細線 self._style.color_grid_rate_minor else self._style.color_grid_rate_major; Window.drawLine(self._rect_chart.left(), y, self._rect_chart.right(), y, color); // 間隔が狭すぎるときは間引く if (mj != 0.0 and ommit) continue; var buf: [10:0]u8 = undefined; const s = bufPrint(&buf, "{d:.4}", .{level}) catch continue; // TODO 細線は色を変えるかサイズを小さくする Window.drawText(self._rect_chart.right(), y, s, self._style.fntRate); } } // 最高値安値 const rr = self._rect_rate; const tr = self._style.fntRate; var buf: [10:0]u8 = undefined; var s = bufPrint(&buf, "{d:.4}", .{self._area_price_highest}) catch return; Window.drawText(rr.left(), rr.top() + tr.size / 2, s, tr); s = bufPrint(&buf, "{d:.4}", .{self._area_price_lowest}) catch return; Window.drawText(rr.left(), rr.bottom() - tr.size / 2, s, tr); } //---- fn drawBidAsk(self: ChartView) void{ const sr = self._series_ orelse return; // const v = self.base_; const ba = sr.bidask orelse return; const ya = self.getY(ba.ask_); const yb = self.getY(ba.bid_); // const rc = self._rect_chart; if (ya >= 0 and ya <= self._rect_chart.bottom()) { Window.drawLine(0, ya, self._rect_chart.right(), ya, Color.rgb(128, 128, 128)); // TODO remove gx. // var buf: []u8 = undefined; // const s = bufPrint(buf, "{d:.5}", .{ba.ask}); // Window.drawText(0, ya - self._style.fntRate.size, s, self._style.fntRate); } if (yb >= 0 and yb <= self._rect_chart.bottom()) { Window.drawLine(0, yb, self._rect_chart.right(), yb, Color.rgb(128, 128, 128)); // var buf: []u8 = undefined; // const s = bufPrint(buf, "{d:.5}", .{ba.bid}); // Window.drawText(0, yb + 1, s, self._style.fntRate); } } fn drawOhlc(self: ChartView) void{ const sr = self._series_ orelse return; // const v = self.base_; const w: f32 = 2; // Barの幅の半分、か? var i = self._right_bar_index; while (i <= self._left_bar_index) : (i += 1) { if (i < 0 or i >= sr.size()) continue; const o = sr.get(@as(usize, @intCast(i))) catch continue; if (o.isZero()) continue; const x = self.getX(i); const yh = self.getY(o.high); const yl = self.getY(o.low); const yo = self.getY(o.open); const yc = self.getY(o.close); // std.log.debug("yc={d:.0}\n", .{yc}); Window.drawLine(x, yh, x, yl, self._style.bar_color); if (o.close > o.open) Window.drawRect(x - 1, yo, w, yc - yo, self._style.bar_color_body_up, true) else if (o.close < o.open) Window.drawRect(x - 1, yo, w, yc - yo, self._style.bar_color_body_down, true) else Window.drawLine(x - 1, yc, x + w, yc, self._style.bar_color); Window.drawRect(x - 1, yo, w, yc - yo, self._style.bar_color, false); //終値 // $if !android { // v.draw_circle_filled(x, yc, 1.2, gx.white) // TODO remove gx. // } } } //------------------------------------------- pub fn toggleShowObjects(self: *ChartView) void{ self.is_show_objects = !self.is_show_objects; // if (self._is_show_objects){ // self.obj_horizontal_lines.read(); // } else { // } } pub inline fn getSeries(self: ChartView) ?*bar.OHLCSeries { return self._series_; } pub inline fn symbol(self: ChartView) ?[]const u8 { return self._series_.?.symbol_; } pub inline fn period(self: ChartView) ?bar.Period { return if (self._series_) |s| s.period_ else null; } pub inline fn setStyle(self: *ChartView, style: Style) void{ self._style = style; } pub inline fn getVScale(self: ChartView) f32 { return self._v_scale; } pub inline fn getAreaHighest(self: ChartView) f64 { return self._area_price_highest; } pub inline fn getAreaLowest(self: ChartView) f64 { return self._area_price_lowest; } /// `value`=0で解除 pub fn setFixedVScale(self: *ChartView, value: f64) void{ self._fixed_v_scale = f32(value); self.calcVScale(); } /// ObjOverlaysのMaxVScaleを加味する pub fn useMaxVScale(self: *ChartView, on: bool) void { self._use_max_v_scale = on; self.calcVScale(); } pub fn getOriginSubTime(self: ChartView) ?bar.MTime { return self._origin_sub_time; } //---- pub fn numAt(self: ChartView, x: f32) i32{ const val = -(x - self.originX() + self._bar_width_ / 2) / self._bar_width_; return @intFromFloat(val); } pub fn barAt(self: ChartView, x: f32) ?*bar.OHLC{ const sr = self._series_.?; const i = self.numAt(x); return if (i < 0 or i >= sr.size()) null else sr.get(@as(usize, @intCast(i))) catch null; } /// y はEvenの値だからWindwoに対する絶対位置 pub fn priceAt(self: ChartView, y: f64) ?f64 { const _y = ((y - self._rect_chart.verticalMid()) / self._v_scale); return if (!self._reverse) self._area_price_center - _y else self._area_price_center + _y; } pub fn xAt(self: ChartView, date: bar.MTime) !f32{ const n = try self.barN(date); return self.getX(n); } /// TODO pub fn yAt(self: ChartView, price: f64) ?f32{ _ = self; _ = price; return 0.0; } // // overlayも含めて // fn getBar(self: ChartView, symbol: []const u8, n: i32) !bar.OHLC{ // if (eql(u8, symbol, self.symbol())) return self._series_.get(n); // for (self.obj_overlays) |o| { // if (eql(u8, symbol, self.symbol())) { // return o.overlay.get(n); // } // } // return error.NotFound; //("no bar found.") // } // // overlayも含めて // fn get_y(self: ChartView, symbol: []const u8, price: f64) f32{ // if (eql(u8, symbol, self.symbol())) return self.getY(price); // for (self.obj_overlays) |o| { // if (eql(u8, symbol, self.symbol())) { // return o.y(price, f32(self._rect_chart.verticalMid())); // } // } // return 0.0; // } // // overlayも含めて // fn getPriceAt(self: ChartView, symbol: []const u8, y: f64) ?f64{ // if (eql(u8, symbol, self.symbol())) return self.priceAt(y); // for (self.obj_overlays) |o| { // if (eql(u8, o.symbol(), symbol)) { // // TODO reverse // return o.price_area_center - ((y - self._rect_chart.verticalMid()) / o.v_scale); // } // } // return 0.0; // } //---- pub fn insideChartRect(self: ChartView, x: f32, y: f32) bool { return self._rect_chart.contains(x, y); } pub fn insidePriceRect(self: ChartView, x: f32, y: f32) bool { const r = self.base_.rect_; return (r._x_ + self._rect_chart.right() <= x and x <= r.right()) and (r._y_ <= y and y <= r._y_ + self._rect_chart.bottom()); } pub fn insideDateRect(self: ChartView, x: f32, y: f32) bool { const r = self.base_.rect_; return (r._x_ <= x and x <= r._x_ + self._rect_chart.right()) and (r._y_ + self._rect_chart.bottom() <= y and y <= r.bottom()); } //------------------------------------------- pub fn onEvent(impl: *anyopaque, e: gui.Event) bool { var self: *ChartView = @alignCast(@ptrCast(impl)); // _=self; switch (e.type){ .mouse_down, .touches_began => self.onMouseDown(e), .mouse_move, .touches_moved => self.onMouseMove(e), .mouse_up, .touches_ended => self.onMouseUp(e), .mouse_scroll => self.onScroll(e), else => {}, } return false; } fn onMouseDown(self: *ChartView, e: gui.Event) void { if (self.insideChartRect(e.x(), e.y())) { // std.log.debug("mouse_mode={}\n", .{self.mouse_mode}); switch (self.mouse_mode) { .default => { Mouse.down(e); Mouse.setXY(e); if (e.modifiers == key_mod_shift and e.mouse_button == .left) { // TODO 拡大?を割り当てたい } else if (e.modifiers == key_mod_ctrl and e.mouse_button == .left) { } else { // popup-menu if (e.mouse_button == .right) { std.log.debug("Right Click", .{}); // if (self.context_menu) |cm| { // _=cm; // var cm = menu.new(view: cw, x: int(e.x()), y: int(e.y()), // items: self.context_menu); // cm.show(); // } } } }, .cross_hair => {}, .channel_line => { Mouse.down(e); switch (self.obj_trend_channels.editor.mode) { .add => { self.obj_trend_channels.editor.place(e, self); }, .remove => { // $if linux { // // already hit_test and changed to selected color by mouse_move self.obj_trend_channels.editor.remove(e, self, 3); // } // $if android { // // TODO long tap -> msg_box -> confirm -> delete // self.channel_editor.remove(e, self, 10) // } // // n := $if android { 10 } $else { 3 } // // self.channel_editor.remove(e, mut self.Chart, n) }, .channel => { // select -> temp? // $if linux { self.obj_trend_channels.editor.placeChannel(e, self, 3); // } // $if android { // self.channel_editor.place_channel(e, self, 10) // } }, } }, .horizontal_line => { Mouse.down(e); switch (self.obj_horizontal_lines.editor.mode) { .add => self.obj_horizontal_lines.editor.place(e, self), .remove => { // $if linux { self.obj_horizontal_lines.editor.remove(e, self, 3); // } // $if android { // self.hline_editor.remove(e, self, 10) // } }, } }, .vertical_line => {}, .zigzag => { // // barを取得 // var c = cw; // const b = self.barAt(e.x()) or { return } // const p = self.priceAt(e.y()) or { return } // const n = util.near(p, b.high, b.low); // self.obj_temp_zigzag.add(b.period,b.dt,n); // // println(self.obj_temp_zigzag.last()) }, .memo => { // Mouse.down(e.x(), e.y()); // // hit_test to get an obj_memo // // ドラッグドロップで移動、サイズ変更 // self.memo_editor.down(e.x(), e.y(), &cw); }, } } else if (self.insidePriceRect(e.x(), e.y())) { // TODO 縦縮尺変更 // 上に引っ張れば下が広がる(最安値を下に設定する) switch (self.mouse_mode) { .default => { if (e.isDown()) { } }, else => {} } } } fn onMouseMove(self: *ChartView, e: gui.Event) void { switch (self.mouse_mode) { .default => { // ドラッグ if (Mouse.isDown()) { // チャートを横ドラッグでスクロール // if (self.insideChartRect(Mouse._down_x, Mouse._down_y)) { if (self.insideChartRect(e.x(), e.y())) { const delta_x = e.x() - Mouse.getPrevXY().x; // std.log.debug("onMouseMove! delta_x={}, {}\n", .{delta_x, self._rect_chart}); self.offsetOrigin(delta_x); Mouse.setXY(e); } // // TODO プライス軸を縦ドラッグで縮率調整 // else if (self.insidePriceRect(Mouse._down_x, Mouse._down_y)) { // const drag_y = e.y() - Mouse._prev_y; // // dump(drag_y) // // 上にドラッグはマイナス値=より狭範囲に可視化=縮率小さく // if (drag_y < 0) { // } // // 下にドラッグはプラス=より広範囲に可視化=縮率大きく // else if (drag_y > 0) { // } // } } }, .cross_hair => { if (self.insideChartRect(e.x(), e.y())) { if (self.barAt(e.x())) |b| { const p = self.priceAt(e.y()) orelse return; const n = self.numAt(e.x()); self._temp_crosshair = CrossHair{ .bar = b.*, .price = p, .num = n }; } } // TODO ドラッグとスクロール }, .channel_line => { switch (self.obj_trend_channels.editor.mode) { .add => { if (Mouse.isDown()) { self.obj_trend_channels.editor.place(e, self); } }, .remove => { // $if linux { // // TODO hit_test _> change to selected color // // self.chart.hit_test_trend_channel(int(e.x), int(e.y)) // } }, .channel => { // move parallel }, } }, .horizontal_line => { if (Mouse.isDown()) self.obj_horizontal_lines.editor.place(e, self); }, .vertical_line => {}, .zigzag => {}, .memo => { // // 移動、サイズ変更 // if (Mouse.isDown()) { // self.memo_editor.move(e.x(), e.y(), cw); // } }, } } /// pointInside() をしないことに注意 fn onMouseUp(self: *ChartView, e: gui.Event) void { _ = e; switch (self.mouse_mode) { .default => Mouse.up(), .cross_hair => {}, .channel_line => { // app := // mut cl := self.channel_editor.get_object(self.chart) // cl.to_detailed_time() // println(cl) Mouse.up(); // write // ch.write_trend_channel() }, .horizontal_line => Mouse.up(), .vertical_line => {}, .zigzag => {}, .memo => Mouse.up(), } } fn onScroll(self: *ChartView, e: gui.Event) void { if (self.insideChartRect(e.x(), e.y())) { const scroll_n = -e.scroll_y * 4 * 6; if (self.insideChartRect(e.x(), e.y())) self.offsetOrigin(scroll_n); } } const Point = struct { x: f32, y: f32, }; /// TODO ゼスチャー等 const Mouse = struct { // ドラッグ // 長押し // ダブルクリック // スワイプ // フリック const _double_click_time_: i64 = 300000000; // 0.3秒 var _down_x: f32 = 0; // down位置 var _down_y: f32 = 0; // var _is_down: bool = false; // drag用 var _down_time: i64 = 0; // double click 検出用 // var _up_time: i64 = 0; var _prev_x: f32 = 0; // drag scrooll var _prev_y: f32= 0; // // var _pressed_time: i64 = 0; // 押していた時間 // TODO var onDoubleClicked: ?*const fn () void = null; var onLongPressed: ?*const fn () void = null; /// 押したx,yを記録 fn down(e: gui.Event) void { _down_x = e.x(); _down_y = e.y(); _is_down = true; _down_time = std.time.milliTimestamp(); } /// 押された状態である fn isDown() bool { return _is_down; } /// dragすると _prev_x は Event.x() に置き換わる fn setXY(e: gui.Event) void { _prev_x = e.x(); _prev_y = e.y(); } fn up() void { _is_down = false; // if () { // if (onDoubleClicked) |on| on(); // } } fn getXY() Point { return .{.x = _down_x, .y = _down_y}; } fn getPrevXY() Point { return .{.x = _prev_x, .y = _prev_y}; } }; const key_mod_alt = 2; const key_mod_super = 256; const key_mod_shift = 257; const key_mod_ctrl = 258; // ctrl + shift = 259 // shift + alt = 261 // ctrl + alt = 262 // ctrl + alt + shift = 263 pub const MouseMode = enum{ default, cross_hair, channel_line, horizontal_line, vertical_line, zigzag, memo, }; pub fn getMouseMode(self: ChartView) MouseMode { return self.mouse_mode; } pub fn setMouseMode(self: *ChartView, mode: MouseMode) void { self.mouse_mode = mode; // これでtempを消せる self._temp_crosshair = .{}; self.obj_trend_channels.temp = null; self.obj_horizontal_lines.temp = null; }
Correct folding
No response
The text was updated successfully, but these errors were encountered:
No branches or pull requests
Zig Version
0.14.0-dev.2568+42dac40b3
ZLS Version
0.14.0-dev.322+e50d59e
Client / Code Editor / Extensions
VSCode
Version: 1.96.2
Commit: fabdb6a30b49f79a7aba0f2ad9df9b399473380f
Date: 2024-12-19T10:22:47.216Z
Electron: 32.2.6
ElectronBuildId: 10629634
Chromium: 128.0.6613.186
Node.js: 20.18.1
V8: 12.8.374.38-electron.0
OS: Linux x64 6.12.6-arch1-1
ziglang.vscode-zig 0.6.3
Steps to Reproduce and Observed Behavior
Expected Behavior
Correct folding
Relevant log output
No response
The text was updated successfully, but these errors were encountered: