Compare commits
30
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d1ed33c2ee | ||
|
|
8bc1455813 | ||
|
|
ecaa04ecf8 | ||
|
|
44e3070544 | ||
|
|
1815950835 | ||
|
|
8762fbf7ce | ||
|
|
0459171d6e | ||
|
|
fcdf72c5d9 | ||
|
|
f5caf0b7ba | ||
|
|
a0f2673b3a | ||
|
|
ba3439b049 | ||
|
|
ae4d7f719b | ||
|
|
64916f6bfc | ||
|
|
1a9dd1c37a | ||
|
|
d77925c568 | ||
|
|
f1f1b68ffb | ||
|
|
835f453f03 | ||
|
|
0eb9518485 | ||
|
|
b5c70fc34e | ||
|
|
a1dba1d7d0 | ||
|
|
efdea5256f | ||
|
|
5df1e5f383 | ||
|
|
99750da31d | ||
|
|
49247dba3f | ||
|
|
a25257cb33 | ||
|
|
b1c180d663 | ||
|
|
6afb16abcb | ||
|
|
995fa3df35 | ||
|
|
18eb28dfd2 | ||
|
|
99cdfadf8d |
No files matched your search
+2
-1
@@ -32,4 +32,5 @@ trace_data/
|
||||
/src/lib/orderman/perf_analysis/last_pretty_log.csv
|
||||
/src/lib/orderman/perf_analysis/log.csv
|
||||
/doc/user/public/*
|
||||
.ionide/*
|
||||
.ionide/*
|
||||
src/web/ui/.ionide/symbolCache.db
|
||||
@@ -1 +1 @@
|
||||
set(sources /home/ppinto/Dropbox/bts/spark/doc/user/build.sh;content/_index.md;content/bts_spark/Glossary/_index.md;content/bts_spark/Help/_index.md;content/bts_spark/Help/about_spark.png;content/bts_spark/Help/image39.png;content/bts_spark/Help/image58.png;content/bts_spark/Launcher/_index.md;content/bts_spark/Launcher/image38.png;content/bts_spark/Launcher/image68.png;content/bts_spark/Logging-In-to-the-System/_index.md;content/bts_spark/Logging-In-to-the-System/image3.png;content/bts_spark/Logging-In-to-the-System/image64.png;content/bts_spark/Market/_index.md;content/bts_spark/Market/change_securities.png;content/bts_spark/Market/image11.png;content/bts_spark/Market/image14.png;content/bts_spark/Market/image20.png;content/bts_spark/Market/image21.png;content/bts_spark/Market/image23.png;content/bts_spark/Market/image24.png;content/bts_spark/Market/image34.png;content/bts_spark/Market/image49.png;content/bts_spark/Market/image53.png;content/bts_spark/Market/image66.png;content/bts_spark/Market/image69.png;content/bts_spark/Market/market_view.png;content/bts_spark/Market/settings_icon.png;content/bts_spark/Market/time_and_sales.png;content/bts_spark/My-Windows/_index.md;content/bts_spark/My-Windows/image15.png;content/bts_spark/My-Windows/image16.png;content/bts_spark/My-Windows/image37.png;content/bts_spark/Settings/_index.md;content/bts_spark/Settings/image51.png;content/bts_spark/Settings/image9.png;content/bts_spark/Spreader/_index.md;content/bts_spark/Spreader/image10.png;content/bts_spark/Spreader/image13.png;content/bts_spark/Spreader/image18.png;content/bts_spark/Spreader/image19.png;content/bts_spark/Spreader/image28.png;content/bts_spark/Spreader/image29.png;content/bts_spark/Spreader/image31.png;content/bts_spark/Spreader/image35.png;content/bts_spark/Spreader/image40.png;content/bts_spark/Spreader/image43.png;content/bts_spark/Spreader/image54.png;content/bts_spark/Spreader/image55.png;content/bts_spark/Spreader/image56.png;content/bts_spark/Spreader/image6.png;content/bts_spark/Spreader/image7.png;content/bts_spark/Spreader/image70.png;content/bts_spark/Spreader/image72.png;content/bts_spark/Spreader/image8.png;content/bts_spark/Trading/OrderTypes/index.md;content/bts_spark/Trading/_index.md;content/bts_spark/Trading/algo_studio.png;content/bts_spark/Trading/collapse_all_icon.png;content/bts_spark/Trading/cover_algo_control.png;content/bts_spark/Trading/expand_all_icon.png;content/bts_spark/Trading/image1.png;content/bts_spark/Trading/image17.png;content/bts_spark/Trading/image2.png;content/bts_spark/Trading/image25.png;content/bts_spark/Trading/image27.png;content/bts_spark/Trading/image33.png;content/bts_spark/Trading/image42.png;content/bts_spark/Trading/image44.png;content/bts_spark/Trading/image47.png;content/bts_spark/Trading/image48.png;content/bts_spark/Trading/image5.png;content/bts_spark/Trading/image50.png;content/bts_spark/Trading/image52.png;content/bts_spark/Trading/image57.png;content/bts_spark/Trading/image59.png;content/bts_spark/Trading/image60.png;content/bts_spark/Trading/image61.png;content/bts_spark/Trading/image62.png;content/bts_spark/Trading/image73.png;content/bts_spark/Trading/image74.png;content/bts_spark/Trading/join_algo_control.png;content/bts_spark/Trading/ladder.png;content/bts_spark/Trading/ladder_badges.png;content/bts_spark/Trading/ladder_main_part.png;content/bts_spark/Trading/lean_algo_control.png;content/bts_spark/Trading/mini_orderbook.png;content/bts_spark/Trading/oco_algo_control.png;content/bts_spark/Trading/offset_algo_control.png;content/bts_spark/Trading/pnl_calculation.png;content/bts_spark/Trading/pnl_window.png;content/bts_spark/Trading/recreate_icon.png;content/bts_spark/Trading/settings_icon.png;content/bts_spark/Trading/spark_menu_order_book.png;content/bts_spark/Trading/top_algo_control.png;content/bts_spark/Trading/trigger_algo_control.png;content/bts_spark/_index.md;content/release_notes/_index.md;content/system_requirements/_index.md;static/book.js;static/favicon.ico;static/image.js;templates/index.html;templates/page.html;templates/section.html;templates/shortcodes/back_button.html)
|
||||
set(sources /home/ppinto/Dropbox/bts/spark/doc/user/build.sh;content/_index.md;content/bts_spark/Algos/_index.md;content/bts_spark/Glossary/index.md;content/bts_spark/Help/about_spark.png;content/bts_spark/Help/image39.png;content/bts_spark/Help/image58.png;content/bts_spark/Help/index.md;content/bts_spark/Launcher/image38.png;content/bts_spark/Launcher/image68.png;content/bts_spark/Launcher/index.md;content/bts_spark/Logging-In-to-the-System/image3.png;content/bts_spark/Logging-In-to-the-System/image64.png;content/bts_spark/Logging-In-to-the-System/index.md;content/bts_spark/Market/change_securities.png;content/bts_spark/Market/image11.png;content/bts_spark/Market/image14.png;content/bts_spark/Market/image20.png;content/bts_spark/Market/image21.png;content/bts_spark/Market/image23.png;content/bts_spark/Market/image24.png;content/bts_spark/Market/image34.png;content/bts_spark/Market/image49.png;content/bts_spark/Market/image53.png;content/bts_spark/Market/image66.png;content/bts_spark/Market/image69.png;content/bts_spark/Market/index.md;content/bts_spark/Market/market_view.png;content/bts_spark/Market/settings_icon.png;content/bts_spark/Market/time_and_sales.png;content/bts_spark/My-Windows/image15.png;content/bts_spark/My-Windows/image16.png;content/bts_spark/My-Windows/image37.png;content/bts_spark/My-Windows/index.md;content/bts_spark/Settings/image51.png;content/bts_spark/Settings/image9.png;content/bts_spark/Settings/index.md;content/bts_spark/Spreader/image10.png;content/bts_spark/Spreader/image13.png;content/bts_spark/Spreader/image18.png;content/bts_spark/Spreader/image19.png;content/bts_spark/Spreader/image28.png;content/bts_spark/Spreader/image29.png;content/bts_spark/Spreader/image31.png;content/bts_spark/Spreader/image35.png;content/bts_spark/Spreader/image40.png;content/bts_spark/Spreader/image43.png;content/bts_spark/Spreader/image54.png;content/bts_spark/Spreader/image55.png;content/bts_spark/Spreader/image56.png;content/bts_spark/Spreader/image6.png;content/bts_spark/Spreader/image7.png;content/bts_spark/Spreader/image70.png;content/bts_spark/Spreader/image72.png;content/bts_spark/Spreader/image8.png;content/bts_spark/Spreader/index.md;content/bts_spark/Trading/algo_studio.png;content/bts_spark/Trading/collapse_all_icon.png;content/bts_spark/Trading/cover_algo_control.png;content/bts_spark/Trading/expand_all_icon.png;content/bts_spark/Trading/image1.png;content/bts_spark/Trading/image17.png;content/bts_spark/Trading/image2.png;content/bts_spark/Trading/image25.png;content/bts_spark/Trading/image27.png;content/bts_spark/Trading/image33.png;content/bts_spark/Trading/image42.png;content/bts_spark/Trading/image44.png;content/bts_spark/Trading/image47.png;content/bts_spark/Trading/image48.png;content/bts_spark/Trading/image5.png;content/bts_spark/Trading/image50.png;content/bts_spark/Trading/image52.png;content/bts_spark/Trading/image57.png;content/bts_spark/Trading/image59.png;content/bts_spark/Trading/image60.png;content/bts_spark/Trading/image61.png;content/bts_spark/Trading/image62.png;content/bts_spark/Trading/image73.png;content/bts_spark/Trading/image74.png;content/bts_spark/Trading/index.md;content/bts_spark/Trading/join_algo_control.png;content/bts_spark/Trading/ladder.png;content/bts_spark/Trading/ladder_badges.png;content/bts_spark/Trading/ladder_main_part.png;content/bts_spark/Trading/lean_algo_control.png;content/bts_spark/Trading/mini_orderbook.png;content/bts_spark/Trading/oco_algo_control.png;content/bts_spark/Trading/offset_algo_control.png;content/bts_spark/Trading/pnl_calculation.png;content/bts_spark/Trading/pnl_window.png;content/bts_spark/Trading/recreate_icon.png;content/bts_spark/Trading/settings_icon.png;content/bts_spark/Trading/spark_menu_order_book.png;content/bts_spark/Trading/top_algo_control.png;content/bts_spark/Trading/trigger_algo_control.png;content/bts_spark/_index.md;content/release_notes/_index.md;content/system_requirements/_index.md;static/book.js;static/favicon.ico;static/image.js;templates/index.html;templates/page.html;templates/section.html;templates/shortcodes/back_button.html)
|
||||
@@ -4,51 +4,6 @@ weight = 3
|
||||
sort_by = "weight"
|
||||
insert_anchor_links = "right"
|
||||
+++
|
||||
## 2.9.2
|
||||
### Bug Fixes
|
||||
- Fixed an issue with the exchange simulator that could lead to crossed markets
|
||||
|
||||
## 2.9.1
|
||||
### Bug Fixes
|
||||
- Fixed an issue with trade sound alerts, where in some cases the use of a "In Between" condition could lead to a UI failure.
|
||||
- Fixed an issue with the display of the new raw position column in the market view window.
|
||||
- Fixed the popup label that describes the raw position information in the ladder
|
||||
### Internal Changes
|
||||
- Avoided shipping production configuration defaults that lead to trying to download a position file for the development sftp server.
|
||||
### Api
|
||||
- C# API sample now uses synchronous calls.
|
||||
|
||||
## 2.9.0
|
||||
### User Visible Changes
|
||||
- Improved Workspace management User Interface
|
||||
- Added the ability to change grid background colors
|
||||
- The Spark UI Window group now has a title in the Windows task bar
|
||||
- Added sound alerts for trade events, see Launcher -> Settings -> Alerts
|
||||
- Allowed composite algos that are attached to existing orders to identify fills that were already present prior to attachment
|
||||
- Added import facilities for GMI position files
|
||||
- Added the ability for risk managers to enter manual fills
|
||||
- Added "raw" P&L columns, that is columns that *do not* include spread legs in their accounting
|
||||
### Bug Fixes
|
||||
- Fixed the settings action menu (Load/Save/Reset to Factory)
|
||||
- Fixed the method used to determine if a security is expired to take into account the start of day time
|
||||
### API Changes
|
||||
- Fixed an issue in the Client SDK that could lead to occasional failures to connect to the spark server as well as excessive memory usage.
|
||||
- Added the ability for algo orders to distinguish fills existing prior to launch
|
||||
### Internal Changes
|
||||
- Changed how we packaged certain third-party components
|
||||
- Added logging of entitlement status
|
||||
- Tagged outgoing orders with sequence number of marketdata or FIX event that triggered them
|
||||
## 2.8.3
|
||||
### Bug Fixes
|
||||
- Fixed a bug that could cause an OrderBook's Security column filter to be blank
|
||||
- Fixed a bug that could cause a UI failure when clearing a filter selection on a filtering widget
|
||||
## 2.8.2
|
||||
### Bug Fixes
|
||||
- Added proper display factors for SOM spreads
|
||||
- Fixed a bug that could cause a UI failure when trying to change settings in a synthetic ladder
|
||||
## 2.8.1
|
||||
### Bug fixes
|
||||
- Fixed an error in the UI grids filtering widget
|
||||
## 2.8.0
|
||||
### User Visible Changes
|
||||
- Added ProRata and Scratch algos
|
||||
@@ -61,7 +16,6 @@ insert_anchor_links = "right"
|
||||
selected by default
|
||||
- Increased width of login panel to better show login error messages
|
||||
- Added optional 'Maker/Taker' pricing to Spreader algo
|
||||
- Algo buttons in ladder can now be made to deactivate after launching an algo or remain 'sticky'
|
||||
### Internal changes
|
||||
- Made it easier to generate simple UI's for new order types
|
||||
### API Changes
|
||||
|
||||
+1
-1
Submodule docker/linux updated: b09d4c035b...e13bb5d8df.
+1
-1
Submodule externals updated: 6b8c745fb2...67a79263ba.
@@ -70,6 +70,7 @@ namespace mbts.ui.shell
|
||||
{
|
||||
Log.Gen.info("Application.OnExit");
|
||||
if (_runtime != null) _runtime.Dispose();
|
||||
utils.BTSNativeMethods.finit_spark_lib();
|
||||
base.OnExit(e);
|
||||
Environment.Exit(0);
|
||||
}
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
using bts.api;
|
||||
using bts.shell;
|
||||
using mbts;
|
||||
using mbts.persist;
|
||||
using mbts.spark;
|
||||
using mbts.ui;
|
||||
using mbts.ui.spark;
|
||||
using mbts.ui.utilities;
|
||||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Linq;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Reactive.Linq;
|
||||
using System.Windows;
|
||||
using Disposable = mbts.Disposable;
|
||||
|
||||
namespace bts {
|
||||
[Export(typeof(IShellDaemon))]
|
||||
class GlobalAlertsDaemon : IShellDaemon, IDisposable {
|
||||
const string
|
||||
// used for logging/diagnostic purposes
|
||||
_window_name = "Global Alerts Dialog",
|
||||
// actual window title
|
||||
_window_title = "Alerts";
|
||||
|
||||
readonly ThreadedWindowLauncher _launcher;
|
||||
readonly UserSettingsHub _hub;
|
||||
readonly IBus _bus;
|
||||
readonly INotificationService _notification_service;
|
||||
IDisposable _dispose;
|
||||
|
||||
[ImportingConstructor]
|
||||
public GlobalAlertsDaemon(IBus bus, UserSettingsHub hub, INotificationService notification_service) {
|
||||
_bus = bus;
|
||||
_hub = hub;
|
||||
_notification_service = notification_service;
|
||||
_launcher = new ThreadedWindowLauncher(_window_name);
|
||||
_dispose = Disposable.Create(
|
||||
bus.subscribe(SparkTags.ShowGlobalAlertsTag)
|
||||
.Subscribe(_ => _launcher.show(_view_factory)),
|
||||
_hub.subscribe_default_settings()
|
||||
.SubscribeEach(s => new CompositeDisposable(
|
||||
s.alerts.trade_alerts.Where(t => t.is_enabled).Select(
|
||||
a => _notification_service.link_to_trades(
|
||||
a.predicate.security_criteria, a.predicate.create(), a.sound)))));
|
||||
}
|
||||
|
||||
Window _view_factory() {
|
||||
var vm = new GlobalAlertsVM();
|
||||
vm.load_from(_hub.current.alerts);
|
||||
vm.request_commit += commit;
|
||||
var sec_sub = _bus.subscribe(SparkTags.SecuritiesTag).OnDispatcher().Subscribe(m => vm.security_map = m);
|
||||
var win = new GlobalAlertsDialog {
|
||||
Title = "Alerts",
|
||||
DataContext = vm
|
||||
};
|
||||
win.Closed += (s, e) => Disposable.dispose(ref sec_sub);
|
||||
return win;
|
||||
void commit() {
|
||||
var s = _hub.current.with(alerts: vm.create());
|
||||
_hub.save_default(s).ignore();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose() =>
|
||||
Disposable.dispose(ref _dispose);
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,6 @@ namespace mbts.ui {
|
||||
EditDocumentAltGeometryKey = nameof(EditDocumentAltGeometryKey),
|
||||
EditDocumentGeometryKey = nameof(EditDocumentGeometryKey),
|
||||
GearGeometryKey = nameof(GearGeometryKey),
|
||||
HornGeometryKey = nameof(HornGeometryKey),
|
||||
LetterAKey = nameof(LetterAKey),
|
||||
LetterDKey = nameof(LetterDKey),
|
||||
OrdersGeometryKey = nameof(OrdersGeometryKey),
|
||||
@@ -49,7 +48,6 @@ namespace mbts.ui {
|
||||
PlayGeometryKey = nameof(PlayGeometryKey),
|
||||
ProhibitedGeometryKey = nameof(ProhibitedGeometryKey),
|
||||
RubberStampGeometryKey = nameof(RubberStampGeometryKey),
|
||||
SpeakerGeometryKey = nameof(SpeakerGeometryKey),
|
||||
StarGeometryKey = nameof(StarGeometryKey),
|
||||
ToolsGeometryKey = nameof(ToolsGeometryKey),
|
||||
TrashGeometryKey = nameof(TrashGeometryKey),
|
||||
@@ -170,7 +168,6 @@ namespace mbts.ui {
|
||||
public static ImageSource FilterActive { get { return load_resource_and_freeze("FilterActive"); } }
|
||||
public static ImageSource FilterError { get { return load_resource_and_freeze("FilterError"); } }
|
||||
public static ImageSource Gear => create_icon(GearGeometryKey);
|
||||
public static ImageSource Horn => create_icon(HornGeometryKey);
|
||||
public static ImageSource Import => load_resource_and_freeze("Import");
|
||||
public static ImageSource Knight { get { return load_resource_and_freeze("Knight"); } }
|
||||
public static ImageSource Payment => create_icon(PaymentGeometryKey);
|
||||
@@ -179,7 +176,6 @@ namespace mbts.ui {
|
||||
public static ImageSource RubberStamp => create_icon(RubberStampGeometryKey);
|
||||
public static ImageSource RedoCircular => create_icon(UndoCircularGeometryKey, Brushes.Black, null, new ScaleTransform(-1, 1));
|
||||
public static ImageSource Spawn { get { return load_resource_and_freeze("Spawn"); } }
|
||||
public static ImageSource Speaker => create_icon(SpeakerGeometryKey);
|
||||
public static ImageSource Star => create_icon(StarGeometryKey);
|
||||
public static ImageSource Target { get { return load_resource_and_freeze("Target"); } }
|
||||
public static ImageSource Tetris { get { return load_resource_and_freeze("Tetris"); } }
|
||||
|
||||
@@ -274,10 +274,6 @@
|
||||
M16.000015,9.6000061C12.500012,9.6000061 9.5999844,12.5 9.5999844,16 9.5999844,19.5 12.500012,22.399994 16.000015,22.399994 19.500019,22.399994 22.400015,19.5 22.400015,16 22.400015,12.5 19.500019,9.6000061 16.000015,9.6000061z M14.199995,0L17.800005,0C18.199999,0,18.599993,0.29998779,18.599993,0.79998779L18.599993,4.7999878C19.900013,5.1000061,21.000019,5.6000061,22.099997,6.2999878L25.000023,3.3999939C25.099999,3.2999878 25.300011,3.1999817 25.500025,3.1999817 25.700007,3.1999817 25.900019,3.2999878 26.000025,3.3999939L28.600033,6C28.900021,6.2999878,28.900021,6.7999878,28.600033,7.1000061L25.700007,10C26.400019,11.100006,26.900019,12.299988,27.200007,13.5L31.20001,13.5C31.600037,13.5,31.999998,13.799988,31.999998,14.299988L31.999998,17.899994C31.999998,18.299988,31.700012,18.699982,31.20001,18.699982L27.200007,18.699982C26.900019,20,26.400019,21.100006,25.700007,22.199982L28.600033,25.100006C28.700009,25.199982 28.799984,25.399994 28.799984,25.600006 28.799984,25.799988 28.700009,26 28.600033,26.100006L26.000025,28.600006C25.900019,28.699982 25.700007,28.799988 25.500025,28.799988 25.300011,28.799988 25.099999,28.699982 25.000023,28.600006L22.099997,25.699982C21.000019,26.399994,19.800007,26.899994,18.599993,27.199982L18.599993,31.199982C18.599993,31.600006,18.300005,32,17.800005,32L14.199995,32C13.8,32,13.400006,31.699982,13.400006,31.199982L13.400006,27.199982C12.099987,26.899994,11.000011,26.399994,9.9000027,25.699982L7.0000064,28.600006C6.9000003,28.699982 6.6999881,28.799988 6.5000059,28.799988 6.2999937,28.799988 6.099981,28.699982 6.0000054,28.600006L3.3999967,26C3.0999784,25.699982,3.0999784,25.199982,3.3999967,24.899994L6.2999937,22C5.5999805,20.899994,5.0999801,19.699982,4.7999923,18.5L0.79998828,18.5C0.39999409,18.5,-1.933513E-07,18.199982,0,17.699982L0,14.100006C-1.933513E-07,13.699982,0.2999879,13.299988,0.79998828,13.299988L4.7999923,13.299988C5.0999801,12,5.5999805,10.899994,6.2999937,9.7999878L3.3999967,7C3.2999908,6.8999939 3.1999845,6.6999817 3.1999845,6.5 3.1999845,6.2999878 3.2999908,6.1000061 3.3999967,6L6.0000054,3.3999939C6.099981,3.2999878 6.2999937,3.1999817 6.5000059,3.1999817 6.6999881,3.1999817 6.9000003,3.2999878 7.0000064,3.3999939L9.9000027,6.2999878C11.000011,5.6000061,12.199993,5.1000061,13.400006,4.7999878L13.400006,0.79998779C13.400006,0.29998779,13.8,0,14.199995,0z
|
||||
</sys:String>
|
||||
|
||||
<sys:String x:Key="{x:Static mbts:Icons.HornGeometryKey}">
|
||||
M4.0499969,7.0870913C2.9519978,7.0870913 2.0249984,8.4780906 2.0249987,10.125091 2.0249984,11.771091 2.9519978,13.162091 4.0499969,13.162091 5.1479961,13.162091 6.0749954,11.771091 6.0749955,10.125091 6.0749954,8.4780906 5.1479961,7.0870913 4.0499969,7.0870913z M29.975016,3.316155C23.883831,8.2016557,11.829888,8.9691842,8.1184387,9.0896781L8.0151367,9.0928462 8.0175586,9.1059306C8.0716033,9.4352315 8.0999939,9.7760909 8.0999942,10.125091 8.0999939,10.473966 8.0716033,10.814716 8.0175586,11.143921L8.0098858,11.185364 8.193316,11.195448C12.163719,11.420399,23.994581,12.458921,29.975016,17.005057z M31.176758,0.00019051835C31.280359,-0.0022594665 31.391857,0.01889403 31.514975,0.065146674 31.908983,0.21413228 31.999985,0.59114302 31.999985,1.0121596L31.999985,19.236969C31.999985,19.653958 31.913988,20.028956 31.525991,20.179955 31.405998,20.226952 31.450981,20.250023 31.32699,20.250023 31.050991,20.250023 30.778992,20.13601 30.583986,19.925928 25.229074,14.140572 9.6001122,13.269665 7.2865356,13.172559L7.2813506,13.172345 7.1739591,13.343056C6.4304952,14.468533 5.3060585,15.187091 4.0499969,15.187091 1.8169985,15.187091 0,12.916091 0,10.125091 0,7.3330911 1.8169985,5.0620912 4.0499969,5.0620912 5.3060585,5.0620912 6.4304952,5.78065 7.1739591,6.9063042L7.2852713,7.0832756 7.3087114,7.0828188C9.6077337,7.036146 25.141474,6.4939421 30.567997,0.34315929 30.75913,0.12521249 30.948835,0.005580858 31.176758,0.00019051835z
|
||||
</sys:String>
|
||||
|
||||
<sys:String x:Key="{x:Static mbts:Icons.OrdersGeometryKey}">
|
||||
M50.14375,33.389126C50.416863,33.394688,50.653496,33.587773,50.705998,33.864617L50.015995,45.114388C49.810295,46.291462,49.130394,45.097489,49.130394,45.097489L47.65659,42.640538C42.747776,49.635197 34.633152,54.221001 25.433924,54.221001 14.884193,54.221001 5.7437552,48.204125 1.2449617,39.419205 0.80087067,38.552021 0.21358696,37.705738 0.052086769,36.736958 -0.49735315,33.490925 3.4507786,32.901036 4.8049124,34.432404 5.3674143,35.067692 6.034036,36.16407 6.5079177,37.026051 10.106778,43.799416 17.2321,48.413819 25.433924,48.413819 32.656544,48.413819 39.026364,44.826695 42.903977,39.351506L40.229367,38.854115C40.229367,38.854115,38.677464,38.647118,39.742466,37.996033L50.024895,33.398526C50.064972,33.391365,50.104733,33.388333,50.14375,33.389126z M23.030291,9.0310004L27.889918,9.0310004 27.889918,13.028299C30.539786,13.205498,32.324932,13.958096,33.464161,14.528294L33.964176,14.775793 32.417435,20.038878 31.706417,19.68068C30.579987,19.11958 28.839241,18.244583 26.149073,18.244583 24.006916,18.244583 22.723183,19.122181 22.723183,20.590876 22.723183,21.894272 23.943314,22.804369 27.551308,24.187165 32.706444,26.124659 34.911,28.702853 34.911,32.817341 34.911,36.892929 32.090527,40.05172 27.673611,40.989117L27.673611,45.540001 22.767485,45.540001 22.767485,41.330414C20.252919,41.155815,17.695551,40.424118,16.101811,39.404721L15.683999,39.137619 17.221739,33.731438 17.969059,34.180634C19.088988,34.851235 21.402748,35.97873 24.340325,35.97873 26.734886,35.97873 28.276728,34.921635 28.276728,33.291336 28.276728,31.986641 27.547409,30.853846 23.986016,29.57255 20.083815,28.144253 16.095511,26.04406 16.095511,21.239174 16.095511,17.280985 18.737379,14.291395 23.030291,13.329098z M25.268765,0C35.819447,2.7347414E-07 44.961449,6.0155495 49.458798,14.804573 49.902695,15.669173 50.490094,16.516972 50.651287,17.48427 51.198281,20.734265 47.255726,21.318962 45.901341,19.789065 45.336346,19.153567 44.669552,18.051968 44.19566,17.19517 40.5957,10.421881 33.473275,5.8072398 25.268765,5.8072393 18.047545,5.8072398 11.678013,9.3918336 7.8003562,14.867073L10.474627,15.367072C10.474627,15.367072,12.026909,15.574172,10.962921,16.223872L0.67933281,20.826762C0.36037083,20.880062,0.057029126,20.669165,0.00099972012,20.354063L0.69093342,9.1118043C0.89552094,7.9296158,1.5738537,9.124924,1.5738539,9.124924L3.0452772,11.583379C7.9566038,4.584462,16.073666,2.7347414E-07,25.268765,0z
|
||||
</sys:String>
|
||||
@@ -331,11 +327,6 @@
|
||||
M33.5,4h-6v10h6V4z M31.5,12h-2V6h2V12z
|
||||
</sys:String>
|
||||
</x:Array>
|
||||
|
||||
<sys:String x:Key="{x:Static mbts:Icons.SpeakerGeometryKey}">
|
||||
M18,12.839666L18,19.159448 18.029955,19.131576C18.848108,18.314159 19.332083,17.192702 19.332083,15.999873 19.332083,14.806357 18.848108,13.684685 18.029955,12.867527z M21.738388,4.748316C21.868353,4.7515661 21.999973,4.7804414 22.126221,4.8376924 26.511095,6.8177201 29.344013,11.199782 29.344013,15.999849 29.344013,20.799915 26.511095,25.181977 22.126221,27.162004 21.992224,27.223004 21.853228,27.251006 21.715233,27.251006 21.333244,27.251006 20.970255,27.032003 20.803259,26.662997 20.575266,26.15899 20.799259,25.566981 21.302244,25.339978 24.973139,23.681956 27.34407,20.015905 27.34407,15.999849 27.34407,11.983792 24.973139,8.3177406 21.302244,6.6597177 20.799259,6.4327146 20.575266,5.8407065 20.803259,5.3366992 20.973503,4.9594442 21.348494,4.7385656 21.738388,4.748316z M16,3.1854247L6.6530151,11.257323C6.4719848,11.414303,6.2399902,11.500301,6,11.500301L1.9999998,11.500301 1.9999998,21.500148 6,21.500148C6.2219848,21.500148,6.4370117,21.573144,6.6110229,21.709129L16,28.964023z M17.000252,1.186163E-08C17.141373,-2.1778678E-05 17.283264,0.02997845 17.416016,0.091477852 17.770996,0.25450047 18,0.60843792 18,1.0004608L18,10.368904 18.170259,10.465408C20.111607,11.612996 21.331976,13.716092 21.331976,15.999873 21.331976,18.282748 20.111607,20.38576 18.170259,21.53334L18,21.629842 18,31.000003C18,31.382016 17.78302,31.728995 17.440002,31.896962 17.301025,31.965991 17.150024,31.99999 17,31.99999 16.78302,31.99999 16.567017,31.929982 16.388977,31.791007L5.65802,23.500117 0.99999966,23.500117C0.44702145,23.500117,-3.9348969E-08,23.053104,3.5527137E-15,22.500133L3.5527137E-15,10.500316C-3.9348969E-08,9.9473459,0.44702145,9.5003311,0.99999966,9.5003311L5.6279907,9.5003311 16.346985,0.24345344C16.531998,0.083467316,16.765053,3.6392792E-05,17.000252,1.186163E-08z
|
||||
</sys:String>
|
||||
|
||||
<sys:String x:Key="{x:Static mbts:Icons.StarGeometryKey}">
|
||||
M1540.22,2082.07L1546.95,2102.78 1568.73,2102.78 1551.11,2115.58 1557.84,2136.29 1540.22,2123.49 1522.6,2136.29 1529.33,2115.58 1511.71,2102.78 1533.49,2102.78 1540.22,2082.07z
|
||||
</sys:String>
|
||||
|
||||
@@ -36,7 +36,6 @@ namespace mbts.spark {
|
||||
readonly MyOrderSummarizer _order_summarizer = new MyOrderSummarizer();
|
||||
readonly MDDisplayHelper.SizeFormatter _size_formatter = MDDisplayHelper.get_formatter(MarketSizeDisplay.get_default());
|
||||
Property<uint> _bid_direct_size, _bid_merged_size, _ask_direct_size, _ask_merged_size;
|
||||
double _raw_day_position;
|
||||
string _bid_merged_size_text, _ask_merged_size_text;
|
||||
ulong _bbo_ref, _last_ref;
|
||||
|
||||
@@ -90,10 +89,6 @@ namespace mbts.spark {
|
||||
_dispose = Disposable.Create(
|
||||
_orders_model.on_security_root_orders(security.Id).OnDispatcher().Subscribe(_on_orders),
|
||||
_position.OnDispatcher().Subscribe(v => _animation.animate_position(v.newval > v.oldval)),
|
||||
_orders_model.on_security_positions(security.Id).OnDispatcher().Subscribe(t => {
|
||||
if (Set(ref _raw_day_position, t.state.Values.Sum(p => p.Item2.raw_day_pos()), nameof(raw_day_position)))
|
||||
raise_invalidated();
|
||||
}),
|
||||
_inside_bids.OnDispatcher().Subscribe(v => _animation.animate_working_bids_inside(v.newval)),
|
||||
_inside_asks.OnDispatcher().Subscribe(v => _animation.animate_working_asks_inside(v.newval)),
|
||||
_day_change.OnDispatcher().Subscribe(v => _animation.animate_day_change(v.newval > v.oldval)),
|
||||
@@ -107,7 +102,6 @@ namespace mbts.spark {
|
||||
public bool inside_bids => _inside_bids.value;
|
||||
public bool inside_asks => _inside_asks.value;
|
||||
public int position => _position.value;
|
||||
public double raw_day_position => _raw_day_position;
|
||||
public double last_price => _last_price.Value;
|
||||
public uint last_size => _last_size.value;
|
||||
public double bid_price => _bid_price.Value;
|
||||
|
||||
@@ -10,6 +10,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reactive.Subjects;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Media;
|
||||
using static bts.shell.MarketViewWindowFactory;
|
||||
@@ -41,13 +42,12 @@ namespace mbts.spark {
|
||||
add_col(Columns.Last, "Last", r => r.format_price(r.last_price), nameof(Row.last_price), DefaultColumnWidths.Price, last_price_filter, DataGridFilterKnownTemplates.DoubleMinMax);
|
||||
add_col(Columns.LastSize, merged_size_col_header, r => r.last_size.ToString(), nameof(Row.last_size), DefaultColumnWidths.Size, last_size_filter, DataGridFilterKnownTemplates.UIntMinMax, h_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
add_col(Columns.Position, "Pos", r => r.position.ToString(), nameof(Row.position), DefaultColumnWidths.Size, position_filter, DataGridFilterKnownTemplates.IntMinMax, true, h_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
add_col(Columns.RawDayPosition, "Raw Day Pos", r => SparkUtils.fmt_position(r.raw_day_position), nameof(Row.raw_day_position), DefaultColumnWidths.Size, raw_day_position_filter, DataGridFilterKnownTemplates.DoubleMinMax, true, h_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
add_hl_working_orders_col(Columns.WorkingBids, working_col_header, nameof(Row.working_bids_text), nameof(Row.working_best_bid), nameof(Row.working_bids_click), working_bid_volume_filter, BTSColors.MarketData.bid, System.Windows.HorizontalAlignment.Right);
|
||||
add_col(Columns.BidImpliedSize, implied_size_col_header, r => r.bid_implied_size.ToString(), nameof(Row.bid_implied_size), DefaultColumnWidths.ImpliedDirectMergedSize, bid_implied_size_filter, DataGridFilterKnownTemplates.UIntMinMax, font_color: BTSColors.MarketData.bid, h_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
add_col(Columns.BidDirectSize, direct_size_col_header, r => r.bid_direct_size.ToString(), nameof(Row.bid_direct_size), DefaultColumnWidths.ImpliedDirectMergedSize, bid_direct_size_filter, DataGridFilterKnownTemplates.UIntMinMax, font_color: BTSColors.MarketData.bid, h_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
add_hl_merged_size_col(Columns.BidMergedSize, merged_size_col_header, nameof(Row.bid_merged_size), nameof(Row.bid_merged_size_text), nameof(Row.bid_merged_size_click), bid_merged_size_filter, BTSColors.MarketData.bid, h_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
add_hl_price_col(Columns.Bid, "Bid", nameof(Row.bid_price), nameof(Row.bid_price_click), bid_price_filter, BTSColors.header_text, BTSColors.MarketData.bid, h_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
add_hl_price_col(Columns.Ask, "Ask", nameof(Row.ask_price), nameof(Row.ask_price_click), ask_price_filter, BTSColors.header_text, BTSColors.MarketData.ask, h_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
add_hl_price_col(Columns.Bid, "Bid", nameof(Row.bid_price), nameof(Row.bid_price_click), bid_price_filter, BTSColors.header_text, BTSColors.MarketData.bid_brush, h_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
add_hl_price_col(Columns.Ask, "Ask", nameof(Row.ask_price), nameof(Row.ask_price_click), ask_price_filter, BTSColors.header_text, BTSColors.MarketData.ask_brush, h_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
add_hl_merged_size_col(Columns.AskMergedSize, merged_size_col_header, nameof(Row.ask_merged_size), nameof(Row.ask_merged_size_text), nameof(Row.ask_merged_size_click), ask_merged_size_filter, BTSColors.MarketData.ask, h_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
add_col(Columns.AskDirectSize, direct_size_col_header, r => r.ask_direct_size.ToString(), nameof(Row.ask_direct_size), DefaultColumnWidths.ImpliedDirectMergedSize, ask_direct_size_filter, DataGridFilterKnownTemplates.UIntMinMax, font_color: BTSColors.MarketData.ask, h_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
add_col(Columns.AskImpliedSize, implied_size_col_header, r => r.ask_implied_size.ToString(), nameof(Row.ask_implied_size), DefaultColumnWidths.ImpliedDirectMergedSize, ask_implied_size_filter, DataGridFilterKnownTemplates.UIntMinMax, font_color: BTSColors.MarketData.ask, h_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
@@ -77,18 +77,18 @@ namespace mbts.spark {
|
||||
horizontal_alignment = h_alignment
|
||||
});
|
||||
|
||||
void add_hl_price_col(Columns col_key, string header, string prop, string command_prop, IDataGridColumnFilter filter, Color foreground, Color? background, System.Windows.HorizontalAlignment h_alignment) =>
|
||||
add_hl_col(col_key, header, price_binding(prop), prop, command_prop, filter, DataGridFilterKnownTemplates.DoubleMinMax, DefaultColumnWidths.MViewPrice, foreground, background, h_alignment);
|
||||
void add_hl_price_col(Columns col_key, string header, string prop, string command_prop, IDataGridColumnFilter filter, Color foreground, Brush background, System.Windows.HorizontalAlignment h_alignment) =>
|
||||
add_hl_col(col_key, header, price_binding(prop), prop, command_prop, filter, DataGridFilterKnownTemplates.DoubleMinMax, DefaultColumnWidths.MViewPrice, foreground, background, DefaultSelectionHighlight, h_alignment);
|
||||
|
||||
void add_hl_merged_size_col(Columns col_key, string header, string sort_prop, string text_prop, string command_prop, IDataGridColumnFilter filter, Color foreground, System.Windows.HorizontalAlignment h_alignment) =>
|
||||
add_hl_col(col_key, header, new Binding(text_prop), sort_prop, command_prop, filter, DataGridFilterKnownTemplates.UIntMinMax, DefaultColumnWidths.ImpliedDirectMergedSize, foreground, null, h_alignment);
|
||||
add_hl_col(col_key, header, new Binding(text_prop), sort_prop, command_prop, filter, DataGridFilterKnownTemplates.UIntMinMax, DefaultColumnWidths.ImpliedDirectMergedSize, foreground, null, null, h_alignment);
|
||||
|
||||
void add_hl_working_orders_col(Columns col_key, string header, string text_prop, string sort_prop, string command_prop, IDataGridColumnFilter filter, Color foreground, System.Windows.HorizontalAlignment h_alignment) =>
|
||||
add_hl_col(col_key, header, new Binding(text_prop), sort_prop, command_prop, filter, DataGridFilterKnownTemplates.IntMinMax, working_orders_col_width, foreground, null, h_alignment);
|
||||
add_hl_col(col_key, header, new Binding(text_prop), sort_prop, command_prop, filter, DataGridFilterKnownTemplates.IntMinMax, working_orders_col_width, foreground, null, null, h_alignment);
|
||||
|
||||
void add_hl_col(
|
||||
Columns col_key, string header, BindingBase binding, string prop, string command_prop,
|
||||
IDataGridColumnFilter filter, string filter_template, double width, Color foreground, Color? background, System.Windows.HorizontalAlignment h_alignment = System.Windows.HorizontalAlignment.Left) =>
|
||||
IDataGridColumnFilter filter, string filter_template, double width, Color foreground, Brush background, Brush background_overlay, System.Windows.HorizontalAlignment h_alignment = System.Windows.HorizontalAlignment.Left) =>
|
||||
columns.Add(new HyperlinkElementColumn<Row, Columns>(
|
||||
col_key, binding, new Binding(command_prop), filter, filter_template) {
|
||||
// it's necessary to have this id populated for grid settings
|
||||
@@ -96,7 +96,8 @@ namespace mbts.spark {
|
||||
Header = header,
|
||||
sort_member_path = prop,
|
||||
font_color_override = foreground,
|
||||
background_color_override = background,
|
||||
background = background,
|
||||
background_overlay = background_overlay,
|
||||
Width = DefaultColumnWidths.Price,
|
||||
horizontal_alignment = h_alignment
|
||||
});
|
||||
@@ -110,8 +111,14 @@ namespace mbts.spark {
|
||||
//
|
||||
// make font-color immutable for Side, Size, (the font-color rule is hard-coded: MarketData.bid for Buy orders, MarketData.ask for Sells)
|
||||
var col_settings_flags = new Dictionary<string, GridColumnSettingsFeatureFlags> {
|
||||
{ Columns.Bid.ToString(), GridColumnSettingsFeatureFlags.ImmutableFontColor | GridColumnSettingsFeatureFlags.ImmutableBackgroundColor },
|
||||
{ Columns.Ask.ToString(), GridColumnSettingsFeatureFlags.ImmutableFontColor | GridColumnSettingsFeatureFlags.ImmutableBackgroundColor},
|
||||
{ Columns.Bid.ToString(), GridColumnSettingsFeatureFlags.ImmutableFontColor },
|
||||
{ Columns.Ask.ToString(), GridColumnSettingsFeatureFlags.ImmutableFontColor },
|
||||
{ Columns.BidImpliedSize.ToString(), GridColumnSettingsFeatureFlags.ImmutableFontColor },
|
||||
{ Columns.BidDirectSize.ToString(), GridColumnSettingsFeatureFlags.ImmutableFontColor },
|
||||
{ Columns.BidMergedSize.ToString(), GridColumnSettingsFeatureFlags.ImmutableFontColor },
|
||||
{ Columns.AskImpliedSize.ToString(), GridColumnSettingsFeatureFlags.ImmutableFontColor },
|
||||
{ Columns.AskDirectSize.ToString(), GridColumnSettingsFeatureFlags.ImmutableFontColor },
|
||||
{ Columns.AskMergedSize.ToString(), GridColumnSettingsFeatureFlags.ImmutableFontColor },
|
||||
{ Columns.WorkingAsks.ToString(), GridColumnSettingsFeatureFlags.ImmutableFontColor },
|
||||
{ Columns.WorkingBids.ToString(), GridColumnSettingsFeatureFlags.ImmutableFontColor }
|
||||
};
|
||||
@@ -144,7 +151,6 @@ namespace mbts.spark {
|
||||
session_low_filter.load_state(filters.session_low);
|
||||
session_high_filter.load_state(filters.session_high);
|
||||
position_filter.load_state(filters.position);
|
||||
raw_day_position_filter.load_state(filters.raw_day_position);
|
||||
working_bid_volume_filter.load_state(filters.working_bid_volume);
|
||||
working_ask_volume_filter.load_state(filters.working_ask_volume);
|
||||
volume_filter.load_state(filters.volume);
|
||||
@@ -156,7 +162,7 @@ namespace mbts.spark {
|
||||
bid_price_filter, bid_merged_size_filter, bid_direct_size_filter, bid_implied_size_filter,
|
||||
ask_price_filter, ask_merged_size_filter, ask_direct_size_filter, ask_implied_size_filter,
|
||||
last_price_filter, last_size_filter, day_change_filter, settlement_filter,
|
||||
session_low_filter, session_high_filter, position_filter, raw_day_position_filter, working_bid_volume_filter, working_ask_volume_filter, volume_filter, security_key_filter),
|
||||
session_low_filter, session_high_filter, position_filter, working_bid_volume_filter, working_ask_volume_filter, volume_filter, security_key_filter),
|
||||
settings_manager.subscribe_current().OnDispatcher().Subscribe(grid_settings_manager),
|
||||
WorkLoop.Start("MarketView", _refresh)
|
||||
);
|
||||
@@ -224,7 +230,6 @@ namespace mbts.spark {
|
||||
public IDataGridColumnPersistentFilter<Row, DoubleMinMaxState> session_low_filter { get; } = new DoubleMinMax<Row>(r => r.session_low, nameof(Row.session_low));
|
||||
public IDataGridColumnPersistentFilter<Row, DoubleMinMaxState> session_high_filter { get; } = new DoubleMinMax<Row>(r => r.session_high, nameof(Row.session_high));
|
||||
public IDataGridColumnPersistentFilter<Row, IntMinMaxState> position_filter { get; } = new IntMinMax<Row>(r => r.position, nameof(Row.position));
|
||||
public IDataGridColumnPersistentFilter<Row, DoubleMinMaxState> raw_day_position_filter { get; } = new DoubleMinMax<Row>(r => r.raw_day_position, nameof(Row.raw_day_position));
|
||||
public IDataGridColumnPersistentFilter<Row, IntMinMaxState> working_bid_volume_filter { get; } = new IntMinMax<Row>(r => r.working_bid_volume, nameof(Row.working_bid_volume));
|
||||
public IDataGridColumnPersistentFilter<Row, IntMinMaxState> working_ask_volume_filter { get; } = new IntMinMax<Row>(r => r.working_ask_volume, nameof(Row.working_ask_volume));
|
||||
public IDataGridColumnPersistentFilter<Row, ULongMinMaxState> volume_filter { get; } = new ULongMinMax<Row>(r => r.volume, nameof(Row.volume));
|
||||
@@ -256,7 +261,6 @@ namespace mbts.spark {
|
||||
session_low = session_low_filter.get_state(),
|
||||
session_high = session_high_filter.get_state(),
|
||||
position = position_filter.get_state(),
|
||||
raw_day_position = raw_day_position_filter.get_state(),
|
||||
working_bid_volume = working_bid_volume_filter.get_state(),
|
||||
working_ask_volume = working_ask_volume_filter.get_state(),
|
||||
volume = volume_filter.get_state(),
|
||||
|
||||
@@ -8,34 +8,8 @@ namespace mbts.ui.spark {
|
||||
/// Interaction logic for MarketSheetsView.xaml
|
||||
/// </summary>
|
||||
partial class MarketViewView : UserControl {
|
||||
public enum Columns {
|
||||
Security,
|
||||
Volume,
|
||||
Last,
|
||||
LastSize,
|
||||
Position,
|
||||
RawDayPosition,
|
||||
WorkingBids,
|
||||
Bid,
|
||||
BidMergedSize,
|
||||
BidDirectSize,
|
||||
BidImpliedSize,
|
||||
Ask,
|
||||
AskMergedSize,
|
||||
AskDirectSize,
|
||||
AskImpliedSize,
|
||||
WorkingAsks,
|
||||
Settlement,
|
||||
DayChange,
|
||||
SessionLow,
|
||||
SessionHigh,
|
||||
SecurityKey,
|
||||
MarketDescription,
|
||||
ClearedAlias,
|
||||
ProductDescription,
|
||||
HubAlias,
|
||||
StripName,
|
||||
MicCode
|
||||
public enum Columns { Security, Volume, Last, LastSize, Position, WorkingBids, Bid, BidMergedSize, BidDirectSize, BidImpliedSize, Ask, AskMergedSize, AskDirectSize, AskImpliedSize, WorkingAsks, Settlement, DayChange, SessionLow, SessionHigh, SecurityKey,
|
||||
MarketDescription, ClearedAlias, ProductDescription, HubAlias, StripName, MicCode
|
||||
}
|
||||
|
||||
public MarketViewView() {
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace mbts.spark {
|
||||
_selected_security_criteria_backing,
|
||||
_sorted_selected_security_criteria_backing;
|
||||
|
||||
public MarketViewWindow(MarketViewWindowFactory factory, IBus bus, OrdersModel orders_model, MarketViewWindowFactory.StartupData state, IWindowSettingsManager<GridSettings> settings_manager)
|
||||
public MarketViewWindow(MarketViewWindowFactory factory, IBus bus, OrdersModel orders_model, MarketViewWindowFactory.StartupData state, WindowSettingsManager<GridSettings> settings_manager)
|
||||
: base("Market View", bus, factory, SparkCommands.ShowMarketView.icon) {
|
||||
_orders_model = orders_model;
|
||||
_state = state;
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using bts.api;
|
||||
using bts.proto;
|
||||
using bts.settings;
|
||||
using mbts.persist;
|
||||
using mbts.spark;
|
||||
using mbts.spark.filters;
|
||||
@@ -9,7 +7,6 @@ using mbts.ui.settings;
|
||||
using ProtoBuf;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading.Tasks;
|
||||
@@ -38,8 +35,13 @@ namespace bts.shell {
|
||||
void _show(SecurityCriteria criteria) =>
|
||||
_bus.post(SparkTags.ShowWindow, _create(StartupData.Default(criteria)));
|
||||
|
||||
IShellWindow _create(StartupData state) =>
|
||||
new MarketViewWindow(this, _bus, _orders_model, state, _settings_hub.create_mview_settings_manager(state.grid));
|
||||
IShellWindow _create(StartupData state) {
|
||||
var settings_manager = new WindowSettingsManager<GridSettings>(_settings_hub,
|
||||
state.grid,
|
||||
get: us => us.market_view_grid,
|
||||
set: (us, gs) => us.with(provided_market_view_gridsettings: gs));
|
||||
return new MarketViewWindow(this, _bus, _orders_model, state, settings_manager);
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
public class StartupData {
|
||||
@@ -66,29 +68,7 @@ namespace bts.shell {
|
||||
_sorting_criteria = sort;
|
||||
}
|
||||
|
||||
public static GridSettings sanitize(GridSettings s) {
|
||||
if (s == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// column upgrade
|
||||
var columns = s.columns.ToList();
|
||||
var bid_col_index = columns.FindIndex(x => x.id == col_id(Columns.Bid));
|
||||
var ask_col_index = columns.FindIndex(x => x.id == col_id(Columns.Ask));
|
||||
if (bid_col_index > -1 && ask_col_index > -1) {
|
||||
if (!columns[bid_col_index].background_color_override.HasValue) {
|
||||
columns[bid_col_index] = columns[bid_col_index].with(background_color_override: new NullableColorProto(BTSColors.MarketData.bid.ToUint()));
|
||||
}
|
||||
if (!columns[ask_col_index].background_color_override.HasValue) {
|
||||
columns[ask_col_index] = columns[ask_col_index].with(background_color_override: new NullableColorProto(BTSColors.MarketData.ask.ToUint()));
|
||||
}
|
||||
}
|
||||
// and enable background color upgrade
|
||||
return s.with(columns, enable_background_color: true);
|
||||
}
|
||||
|
||||
|
||||
public static GridSettings DefaultGridSettings => GridSettings.Default.with(DefaultColumns.all, DefaultColumns.default_sorting_criterion, enable_background_color: true);
|
||||
public static GridSettings DefaultGridSettings => GridSettings.Default.with(DefaultColumns.all, DefaultColumns.default_sorting_criterion);
|
||||
|
||||
public IReadOnlyList<SortingCriteria> sorting_criterion => _sorting_criteria ?? (_sorting_criteria = DefaultColumns.default_sorting_criterion.ToList());
|
||||
|
||||
@@ -102,13 +82,12 @@ namespace bts.shell {
|
||||
public static GridColumnSettings last => new GridColumnSettings(col_id(Columns.Last), DefaultColumnHeaders.Last, width: DefaultColumnWidths.Price);
|
||||
public static GridColumnSettings last_size => new GridColumnSettings(col_id(Columns.LastSize), DefaultColumnHeaders.Size, width: DefaultColumnWidths.Size, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings position => new GridColumnSettings(col_id(Columns.Position), DefaultColumnHeaders.Pos, width: DefaultColumnWidths.Size, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings raw_day_position => new GridColumnSettings(col_id(Columns.RawDayPosition), DefaultColumnHeaders.RawDayPos, width: DefaultColumnWidths.Size, horizontal_alignment: System.Windows.HorizontalAlignment.Right, is_visible: false);
|
||||
public static GridColumnSettings working_bids => new GridColumnSettings(col_id(Columns.WorkingBids), DefaultColumnHeaders.Working, font_color_override: BTSColors.MarketData.bid, width: DefaultColumnWidths.Working, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings bid_implied_size => new GridColumnSettings(col_id(Columns.BidImpliedSize), DefaultColumnHeaders.ImpliedSize, font_color_override: BTSColors.MarketData.bid, width: DefaultColumnWidths.ImpliedDirectMergedSize, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings bid_direct_size => new GridColumnSettings(col_id(Columns.BidDirectSize), DefaultColumnHeaders.DirectSize, font_color_override: BTSColors.MarketData.bid, width: DefaultColumnWidths.ImpliedDirectMergedSize, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings bid_merged_size => new GridColumnSettings(col_id(Columns.BidMergedSize), DefaultColumnHeaders.MergedSize, font_color_override: BTSColors.MarketData.bid, width: DefaultColumnWidths.ImpliedDirectMergedSize, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings bid => new GridColumnSettings(col_id(Columns.Bid), DefaultColumnHeaders.Bid, font_color_override: BTSColors.header_text, width:DefaultColumnWidths.MViewPrice, horizontal_alignment: System.Windows.HorizontalAlignment.Right, background_color_override: BTSColors.MarketData.bid);
|
||||
public static GridColumnSettings ask => new GridColumnSettings(col_id(Columns.Ask), DefaultColumnHeaders.Ask, font_color_override: BTSColors.header_text, width: DefaultColumnWidths.MViewPrice, horizontal_alignment: System.Windows.HorizontalAlignment.Right, background_color_override: BTSColors.MarketData.ask);
|
||||
public static GridColumnSettings bid => new GridColumnSettings(col_id(Columns.Bid), DefaultColumnHeaders.Bid, font_color_override: BTSColors.header_text, width:DefaultColumnWidths.MViewPrice, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings ask => new GridColumnSettings(col_id(Columns.Ask), DefaultColumnHeaders.Ask, font_color_override: BTSColors.header_text, width: DefaultColumnWidths.MViewPrice, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings ask_merged_size => new GridColumnSettings(col_id(Columns.AskMergedSize), DefaultColumnHeaders.MergedSize, font_color_override: BTSColors.MarketData.ask, width: DefaultColumnWidths.ImpliedDirectMergedSize, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings ask_direct_size => new GridColumnSettings(col_id(Columns.AskDirectSize), DefaultColumnHeaders.DirectSize, font_color_override: BTSColors.MarketData.ask, width: DefaultColumnWidths.ImpliedDirectMergedSize, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings ask_implied_size => new GridColumnSettings(col_id(Columns.AskImpliedSize), DefaultColumnHeaders.ImpliedSize, font_color_override: BTSColors.MarketData.ask, width: DefaultColumnWidths.ImpliedDirectMergedSize, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
@@ -140,7 +119,6 @@ namespace bts.shell {
|
||||
yield return last;
|
||||
yield return last_size;
|
||||
yield return position;
|
||||
yield return raw_day_position;
|
||||
yield return working_bids;
|
||||
yield return bid_implied_size;
|
||||
yield return bid_direct_size;
|
||||
@@ -195,8 +173,6 @@ namespace bts.shell {
|
||||
public DoubleMinMaxState session_high;
|
||||
[ProtoMember(12)]
|
||||
public IntMinMaxState position;
|
||||
[ProtoMember(27)]
|
||||
public DoubleMinMaxState raw_day_position;
|
||||
[ProtoMember(13)]
|
||||
public IntMinMaxState working_bid_volume;
|
||||
[ProtoMember(14)]
|
||||
@@ -226,46 +202,5 @@ namespace bts.shell {
|
||||
[ProtoMember(26)]
|
||||
public string mic_code;
|
||||
}
|
||||
|
||||
// migration rules
|
||||
[Export(typeof(IMarketViewGridSettingsMigrationRule))]
|
||||
class AddMissingColumnsMigrationRule : IMarketViewGridSettingsMigrationRule {
|
||||
public bool apply(GridSettings settings, out GridSettings new_settings) =>
|
||||
settings.add_missing_columns(StartupData.DefaultGridSettings.columns, out new_settings);
|
||||
}
|
||||
|
||||
[Export(typeof(IMarketViewGridSettingsMigrationRule))]
|
||||
class InitBidAskBackgroundOverrideMigrationRule : IMarketViewGridSettingsMigrationRule {
|
||||
public bool apply(GridSettings s, out GridSettings new_settings) {
|
||||
var columns = s.columns.ToList();
|
||||
var bid_col_index = columns.FindIndex(x => x.id == col_id(Columns.Bid));
|
||||
var ask_col_index = columns.FindIndex(x => x.id == col_id(Columns.Ask));
|
||||
bool applied = false;
|
||||
if (bid_col_index > -1 && ask_col_index > -1) {
|
||||
if (!columns[bid_col_index].background_color_override.HasValue) {
|
||||
columns[bid_col_index] = columns[bid_col_index].with(background_color_override: new NullableColorProto(BTSColors.MarketData.bid.ToUint()));
|
||||
applied = true;
|
||||
}
|
||||
if (!columns[ask_col_index].background_color_override.HasValue) {
|
||||
columns[ask_col_index] = columns[ask_col_index].with(background_color_override: new NullableColorProto(BTSColors.MarketData.ask.ToUint()));
|
||||
applied = true;
|
||||
}
|
||||
}
|
||||
new_settings = applied ? s.with(columns) : default;
|
||||
return applied;
|
||||
}
|
||||
}
|
||||
|
||||
[Export(typeof(IMarketViewGridSettingsMigrationRule))]
|
||||
class InitBackgroundOverrideMigrationRule : IMarketViewGridSettingsMigrationRule {
|
||||
public bool apply(GridSettings settings, out GridSettings new_settings) {
|
||||
if (!settings.enable_background_color) {
|
||||
new_settings = settings.with(enable_background_color: true);
|
||||
return true;
|
||||
}
|
||||
new_settings = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,16 @@ using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
|
||||
namespace mbts.spark {
|
||||
struct TradeInfo {
|
||||
public readonly SecurityImmutable security;
|
||||
public readonly TradeMessageImmutable trade;
|
||||
|
||||
public TradeInfo(SecurityImmutable security, TradeMessageImmutable trade) {
|
||||
this.security = security;
|
||||
this.trade = trade;
|
||||
}
|
||||
}
|
||||
|
||||
class TimeAndSalesModel : IDisposable {
|
||||
internal readonly SecurityCriteria security_criteria;
|
||||
|
||||
|
||||
@@ -137,7 +137,7 @@ namespace mbts.spark {
|
||||
public Row(TimeAndSalesVM parent, SecurityImmutable security, TradeMessageImmutable trade) : base(security) {
|
||||
_trade_key = trade.Key;
|
||||
time = ((ulong)trade.Time).lldatetime().ToLocalTime();
|
||||
price = trade.price();
|
||||
price = trade.Mantissa * Math.Pow(10, trade.Exponent);
|
||||
size = trade.Size;
|
||||
aggressor_side = trade.AggressorSide;
|
||||
show_ladder = new DelegateCommand(() => parent._show_ladder_async(this).ignore());
|
||||
|
||||
@@ -26,10 +26,7 @@ namespace bts.ui {
|
||||
public SettingsShellModule(IBus bus, UserSettingsHub settings_hub, WorkspacesModel ws) {
|
||||
// todo: settings dialog
|
||||
ws_model = ws;
|
||||
var actions_list = new List<SparkCommand>(2) {
|
||||
SparkCommands.ShowGlobalAlerts,
|
||||
SparkCommands.ShowWorkspaces
|
||||
};
|
||||
var actions_list = new List<SparkCommand>(2) { SparkCommands.ShowWorkspaces };
|
||||
if (Configuration.get<bts.config.toggles.ToastNotifications>().enabled) {
|
||||
actions_list.Add(SparkCommands.ShowSettings);
|
||||
}
|
||||
|
||||
+2
-2
@@ -10,7 +10,7 @@ using System.Reactive.Disposables;
|
||||
namespace mbts.spark {
|
||||
static class AlgoParamRowsHelper {
|
||||
public static IDisposable connect(AlgoDefinitionVM algo, ObservableAggregateCollection<AlgoParamRowVM> rows, bool include_algo_type_rows) =>
|
||||
algo == null ? null : new CompositeDisposable(algo.observe_algo_root().OnDispatcher().SubscribeEach(a => {
|
||||
algo == null ? null : new CompositeDisposable(algo.observe_algo_root().SubscribeEach(a => {
|
||||
rows.clear_collections();
|
||||
if (a == null)
|
||||
return Disposable.empty;
|
||||
@@ -77,7 +77,7 @@ namespace mbts.spark {
|
||||
include_algo_type_rows ?
|
||||
(ObservableCollection<AlgoParamRowVM>)new ObservableAppendCollection<AlgoParamRowVM>(child_rows, selector_wrapper) :
|
||||
child_rows,
|
||||
s.subscribe_selected_algo().OnDispatcher().SubscribeEach(a => {
|
||||
s.subscribe_selected_algo().SubscribeEach(a => {
|
||||
s.value = a;
|
||||
child_rows.clear_collections();
|
||||
return _wrap_rows(a, selector_wrapper, child_rows, include_algo_type_rows);
|
||||
|
||||
@@ -194,27 +194,20 @@ namespace mbts.spark {
|
||||
readonly bts.persist.algo.AlgoPersistedKey[] _favorite_algos;
|
||||
[ProtoMember(2)]
|
||||
public readonly int column_count;
|
||||
[ProtoMember(3)]
|
||||
readonly bts.persist.algo.AlgoPersistedKey[] _stickied_algos;
|
||||
|
||||
public LadderFavoriteAlgosSettings(AlgoKey[] favorite_algos, int column_count, AlgoKey[] stickied_algos)
|
||||
: this(ArrayHelper.safe_convert(favorite_algos, k => new bts.persist.algo.AlgoPersistedKey(k.persisted)),
|
||||
column_count,
|
||||
ArrayHelper.safe_convert(stickied_algos, k => new bts.persist.algo.AlgoPersistedKey(k.persisted))) { }
|
||||
public LadderFavoriteAlgosSettings(AlgoKey[] favorite_algos, int column_count)
|
||||
: this(ArrayHelper.safe_convert(favorite_algos, k => new bts.persist.algo.AlgoPersistedKey(k.persisted)), column_count) { }
|
||||
|
||||
LadderFavoriteAlgosSettings(bts.persist.algo.AlgoPersistedKey[] favorite_algos, int column_count, bts.persist.algo.AlgoPersistedKey[] stickied_algos) {
|
||||
LadderFavoriteAlgosSettings(bts.persist.algo.AlgoPersistedKey[] favorite_algos, int column_count) {
|
||||
_favorite_algos = favorite_algos;
|
||||
this.column_count = column_count;
|
||||
_stickied_algos = stickied_algos;
|
||||
}
|
||||
|
||||
public IReadOnlyList<AlgoKey> favorite_algos => ArrayHelper.safe_convert(_favorite_algos, k => k.to_algo_key());
|
||||
public IReadOnlyList<AlgoKey> stickied_algos => ArrayHelper.safe_convert(_stickied_algos, k => k.to_algo_key());
|
||||
|
||||
public bool Equals(LadderFavoriteAlgosSettings other) =>
|
||||
column_count == other.column_count &&
|
||||
favorite_algos.SequenceEqual(other.favorite_algos) &&
|
||||
stickied_algos.SequenceEqual(other.stickied_algos);
|
||||
favorite_algos.SequenceEqual(other.favorite_algos);
|
||||
|
||||
public override bool Equals(object obj) =>
|
||||
EqualityComparer.equals_obj(this, obj);
|
||||
@@ -222,8 +215,7 @@ namespace mbts.spark {
|
||||
public override int GetHashCode() =>
|
||||
EqualityComparer.aggregate_hash(
|
||||
column_count,
|
||||
EqualityComparer.aggregate_hash_limited(favorite_algos),
|
||||
EqualityComparer.aggregate_hash_limited(stickied_algos));
|
||||
EqualityComparer.aggregate_hash_limited(favorite_algos));
|
||||
|
||||
public static bool operator ==(LadderFavoriteAlgosSettings a, LadderFavoriteAlgosSettings b) => a.Equals(b);
|
||||
public static bool operator !=(LadderFavoriteAlgosSettings a, LadderFavoriteAlgosSettings b) => !a.Equals(b);
|
||||
@@ -281,7 +273,7 @@ namespace mbts.spark {
|
||||
order_entry_panel_initial_width = _default_order_entry_panel_width;
|
||||
zoom_factor = _default_zoom_factor;
|
||||
use_defaults = true;
|
||||
favorite_algos = new LadderFavoriteAlgosSettings(null, 2, null);
|
||||
favorite_algos = new LadderFavoriteAlgosSettings(null, 2);
|
||||
show_warn_on_cross = _default_show_warn_on_cross;
|
||||
}
|
||||
|
||||
@@ -450,8 +442,6 @@ namespace mbts.spark {
|
||||
public bool account_position_visible { get; set; }
|
||||
[ProtoMember(50), DefaultValue(true)]
|
||||
public bool day_position_visible { get; set; }
|
||||
[ProtoMember(56)]
|
||||
public bool raw_day_position_visible { get; set; }
|
||||
[ProtoMember(37), DefaultValue(true)]
|
||||
public bool size_presets_visible { get; set; }
|
||||
[ProtoMember(38), DefaultValue(true)]
|
||||
@@ -572,7 +562,6 @@ namespace mbts.spark {
|
||||
order_type_visible == other.order_type_visible &&
|
||||
account_position_visible == other.account_position_visible &&
|
||||
day_position_visible == other.day_position_visible &&
|
||||
raw_day_position_visible == other.raw_day_position_visible &&
|
||||
size_presets_visible == other.size_presets_visible &&
|
||||
cancel_buys_visible == other.cancel_buys_visible &&
|
||||
cancel_sells_visible == other.cancel_sells_visible &&
|
||||
|
||||
@@ -11,7 +11,6 @@ using ProtoBuf;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reactive.Subjects;
|
||||
@@ -23,7 +22,6 @@ using LadderSettings = mbts.spark.LadderSettings;
|
||||
namespace bts.shell {
|
||||
class LadderWindowFactory : ShellWindowFactory<LadderSaveState> {
|
||||
const string _window_type = "ladder";
|
||||
const int _version_where_stickied_algos_introduced = 2;
|
||||
readonly LadderConfig _ladder_conf;
|
||||
readonly IBus _bus;
|
||||
readonly IWindowManager _wm;
|
||||
@@ -40,7 +38,7 @@ namespace bts.shell {
|
||||
OrdersModel orders_model,
|
||||
MBOService mbo)
|
||||
|
||||
: base(owner, "SparkLadder", 2) {
|
||||
: base(owner, "SparkLadder") {
|
||||
_bus = bus;
|
||||
_wm = wm;
|
||||
_settings_hub = settings_hub;
|
||||
@@ -62,16 +60,6 @@ namespace bts.shell {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected override Task<IShellWindow> try_upgrade_state(Stream load_stream, int version) {
|
||||
var state = ProtoBuf.Serializer.DeserializeWithLengthPrefix<LadderSaveState>(load_stream, ProtoBuf.PrefixStyle.Fixed32);
|
||||
if (version < _version_where_stickied_algos_introduced) {
|
||||
// We want to assign any existing favorite algos to stickied algos upon setting upgrade
|
||||
var favorite_algos_array = state.settings.favorite_algos.favorite_algos.ToArray();
|
||||
state.settings.favorite_algos = new LadderFavoriteAlgosSettings(favorite_algos_array, state.settings.favorite_algos.column_count, favorite_algos_array);
|
||||
}
|
||||
return load(state);
|
||||
}
|
||||
|
||||
LadderSaveState _get_state(LadderVM vm, uint id) {
|
||||
LadderSettings s = vm.get_save_state();
|
||||
if (id != 0) {
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
using bts.api;
|
||||
using bts.api.orderman;
|
||||
using bts.collections;
|
||||
using bts.persist.algo;
|
||||
using bts.shell.orderbook;
|
||||
using bts.utils;
|
||||
using mbts.collections;
|
||||
@@ -72,7 +71,6 @@ namespace mbts.spark {
|
||||
_order_type_visibility = new OrderEntryPanelItemVisibilityVM("Order Type"),
|
||||
_account_position_visibility = new OrderEntryPanelItemVisibilityVM("Account Position"),
|
||||
_day_position_visibility = new OrderEntryPanelItemVisibilityVM("Day Position"),
|
||||
_raw_day_position_visibility = new OrderEntryPanelItemVisibilityVM("Raw Day Position"),
|
||||
_size_preset_visibility = new OrderEntryPanelItemVisibilityVM("Size Presets"),
|
||||
_auto_clear_visibility = new OrderEntryPanelItemVisibilityVM("Auto Clear"),
|
||||
_clear_visibility = new OrderEntryPanelItemVisibilityVM("Clear"),
|
||||
@@ -138,7 +136,7 @@ namespace mbts.spark {
|
||||
c.PropertyChanged -= on_prop_changed;
|
||||
}
|
||||
AccountTuple? account_factory(KeyValuePair<uint, string> kv) => new AccountTuple(kv.Key, kv.Value);
|
||||
AlgoDefinitionVM algo_factory(AlgoData data) => AlgoDefinitionVM.create(_model, data, is_synthetic ? AlgoFormats.Spreader : AlgoFormats.Exchange);
|
||||
AlgoDefinitionVM algo_factory(AlgoData data) => AlgoDefinitionVM.create(_model, data);
|
||||
}
|
||||
|
||||
public event Action result_changed;
|
||||
@@ -240,7 +238,6 @@ namespace mbts.spark {
|
||||
yield return _order_type_visibility;
|
||||
yield return _account_position_visibility;
|
||||
yield return _day_position_visibility;
|
||||
yield return _raw_day_position_visibility;
|
||||
yield return _size_preset_visibility;
|
||||
yield return _clear_visibility;
|
||||
yield return _auto_clear_visibility;
|
||||
@@ -283,7 +280,6 @@ namespace mbts.spark {
|
||||
yield return _order_type_visibility;
|
||||
yield return _account_position_visibility;
|
||||
yield return _day_position_visibility;
|
||||
yield return _raw_day_position_visibility;
|
||||
yield return _size_preset_visibility;
|
||||
yield return _auto_clear_visibility;
|
||||
yield return _clear_visibility;
|
||||
@@ -346,7 +342,6 @@ namespace mbts.spark {
|
||||
_order_type_visibility.show = settings.order_type_visible;
|
||||
_account_position_visibility.show = settings.account_position_visible;
|
||||
_day_position_visibility.show = settings.day_position_visible;
|
||||
_raw_day_position_visibility.show = settings.raw_day_position_visible;
|
||||
_size_preset_visibility.show = settings.size_presets_visible;
|
||||
_clear_visibility.show = settings.clear_visible;
|
||||
_auto_clear_visibility.show = settings.auto_clear_visible;
|
||||
@@ -419,7 +414,6 @@ namespace mbts.spark {
|
||||
clone.order_type_visible = _order_type_visibility.show;
|
||||
clone.account_position_visible = _account_position_visibility.show;
|
||||
clone.day_position_visible = _day_position_visibility.show;
|
||||
clone.raw_day_position_visible = _raw_day_position_visibility.show;
|
||||
clone.size_presets_visible = _size_preset_visibility.show;
|
||||
clone.auto_clear_visible = _auto_clear_visibility.show;
|
||||
clone.clear_visible = _clear_visibility.show;
|
||||
@@ -729,15 +723,12 @@ namespace mbts.spark {
|
||||
readonly IObservableCollection<AlgoDefinitionVM> _algo_definitions;
|
||||
readonly ObservableCollection<FavoriteAlgoItem> _raw_items = new ObservableCollection<FavoriteAlgoItem>();
|
||||
int _column_count = 2;
|
||||
HashSet<AlgoPersistedKey> _sticky_algo_keys;
|
||||
IDisposable _sticky_algos_hook;
|
||||
|
||||
public FavoriteAlgosVM(IObservableCollection<AlgoDefinitionVM> algo_definitions) {
|
||||
_algo_definitions = algo_definitions;
|
||||
items = new ObservableAggregateCollection<FavoriteAlgoItem>(_raw_items, new ObservableCollection<FavoriteAlgoItem> { FavoriteAlgoItem.AddButtonPlaceholderItem });
|
||||
add_item_cmd = new DelegateCommand(() => _raw_items.Add(_item_factory()));
|
||||
remove_item_cmd = new DelegateCommand<FavoriteAlgoItem>(fai => _raw_items.Remove(fai));
|
||||
_dispose = Disposable.Create(() => Disposable.dispose(ref _sticky_algos_hook));
|
||||
}
|
||||
|
||||
public ObservableCollection<FavoriteAlgoItem> items { get; }
|
||||
@@ -754,17 +745,6 @@ namespace mbts.spark {
|
||||
fai.selector.try_select(k);
|
||||
return fai;
|
||||
}));
|
||||
_sticky_algo_keys = new HashSet<AlgoPersistedKey>(value.stickied_algos.Where(k => k.is_persisted).Select(x => new AlgoPersistedKey(x.persisted)));
|
||||
Disposable.replace(ref _sticky_algos_hook,
|
||||
ObservableAutoWrapper.connect_item_hooks(_raw_items, x =>
|
||||
x.selector.subscribe_value().Subscribe(algo => {
|
||||
if (algo != null &&
|
||||
algo.key.is_persisted &&
|
||||
(x.is_sticky = _sticky_algo_keys.Remove(new AlgoPersistedKey(algo.key.persisted))) &&
|
||||
_sticky_algo_keys.Count == 0)
|
||||
//
|
||||
Disposable.dispose(ref _sticky_algos_hook);
|
||||
})));
|
||||
}
|
||||
|
||||
public LadderFavoriteAlgosSettings create() =>
|
||||
@@ -773,13 +753,7 @@ namespace mbts.spark {
|
||||
.Where(k => k.HasValue && k.Value.is_persisted)
|
||||
.Select(k => k.Value)
|
||||
.ToArray(),
|
||||
column_count,
|
||||
_raw_items
|
||||
.Where(fai => fai.is_sticky)
|
||||
.Select(fai => fai.algo_definition?.key ?? fai.selector.desired_key)
|
||||
.Where(k => k.HasValue && k.Value.is_persisted)
|
||||
.Select(k => k.Value)
|
||||
.ToArray());
|
||||
column_count);
|
||||
|
||||
FavoriteAlgoItem _item_factory() =>
|
||||
new FavoriteAlgoItem(_algo_definitions, new ListCollectionView<AlgoDefinitionVM>(_algo_definitions) {
|
||||
@@ -792,7 +766,6 @@ namespace mbts.spark {
|
||||
public static readonly FavoriteAlgoItem AddButtonPlaceholderItem = new _AddButtonPlaceholderItem();
|
||||
|
||||
internal readonly KeyedItemSelector<AlgoDefinitionVM, AlgoKey> selector;
|
||||
bool _is_sticky;
|
||||
|
||||
protected FavoriteAlgoItem() { }
|
||||
|
||||
@@ -803,7 +776,6 @@ namespace mbts.spark {
|
||||
|
||||
public ICollectionView<AlgoDefinitionVM> algo_definitions_view { get; }
|
||||
public AlgoDefinitionVM algo_definition { get => selector.value; set => selector.value = value; }
|
||||
public bool is_sticky { get => _is_sticky; set => Set(ref _is_sticky, value); }
|
||||
|
||||
class _AddButtonPlaceholderItem : FavoriteAlgoItem, ITemplateSelectorKey {
|
||||
public string key => LadderSettingsView.FavoriteAlgoAddButtonTemplateKey;
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
using bts.api;
|
||||
using bts.api.orderman;
|
||||
using bts.collections;
|
||||
using bts.shell;
|
||||
using bts.spark;
|
||||
using bts.utils;
|
||||
using mbts.co2;
|
||||
using mbts.spark.utilities;
|
||||
using mbts.ui;
|
||||
using mbts.ui.shell;
|
||||
using mbts.utils;
|
||||
@@ -34,9 +32,8 @@ namespace mbts.spark {
|
||||
_show_tif, _show_order_type, _show_size_preset, _show_auto_clear, _show_clear, _show_cancel_buys,
|
||||
_show_cancel_sells, _show_cancel_all, _show_zoom;
|
||||
readonly PropertyAutoWrapper<LadderSettings, Brush> _bid_background, _bid_foreground, _ask_background, _ask_foreground;
|
||||
readonly HashSet<AlgoKey> _sticky_algos = new HashSet<AlgoKey>();
|
||||
double _position, _day_position, _raw_day_position;
|
||||
bool _show_account_position, _show_day_position, _show_raw_day_position;
|
||||
double _position, _day_position;
|
||||
bool _show_account_position, _show_day_position;
|
||||
ulong _misc_time = 0;
|
||||
ulong _last_trade_time = 0;
|
||||
IEqualityComparer<double> _equality;
|
||||
@@ -109,10 +106,7 @@ namespace mbts.spark {
|
||||
(_show_cancel_all = bool_prop(nameof(show_cancel_all), s => s.cancel_all_visible)).set,
|
||||
(_show_zoom = bool_prop(nameof(show_zoom), s => s.zoom_slider_visible)).set,
|
||||
s => {
|
||||
if (
|
||||
Set(ref _show_account_position, s.account_position_visible, nameof(show_account_position)) |
|
||||
Set(ref _show_day_position, s.day_position_visible, nameof(show_day_position)) |
|
||||
Set(ref _show_raw_day_position, s.raw_day_position_visible, nameof(show_raw_day_position))) {
|
||||
if (Set(ref _show_account_position, s.account_position_visible, nameof(show_account_position)) | Set(ref _show_day_position, s.day_position_visible, nameof(show_day_position))) {
|
||||
RaisePropertyChanged(nameof(position_display_text));
|
||||
}
|
||||
size_calculator.buttons = s.calculator_buttons;
|
||||
@@ -138,8 +132,8 @@ namespace mbts.spark {
|
||||
//
|
||||
//_window_height = backing(nameof(window_height), settings_manager.subscribe_current().OnDispatcher().Select(s => s.zoom_factor), (scale) => scale * 600);
|
||||
//_window_width = backing(nameof(window_width), settings_manager.subscribe_current().OnDispatcher().Select(s => s.zoom_factor), (scale) => scale * 420);
|
||||
position_click_command = new DelegateCommand(() => size_calculator.value = DoubleHelper.safe_convert_to_uint(Math.Abs(_show_day_position ? day_position : (_show_raw_day_position ? _raw_day_position : position))));
|
||||
send_order_command = new AsyncCommand<(double price, Side? side, MouseButton button)>(send_order_async);
|
||||
position_click_command = new DelegateCommand(() => size_calculator.value = DoubleHelper.safe_convert_to_uint(Math.Abs(_show_day_position ? day_position : position)));
|
||||
send_order_command = new DelegateCommand<(double price, Side? side, MouseButton button)>(send_order);
|
||||
cancel_command = new AsyncCommand<LadderModel.CancelType>(_model.cancel_orders, true);
|
||||
show_book_command = new DelegateCommand(_show_book);
|
||||
show_ticket_command = new DelegateCommand(_show_ticket);
|
||||
@@ -229,25 +223,17 @@ namespace mbts.spark {
|
||||
.SelectManyInSeries(a => _model.order_gateway.on_security_positions(security.Id)
|
||||
.Select(table => table.state.Values.FirstOrDefault(pos => pos.Item2.Account == a, tup => tup.Item2, (bts.api.position.PositionUnitImmutable?)null)));
|
||||
return Disposable.Create(
|
||||
settings.OnDispatcher().Subscribe(s => {
|
||||
_sticky_algos.SetItems(s.favorite_algos.stickied_algos);
|
||||
order_properties_vm.on_favorite_algos(s.favorite_algos);
|
||||
}),
|
||||
settings.OnDispatcher().Subscribe(s => order_properties_vm.on_favorite_algos(s.favorite_algos)),
|
||||
order_properties_vm,
|
||||
position_src.OnDispatcher().Subscribe(pos => {
|
||||
double total, day, raw_day;
|
||||
double total, day;
|
||||
if (pos.HasValue) {
|
||||
total = pos.Value.total_pos();
|
||||
day = pos.Value.day_pos();
|
||||
raw_day = pos.Value.raw_day_pos();
|
||||
} else {
|
||||
total = day = raw_day = double.NaN;
|
||||
total = day = double.NaN;
|
||||
}
|
||||
if ((
|
||||
Set(ref _position, total, nameof(position)) |
|
||||
Set(ref _day_position, day, nameof(day_position)) |
|
||||
Set(ref _raw_day_position, raw_day, nameof(raw_day_position))
|
||||
) && (show_day_position || show_account_position || show_raw_day_position))
|
||||
if ((Set(ref _position, total, nameof(position)) | Set(ref _day_position, day, nameof(day_position))) && (show_day_position || show_account_position))
|
||||
RaisePropertyChanged(nameof(position_display_text));
|
||||
}));
|
||||
}
|
||||
@@ -298,7 +284,6 @@ namespace mbts.spark {
|
||||
public double settlement => _settlement.Value;
|
||||
public double position => _position;
|
||||
public double day_position => _day_position;
|
||||
public double raw_day_position => _raw_day_position;
|
||||
public ulong volume => _volume.Value;
|
||||
public bool auto_clear { get => _auto_clear.Value; set => _auto_clear.set(value); }
|
||||
public CalculatorLogic size_calculator { get; } = new CalculatorLogic(0);
|
||||
@@ -325,7 +310,6 @@ namespace mbts.spark {
|
||||
public bool show_order_type => _show_order_type.view;
|
||||
public bool show_account_position => _show_account_position;
|
||||
public bool show_day_position => _show_day_position;
|
||||
public bool show_raw_day_position => _show_raw_day_position;
|
||||
public bool show_size_preset => _show_size_preset.view;
|
||||
public bool show_auto_clear => _show_auto_clear.view;
|
||||
public bool show_clear => _show_clear.view;
|
||||
@@ -337,18 +321,12 @@ namespace mbts.spark {
|
||||
public double zoom_factor { get => _zoom_factor.Value; set => _zoom_factor.set(value); }
|
||||
public string position_display_text {
|
||||
get {
|
||||
string rv = null;
|
||||
if (show_day_position)
|
||||
rv = SparkUtils.fmt_position(day_position);
|
||||
if (show_raw_day_position) {
|
||||
var raw_day_text = SparkUtils.fmt_position(raw_day_position);
|
||||
rv = rv == null ? raw_day_text : $"{rv} / {raw_day_text}";
|
||||
}
|
||||
if (show_account_position) {
|
||||
var acc_text = SparkUtils.fmt_position(position);
|
||||
rv = rv == null ? SparkUtils.fmt_position(position) : $"{rv} ({acc_text})";
|
||||
}
|
||||
return rv ?? "";
|
||||
if (show_day_position) {
|
||||
string day_pos_text = SparkUtils.fmt_position(day_position);
|
||||
return show_account_position ? $"{day_pos_text} ({SparkUtils.fmt_position(position)})" : day_pos_text;
|
||||
} else if (show_account_position) {
|
||||
return SparkUtils.fmt_position(position);
|
||||
} else return string.Empty;
|
||||
}
|
||||
}
|
||||
//public double window_height { get => _window_height.Value; set => _window_height.set(value); }
|
||||
@@ -422,19 +400,20 @@ namespace mbts.spark {
|
||||
settings_invalidated?.Invoke();
|
||||
}
|
||||
|
||||
public Task send_order_async((double price, Side? side, MouseButton button) args) {
|
||||
if (order_properties_vm != null && args.button != MouseButton.Middle) {
|
||||
var size_from_alternate = args.button == MouseButton.Right;
|
||||
if (!DoubleHelper.IsNaNOrInfinity(args.price) && args.side.HasValue && !HasErrors && !order_properties_vm.HasErrors && (!size_from_alternate || current_settings.alternate_order_size > 0)) {
|
||||
if (!size_from_alternate && (!size_calculator.value.HasValue || size_calculator.value.Value == 0)) {
|
||||
size_calculator.flash_error();
|
||||
} else {
|
||||
uint size = size_from_alternate ? current_settings.alternate_order_size : (uint)Math.Abs(size_calculator.value ?? 0);
|
||||
return send_order_async(args.side.Value, size, args.price);
|
||||
}
|
||||
}
|
||||
public void send_order((double price, Side? side, MouseButton button) args) {
|
||||
if (order_properties_vm == null || args.button == MouseButton.Middle)
|
||||
return;
|
||||
var size_from_alternate = args.button == MouseButton.Right;
|
||||
if (DoubleHelper.IsNaNOrInfinity(args.price) || !args.side.HasValue || HasErrors || order_properties_vm.HasErrors || (size_from_alternate && current_settings.alternate_order_size == 0)) {
|
||||
return;
|
||||
}
|
||||
if (!size_from_alternate && (!size_calculator.value.HasValue || size_calculator.value.Value == 0)) {
|
||||
size_calculator.flash_error();
|
||||
}
|
||||
else {
|
||||
uint size = size_from_alternate ? current_settings.alternate_order_size : (uint)Math.Abs(size_calculator.value ?? 0);
|
||||
send_order_async(args.side.Value, size, args.price).ignore();
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task send_order_async(Side side, uint size, double price) {
|
||||
@@ -454,13 +433,6 @@ namespace mbts.spark {
|
||||
await _model.order_gateway.send_order(order, price, size);
|
||||
if (auto_clear)
|
||||
size_calculator.value = 0;
|
||||
var fav_algo = order_properties_vm.selected_favorite_algo_definition;
|
||||
if (fav_algo != null && !_sticky_algos.Contains(fav_algo.key) && !order_properties_vm.try_pop_selected_algo_definition()) {
|
||||
var key = new AlgoKey(OrderDataHelper.get_default_algo(OrderDataHelper.get_algo_format(security.ContractCase)));
|
||||
if (order_properties_vm.try_get_algo_definition(key, out var r)) {
|
||||
order_properties_vm.selected_algo_definition = r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _update_header() {
|
||||
|
||||
@@ -805,7 +805,6 @@
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="c0"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="c1"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button Command="{Binding DataContext.remove_item_cmd, RelativeSource={RelativeSource AncestorType=ItemsControl}}" CommandParameter="{Binding}" Style="{StaticResource RoundCloseButton}"/>
|
||||
<ComboBox Grid.Column="1" ItemsSource="{Binding algo_definitions_view}" SelectedItem="{Binding algo_definition}">
|
||||
@@ -815,7 +814,6 @@
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
<CheckBox Grid.Column="2" VerticalAlignment="Center" VerticalContentAlignment="Center" IsChecked="{Binding is_sticky}"> Stay Enabled?</CheckBox>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</u:KeyedTemplateSelector.Default>
|
||||
@@ -825,7 +823,6 @@
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="c0"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="c1"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button Grid.Column="1" HorizontalAlignment="Stretch" Command="{Binding DataContext.add_item_cmd, RelativeSource={RelativeSource AncestorType=ItemsControl}}">Add</Button>
|
||||
</Grid>
|
||||
|
||||
@@ -476,7 +476,6 @@
|
||||
</converters:MultiValueConverterPipe>
|
||||
</MultiBinding.Converter>
|
||||
<Binding Path="show_day_position"/>
|
||||
<Binding Path="show_raw_day_position"/>
|
||||
<Binding Path="show_account_position"/>
|
||||
</MultiBinding>
|
||||
</Button.Visibility>
|
||||
@@ -487,7 +486,6 @@
|
||||
<MultiBinding Converter="{x:Static spark:LadderView.PositionToolTipConverter}">
|
||||
<Binding Path="order_properties_vm.selected_account"/>
|
||||
<Binding Path="show_day_position"/>
|
||||
<Binding Path="show_raw_day_position"/>
|
||||
<Binding Path="show_account_position"/>
|
||||
</MultiBinding>
|
||||
</TextBlock.Text>
|
||||
|
||||
@@ -18,60 +18,32 @@ namespace mbts.spark {
|
||||
MaxZoomFactor = 1.5;
|
||||
|
||||
public static IMultiValueConverter PositionToolTipConverter =
|
||||
ValueConverter.create<AccountTuple?, bool, bool, bool, string>((account, show_day_pos, show_raw_day_pos, show_total_pos) => {
|
||||
const string day_pos_text = "day", raw_day_pos_text = "raw day", total_pos_text = "total";
|
||||
ValueConverter.create<AccountTuple?, bool, bool, string>((account, show_day_pos, show_total_pos) => {
|
||||
const string day_pos_text = "day", total_pos_text = "total";
|
||||
if (!account.HasValue)
|
||||
return string.Empty;
|
||||
/// Note: 'day,' and 'raw day,' are interchangeable wrt the string format, 'total' however is treated differently.
|
||||
/// Different Formats:
|
||||
// No parts are visible:
|
||||
// null
|
||||
//
|
||||
// 1 part 'foo' is visible:
|
||||
// "<account> foo position
|
||||
// click to copy to size"
|
||||
//
|
||||
// 2 parts 'day' and 'raw day' are visible:
|
||||
// "<account> 'day / raw day' position
|
||||
// click to copy day to size"
|
||||
//
|
||||
// 2 parts 'day' and 'total' are visible:
|
||||
// "<account> 'day (total)' position
|
||||
// click to copy day to size"
|
||||
//
|
||||
|
||||
string primary_pos = null;
|
||||
// part count is incremented in the 'add_part' method
|
||||
int part_count = 0;
|
||||
var sb = new StringBuilderEx();
|
||||
|
||||
if (show_day_pos) add_part(day_pos_text, " / ");
|
||||
if (show_raw_day_pos) add_part(raw_day_pos_text, " / ");
|
||||
if (show_total_pos) add_part(total_pos_text, " (", ")");
|
||||
//
|
||||
if (part_count > 1) {
|
||||
// This is a complex expression (showing more than 1 value) - wrap it in single quotes.
|
||||
// Explicitly state which value will be copied to the size input.
|
||||
sb.Insert(0, "'");
|
||||
sb.Add("'");
|
||||
sb.Add(" position\n(click to copy ", primary_pos, " to size)");
|
||||
} else {
|
||||
// Only showing 1 value, no need for single quotes,
|
||||
// nor an explicit statement that indicates which value will be copied to the size input.
|
||||
sb.Add(" position\n(click to copy to size)");
|
||||
}
|
||||
return sb.ToString();
|
||||
|
||||
void add_part(string part_name, string separator = null, string separator_suffix = null) {
|
||||
if (part_count++ > 0) {
|
||||
sb.Add(separator);
|
||||
sb.Add(part_name);
|
||||
sb.Add(separator_suffix);
|
||||
var sb = new StringBuilderEx(account.Value.name);
|
||||
string primary_pos;
|
||||
if (show_day_pos) {
|
||||
primary_pos = day_pos_text;
|
||||
if (show_total_pos) {
|
||||
sb.Add(" '", primary_pos, " ");
|
||||
sb.Add("(", total_pos_text, ")' ");
|
||||
} else {
|
||||
primary_pos = part_name;
|
||||
sb.Add($"{account.Value.name} {part_name}");
|
||||
sb.Add(" ", primary_pos, " ");
|
||||
}
|
||||
} else if (show_total_pos) {
|
||||
primary_pos = total_pos_text;
|
||||
sb.Add(" ", primary_pos, " ");
|
||||
} else {
|
||||
// unexpected code path (the button for which this tooltip is destined should be invisible in this case), but no need to throw an exception.
|
||||
return string.Empty;
|
||||
}
|
||||
sb.Add("position\n(click to copy ");
|
||||
if (show_day_pos && show_total_pos)
|
||||
sb.Add(primary_pos, " position ");
|
||||
sb.Add("to size)");
|
||||
return sb.ToString();
|
||||
});
|
||||
|
||||
internal LadderView(LadderVM vm) {
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
using bts.api;
|
||||
using bts.settings;
|
||||
using bts.api.orderman;
|
||||
using bts.spark;
|
||||
using mbts.persist;
|
||||
using mbts.spark;
|
||||
using mbts.spark.filters;
|
||||
using mbts.ui;
|
||||
using mbts.ui.settings;
|
||||
using mbts.ui.spark;
|
||||
using ProtoBuf;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading.Tasks;
|
||||
@@ -40,14 +40,13 @@ namespace bts.shell {
|
||||
_bus.post(SparkTags.ShowWindow, w);
|
||||
}
|
||||
|
||||
IShellWindow _create(PAndLStartupData state) =>
|
||||
new PAndLWindow(
|
||||
_orders_model,
|
||||
this,
|
||||
_bus,
|
||||
state,
|
||||
SparkCommands.ShowPAndL.icon,
|
||||
_settings_hub.create_pandl_settings_manager(state.grid));
|
||||
IShellWindow _create(PAndLStartupData state) {
|
||||
var settings_manager = new WindowSettingsManager<GridSettings>(_settings_hub,
|
||||
state.grid,
|
||||
get: us => us.pnl_grid,
|
||||
set: (us, gs) => us.with(provided_pnl_gridsettings: gs));
|
||||
return new PAndLWindow(_orders_model, this, _bus, state, SparkCommands.ShowPAndL.icon, settings_manager);
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
public class PAndLStartupData {
|
||||
@@ -82,7 +81,7 @@ namespace bts.shell {
|
||||
[ProtoMember(25)]
|
||||
public GridSettings grid;
|
||||
|
||||
public static class DefaultColumns {
|
||||
static class DefaultColumns {
|
||||
|
||||
public static GridColumnSettings account => new GridColumnSettings(col_id(Columns.Account), DefaultColumnHeaders.Account, width: DefaultColumnWidths.Account);
|
||||
public static GridColumnSettings exchange => new GridColumnSettings(col_id(Columns.Exchange), DefaultColumnHeaders.Exchange, false, width: DefaultColumnWidths.Exchange);
|
||||
@@ -93,12 +92,10 @@ namespace bts.shell {
|
||||
public static GridColumnSettings start_pnl => new GridColumnSettings(col_id(Columns.StartPNL), DefaultColumnHeaders.StartPnL, false, width: DefaultColumnWidths.PnL_Generic, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings day_long_pnl => new GridColumnSettings(col_id(Columns.DayLong), DefaultColumnHeaders.DayLong, width: DefaultColumnWidths.PnL_Generic, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings day_short_pnl => new GridColumnSettings(col_id(Columns.DayShort), DefaultColumnHeaders.DayShort, width: DefaultColumnWidths.PnL_Generic, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings raw_day_net_position => new GridColumnSettings(col_id(Columns.RawDayPos), DefaultColumnHeaders.RawDayPos, width: DefaultColumnWidths.PnL_Generic, horizontal_alignment: System.Windows.HorizontalAlignment.Right, is_visible: false);
|
||||
public static GridColumnSettings day_pnl => new GridColumnSettings(col_id(Columns.DayPNL), DefaultColumnHeaders.DayPnL, width: DefaultColumnWidths.PnL_Generic, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings total_net_pos => new GridColumnSettings(col_id(Columns.TotalNetPos), DefaultColumnHeaders.TotalNetPos, false, width: DefaultColumnWidths.Total_Net_Pos, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings total_pnl => new GridColumnSettings(col_id(Columns.TotalPNL), DefaultColumnHeaders.TotalPnL, false, width: DefaultColumnWidths.PnL_Generic, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings pnl_price => new GridColumnSettings(col_id(Columns.PNLPrice), DefaultColumnHeaders.PnLPrice, width: DefaultColumnWidths.PnL_Generic, horizontal_alignment: System.Windows.HorizontalAlignment.Right);
|
||||
public static GridColumnSettings has_day_trades => new GridColumnSettings(col_id(Columns.TradedToday), "Traded Today", width: 100, horizontal_alignment: System.Windows.HorizontalAlignment.Center);
|
||||
|
||||
public static IEnumerable<SortingCriteria> default_sorting_criterion {
|
||||
get {
|
||||
@@ -120,12 +117,10 @@ namespace bts.shell {
|
||||
yield return start_pnl;
|
||||
yield return day_long_pnl;
|
||||
yield return day_short_pnl;
|
||||
yield return raw_day_net_position;
|
||||
yield return day_pnl;
|
||||
yield return total_net_pos;
|
||||
yield return total_pnl;
|
||||
yield return pnl_price;
|
||||
yield return has_day_trades;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,8 +145,6 @@ namespace bts.shell {
|
||||
public UIntMinMaxState day_long_pos;
|
||||
[ProtoMember(8)]
|
||||
public UIntMinMaxState day_short_pos;
|
||||
[ProtoMember(16)]
|
||||
public DoubleMinMaxState raw_day_net_pos;
|
||||
[ProtoMember(9)]
|
||||
public DoubleMinMaxState day_pnl;
|
||||
[ProtoMember(10)]
|
||||
@@ -169,11 +162,7 @@ namespace bts.shell {
|
||||
public bool[] has_day_trades;
|
||||
}
|
||||
|
||||
// migration rules
|
||||
[Export(typeof(IPAndLGridSettingsMigrationRule))]
|
||||
class AddMissingColumnsMigrationRule : IPAndLGridSettingsMigrationRule {
|
||||
public bool apply(GridSettings settings, out GridSettings new_settings) =>
|
||||
settings.add_missing_columns(PAndLStartupData.DefaultGridSettings.columns, out new_settings);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -4,7 +4,6 @@ using bts.api.orderman;
|
||||
using bts.collections;
|
||||
using bts.spark;
|
||||
using mbts;
|
||||
using mbts.co2;
|
||||
using mbts.collections;
|
||||
using mbts.spark;
|
||||
using mbts.ui;
|
||||
@@ -14,7 +13,6 @@ using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reactive.Subjects;
|
||||
|
||||
namespace bts.shell {
|
||||
interface INotificationService {
|
||||
@@ -24,48 +22,25 @@ namespace bts.shell {
|
||||
IDisposable link_to_activity(INotificationPredicate<Activity> toast_notification_predicate);
|
||||
IDisposable link_to_order(IPredicate<OrderSecurity> predicate, SoundNotification sound);
|
||||
IDisposable link_to_order(INotificationPredicate<OrderSecurity> toast_notification_predicate);
|
||||
// Note: security criteria should not be incorporated in the predicate's 'trigger' impl.
|
||||
// It is easier/cleaner if the INotificationService incorporates the criteria internally.
|
||||
IDisposable link_to_trades(SecurityCriteria security_criteria, IPredicate<TradeInfo> predicate, SoundNotification sound);
|
||||
}
|
||||
|
||||
[Export(typeof(INotificationService))]
|
||||
partial class NotificationService : INotificationService, IDisposable {
|
||||
// activity notifications
|
||||
readonly NotificationProcessor<Activity> _activity_notifications;
|
||||
// order notifications
|
||||
readonly NotificationProcessor<OrderSecurity> _order_notifications;
|
||||
// trade notifications
|
||||
readonly NotificationProcessor<TradeInfo> _trade_notifications;
|
||||
readonly MDConsumer _consumer;
|
||||
readonly Subject<IEnumerable<SecurityCriteria>> _trade_criteria_subject = new Subject<IEnumerable<SecurityCriteria>>();
|
||||
readonly RefCountMap<SecurityCriteria> _trade_criteria_refs = new RefCountMap<SecurityCriteria>();
|
||||
readonly object _trade_lock = new object();
|
||||
//
|
||||
readonly DataCache _data_cache = new DataCache();
|
||||
readonly LoggerContext _log = new LoggerContext(Log.Ui, "Notification Service ", null);
|
||||
|
||||
IDisposable _dispose;
|
||||
|
||||
[ImportingConstructor]
|
||||
public NotificationService(IBus bus, INotifier notifier, OrdersModel orders_model, MDConsumer consumer) {
|
||||
public NotificationService(IBus bus, INotifier notifier, OrdersModel orders_model) {
|
||||
this.notifier = notifier;
|
||||
_activity_notifications = new NotificationProcessor<Activity>(notifier);
|
||||
_order_notifications = new NotificationProcessor<OrderSecurity>(notifier);
|
||||
_trade_notifications = new NotificationProcessor<TradeInfo>(notifier);
|
||||
_consumer = consumer;
|
||||
_dispose = Disposable.Create(
|
||||
bus.subscribe(SparkTags.LogActivity).Subscribe(_on_activity),
|
||||
orders_model.on_orders.Subscribe(_on_orders),
|
||||
TradesSource.create(
|
||||
consumer,
|
||||
bus.subscribe(SparkTags.SecuritiesTag).BufferLatest(TimeSpan.FromSeconds(2))
|
||||
.Table(s => s.security.Id)
|
||||
.Where(t => t.has_changes())
|
||||
.Select(m => MappedDictionary.create(m.state, smd => smd.security)),
|
||||
_trade_criteria_subject,
|
||||
TimeSpan.FromSeconds(0.2))
|
||||
.Subscribe(_trade_notifications.process_items));
|
||||
orders_model.on_orders.Subscribe(_on_orders)
|
||||
);
|
||||
}
|
||||
|
||||
[Export(typeof(INotificationServiceDataCache))]
|
||||
@@ -76,42 +51,8 @@ namespace bts.shell {
|
||||
public IDisposable link_to_activity(INotificationPredicate<Activity> toast_notification_predicate) => _activity_notifications.link(toast_notification_predicate);
|
||||
public IDisposable link_to_order(IPredicate<OrderSecurity> predicate, SoundNotification sound) => _order_notifications.link(predicate, sound);
|
||||
public IDisposable link_to_order(INotificationPredicate<OrderSecurity> toast_notification_predicate) => _order_notifications.link(toast_notification_predicate);
|
||||
|
||||
public IDisposable link_to_trades(SecurityCriteria security_criteria, IPredicate<TradeInfo> predicate, SoundNotification sound) {
|
||||
if (predicate == null) {
|
||||
_log.warn("cannot create trade alert sound because the specified predicate is null.");
|
||||
return Disposable.empty;
|
||||
}
|
||||
if (!security_criteria.is_defined()) {
|
||||
// there are simply too many trades to support universal-product predicates, therefor a product filter must be specified (aka SecrityCriteria).
|
||||
_log.warn("cannot create trade alert sound because the specified security criteria is not defined.");
|
||||
return Disposable.empty;
|
||||
}
|
||||
// This criteria predicate may be redundant, but it safe guards against a case
|
||||
// where the criteria has not been incorporated into the predicate,
|
||||
// as there doesn't seem to be a great way to require this in the interface.
|
||||
// Note: verifying the criteria twice (instead of once) should not hurt on perf at all.
|
||||
predicate = predicate.and<TradeInfo>(PredicateBuilder.create<TradeInfo>(t => ContractPickerModel.security_in_criteria(security_criteria, t.security)));
|
||||
var link = _trade_notifications.link(predicate, sound);
|
||||
lock (_trade_lock) {
|
||||
if (_trade_criteria_refs.increment_adds_key(security_criteria))
|
||||
_reset_trade_readers();
|
||||
}
|
||||
return Disposable.Create(() => {
|
||||
Disposable.dispose(ref link);
|
||||
lock (_trade_lock) {
|
||||
if (_trade_criteria_refs.decrement_removes_key(security_criteria))
|
||||
_reset_trade_readers();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void Dispose() => Disposable.dispose(ref _dispose);
|
||||
|
||||
/// should only call this within a locked scope (<see cref="_trade_lock"/>)
|
||||
void _reset_trade_readers() =>
|
||||
_trade_criteria_subject.OnNext(_trade_criteria_refs.Keys);
|
||||
|
||||
void _on_activity(Activity activity) =>
|
||||
_activity_notifications.process_item(activity);
|
||||
|
||||
@@ -187,31 +128,6 @@ namespace bts.shell {
|
||||
if (sounds.Count > 0 || toast.Count > 0)
|
||||
_notifier.notify(toast, sounds);
|
||||
}
|
||||
|
||||
public void process_items(IEnumerable<T> source) {
|
||||
var sounds = new HashSet<SoundNotification>();
|
||||
var toast = new HashSet<IToastNotification>();
|
||||
var temp_sound_triggers = _sound_trigger_refs;
|
||||
if (temp_sound_triggers.Count > 0) {
|
||||
foreach (var v in source) {
|
||||
foreach (var trigger_kv in temp_sound_triggers) {
|
||||
if (trigger_kv.Key.trigger(v))
|
||||
sounds.AddRange(trigger_kv.Value.Keys);
|
||||
}
|
||||
}
|
||||
}
|
||||
var temp_toast_triggers = _toast_trigger_refs;
|
||||
if (temp_toast_triggers.Count > 0) {
|
||||
foreach (var v in source) {
|
||||
foreach (var trigger_kv in temp_toast_triggers) {
|
||||
if (trigger_kv.Key.trigger(v) is IToastNotification tn && tn.pass)
|
||||
toast.Add(tn);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (sounds.Count > 0 || toast.Count > 0)
|
||||
_notifier.notify(toast, sounds);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
using bts.utils;
|
||||
using mbts;
|
||||
using mbts.ui.utility;
|
||||
using ProtoBuf;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Text;
|
||||
|
||||
namespace bts.shell {
|
||||
enum LicenseType {
|
||||
@@ -31,7 +33,7 @@ namespace bts.shell {
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
class SoundNotification : IEquatable<SoundNotification> {
|
||||
class SoundNotification {
|
||||
SystemSound? _system_sound;
|
||||
string _uri_sound;
|
||||
|
||||
@@ -79,19 +81,6 @@ namespace bts.shell {
|
||||
[ProtoMember(2)]
|
||||
public string uri_sound { get => _uri_sound; set => _set_ref_type(ref _uri_sound, value); }
|
||||
|
||||
public bool Equals(SoundNotification other) =>
|
||||
other != null &&
|
||||
_system_sound == other._system_sound &&
|
||||
_uri_sound == other._uri_sound;
|
||||
|
||||
public override bool Equals(object obj) =>
|
||||
Equals(obj as SoundNotification);
|
||||
|
||||
public override int GetHashCode() =>
|
||||
EqualityComparer.auto_phase(
|
||||
_system_sound.GetHashCode(),
|
||||
_uri_sound?.GetHashCode() ?? 0);
|
||||
|
||||
void _set<T>(ref T? field, T? value) where T : struct {
|
||||
if (value != null) {
|
||||
_clear_values();
|
||||
|
||||
@@ -631,54 +631,25 @@ namespace mbts.spark.utilities {
|
||||
return def;
|
||||
}
|
||||
}
|
||||
|
||||
static CustomOrderDefinition.CustomOneofCase[] algo_config(
|
||||
Func<bts.config.AlgoPermissions, List<string>> selector)
|
||||
{
|
||||
var conf = mbts.config.Configuration.get<bts.config.AlgoPermissions>();
|
||||
return selector(conf).Select(x =>
|
||||
{
|
||||
return (CustomOrderDefinition.CustomOneofCase)System.Enum.Parse(typeof(CustomOrderDefinition.CustomOneofCase), x, true);
|
||||
}).ToArray();
|
||||
}
|
||||
|
||||
public static readonly CustomOrderDefinition.CustomOneofCase[]
|
||||
|
||||
// exchange_order_types are the types which can be submitted on an exchange security
|
||||
exchange_order_types = new[] {
|
||||
CustomOrderDefinition.CustomOneofCase.Market,
|
||||
CustomOrderDefinition.CustomOneofCase.Limit,
|
||||
CustomOrderDefinition.CustomOneofCase.Stop,
|
||||
CustomOrderDefinition.CustomOneofCase.StopLimit,
|
||||
CustomOrderDefinition.CustomOneofCase.GlbxIceberg,
|
||||
CustomOrderDefinition.CustomOneofCase.Iceberg,
|
||||
CustomOrderDefinition.CustomOneofCase.Lean,
|
||||
CustomOrderDefinition.CustomOneofCase.Join,
|
||||
CustomOrderDefinition.CustomOneofCase.Cover,
|
||||
CustomOrderDefinition.CustomOneofCase.Top,
|
||||
CustomOrderDefinition.CustomOneofCase.Offset,
|
||||
CustomOrderDefinition.CustomOneofCase.Splitter,
|
||||
CustomOrderDefinition.CustomOneofCase.Scratch,
|
||||
CustomOrderDefinition.CustomOneofCase.Prorata,
|
||||
},
|
||||
spreader_order_types = new[] {
|
||||
CustomOrderDefinition.CustomOneofCase.Spreader,
|
||||
CustomOrderDefinition.CustomOneofCase.Iceberg,
|
||||
CustomOrderDefinition.CustomOneofCase.Lean,
|
||||
CustomOrderDefinition.CustomOneofCase.Join,
|
||||
CustomOrderDefinition.CustomOneofCase.Cover,
|
||||
CustomOrderDefinition.CustomOneofCase.Top,
|
||||
CustomOrderDefinition.CustomOneofCase.Splitter,
|
||||
CustomOrderDefinition.CustomOneofCase.Scratch,
|
||||
CustomOrderDefinition.CustomOneofCase.Prorata,
|
||||
},
|
||||
attachable_order_types = new[] {
|
||||
CustomOrderDefinition.CustomOneofCase.Iceberg,
|
||||
CustomOrderDefinition.CustomOneofCase.Lean,
|
||||
CustomOrderDefinition.CustomOneofCase.Join,
|
||||
CustomOrderDefinition.CustomOneofCase.Cover,
|
||||
CustomOrderDefinition.CustomOneofCase.Top,
|
||||
CustomOrderDefinition.CustomOneofCase.Offset,
|
||||
CustomOrderDefinition.CustomOneofCase.Scratch,
|
||||
CustomOrderDefinition.CustomOneofCase.Prorata,
|
||||
},
|
||||
outright_order_types = new[] {
|
||||
CustomOrderDefinition.CustomOneofCase.Market,
|
||||
CustomOrderDefinition.CustomOneofCase.Limit,
|
||||
CustomOrderDefinition.CustomOneofCase.Stop,
|
||||
CustomOrderDefinition.CustomOneofCase.StopLimit,
|
||||
CustomOrderDefinition.CustomOneofCase.GlbxIceberg,
|
||||
};
|
||||
exchange_order_types = algo_config(x => x.exchange_order_types),
|
||||
spreader_order_types = algo_config(x => x.spreader_order_types),
|
||||
attachable_order_types = algo_config(x => x.attachable_order_types),
|
||||
outright_order_types = algo_config(x => x.outright_order_types);
|
||||
|
||||
public static readonly CustomOrderDefinition.CustomOneofCase[] all_order_types = exchange_order_types.Union(spreader_order_types).ToArray();
|
||||
|
||||
public static void copy_values(CustomOrderDefinitionImmutable source, ref CustomOrderDefinition target) =>
|
||||
|
||||
@@ -60,7 +60,6 @@ namespace mbts.spark {
|
||||
readonly ListCollectionView<AlgoDefinitionVM> _algo_definitions_view, _favorite_algo_definitions_view;
|
||||
readonly TableUpdateBuffer<AlgoData, AlgoKey> _definitions_buffer = new TableUpdateBuffer<AlgoData, AlgoKey>(d => d.key);
|
||||
readonly ObservableAggregateCollection<AlgoParamRowVM> _algo_param_rows = new ObservableAggregateCollection<AlgoParamRowVM>();
|
||||
readonly AlgoDefinitionVM the_anchor_algo_def_vm;
|
||||
IReadOnlyDictionary<AlgoKey, AlgoDefinitionMetadata> _algo_metadata = DictionaryHelper.empty<AlgoKey, AlgoDefinitionMetadata>();
|
||||
IReadOnlyDictionary<AlgoKey, int> _favorite_algos_index = DictionaryHelper.empty<AlgoKey, int>();
|
||||
IDisposable _algo_param_rows_hook;
|
||||
@@ -69,7 +68,6 @@ namespace mbts.spark {
|
||||
|
||||
public OrderPropertiesEditorVM(OrdersModel model, IObservable<SecurityMD> security_md_source, IObservable<InlineOrderProperties> order_properties) {
|
||||
_model = model;
|
||||
the_anchor_algo_def_vm = AlgoDefinitionVM.create(_model, OrderDataHelper.Defaults.AlgoData(CustomOrderDefinition.CustomOneofCase.Limit));
|
||||
var security_source = security_md_source.Select(smd => smd.security);
|
||||
_security = backing(nameof(security), security_source, SecurityDataHelper.Defaults.SecurityImmutable);
|
||||
security_source = _security.observe_current(true);
|
||||
@@ -239,15 +237,11 @@ namespace mbts.spark {
|
||||
|
||||
void _selected_algo_definition_Changed(AlgoDefinitionVM old_value, AlgoDefinitionVM new_value, SelectionReason reason) {
|
||||
Disposable.dispose(ref _algo_param_rows_hook);
|
||||
if (old_value != null)
|
||||
// Taha feels that we should always revert to a Limit order either when the user explicity unchecks a sticky button
|
||||
// or when a non-sticky button is pressed. Rather than completely remove the stack at this time (b/c it may be come
|
||||
// back in the future), let's just always push a specfic def_vm.key onto it [Limit] ;)
|
||||
//_selected_algo_stack.Push(old_value.key);
|
||||
_selected_algo_stack.Push(the_anchor_algo_def_vm.key);
|
||||
if (new_value != null) {
|
||||
if (use_algo_param_grid)
|
||||
_algo_param_rows_hook = _connect_algo_param_rows(new_value, _algo_param_rows);
|
||||
if (reason != SelectionReason.Explicit)
|
||||
_selected_algo_stack.Push(new_value.key);
|
||||
}
|
||||
if (open_algo_popup_on_select) {
|
||||
if (reason == SelectionReason.UserSelectedNewValue && new_value?.algo_root is IAlgoVM ar && ar.can_expand)
|
||||
@@ -283,18 +277,6 @@ namespace mbts.spark {
|
||||
get => _selected_algo_definition.selected_item;
|
||||
set => _selected_algo_definition.selected_item = value;
|
||||
}
|
||||
|
||||
public bool try_get_algo_definition(AlgoKey a, out AlgoDefinitionVM result) =>
|
||||
_algo_definitions.TryGetValue(a, out result);
|
||||
|
||||
public bool try_pop_selected_algo_definition() {
|
||||
if (_selected_algo_stack.TryPop(out AlgoKey k) && _algo_definitions.TryGetValue(k, out AlgoDefinitionVM vm)) {
|
||||
_selected_algo_definition.select_explicit(vm);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public AlgoDefinitionVM selected_favorite_algo_definition {
|
||||
get => selected_algo_definition?.is_favorite ?? false ? selected_algo_definition : null;
|
||||
set {
|
||||
@@ -371,7 +353,6 @@ namespace mbts.spark {
|
||||
d.favorite_index = _get_favorite_algo_index(d.key);
|
||||
}
|
||||
favorite_algos_column_count = value.column_count;
|
||||
RaisePropertyChanged(nameof(selected_favorite_algo_definition));
|
||||
}
|
||||
|
||||
static IDisposable _connect_algo_param_rows(AlgoDefinitionVM def, ObservableAggregateCollection<AlgoParamRowVM> rows) =>
|
||||
@@ -390,4 +371,4 @@ namespace mbts.spark {
|
||||
algo_definitions_view.Refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
using bts.api;
|
||||
using bts.api.orderman;
|
||||
using bts.co2;
|
||||
using bts.spark;
|
||||
using bts.utils;
|
||||
using mbts.spark.utilities;
|
||||
@@ -329,13 +328,5 @@ namespace mbts.spark {
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
public static AggressorSide to_aggressor(this Side side) {
|
||||
switch (side) {
|
||||
case Side.Buy: return AggressorSide.Buy;
|
||||
case Side.Sell: return AggressorSide.Sell;
|
||||
default: return AggressorSide.None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -61,17 +61,6 @@ namespace mbts {
|
||||
}
|
||||
}
|
||||
|
||||
public static DoublePredicate create(PredicateTypeOneofCase type, double min, double max) {
|
||||
var rv = new DoublePredicate();
|
||||
switch (type) {
|
||||
case PredicateTypeOneofCase.GreaterThan: rv.greater_than = min; break;
|
||||
case PredicateTypeOneofCase.LessThan: rv.less_than = max; break;
|
||||
case PredicateTypeOneofCase.InsideRange: rv.inside_range = new DoubleRange(min, max); break;
|
||||
case PredicateTypeOneofCase.OutsideRange: rv.outside_range = new DoubleRange(min, max); break;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
public IPredicate<double> create() {
|
||||
switch (predicate_type_case) {
|
||||
case PredicateTypeOneofCase.GreaterThan: return PredicateBuilder.greater_than(_greater_than.Value);
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
|
||||
namespace mbts {
|
||||
interface IPredicate {
|
||||
@@ -35,15 +34,9 @@ namespace mbts {
|
||||
public static IPredicate<T> greater_than<T>(T value) =>
|
||||
new GreaterThanTrigger<T>(value, Comparer<T>.Default);
|
||||
|
||||
public static IPredicate<T> greater_than_or_equal_to<T>(T value) =>
|
||||
new GreaterThanOrEqualToTrigger<T>(value, Comparer<T>.Default);
|
||||
|
||||
public static IPredicate<T> less_than<T>(T value) =>
|
||||
greater_than(value).negate();
|
||||
|
||||
public static IPredicate<T> less_than_or_equal_to<T>(T value) =>
|
||||
greater_than_or_equal_to(value).negate();
|
||||
|
||||
public static IPredicate<T> inside_range<T>(T minimum, T maximum) =>
|
||||
greater_than(minimum).and<T>(less_than(maximum));
|
||||
|
||||
@@ -74,14 +67,6 @@ namespace mbts {
|
||||
public static IPredicate<T> not_in_set<T>(ISet<T> set) =>
|
||||
in_set(set).negate();
|
||||
|
||||
public static IPredicate<T> and_safe<T>(this IPredicate<T> first, IPredicate<T> second) {
|
||||
if (first == null)
|
||||
return second;
|
||||
if (second == null)
|
||||
return first;
|
||||
return and<T>(first, second);
|
||||
}
|
||||
|
||||
public static IPredicate<T> and<T>(this IPredicate<T> first, IPredicate<T> second) =>
|
||||
new AndTrigger<T>(first, second);
|
||||
|
||||
@@ -103,7 +88,7 @@ namespace mbts {
|
||||
public static IPredicate<T> state_transition<T>(Func<T, bool> predicate) {
|
||||
// avoid false-positive in the first callback
|
||||
bool last_result = true;
|
||||
return new AnonymousTrigger<T>(v => {
|
||||
return new AnonymousMutableTrigger<T>(v => {
|
||||
bool curr_result = predicate(v);
|
||||
if (last_result == curr_result) {
|
||||
return false;
|
||||
@@ -112,7 +97,7 @@ namespace mbts {
|
||||
}
|
||||
|
||||
#region Implementation
|
||||
class EqualsTrigger<T> : IPredicate<T>, IEquatable<EqualsTrigger<T>> {
|
||||
class EqualsTrigger<T> : IPredicate<T> {
|
||||
readonly T _value;
|
||||
readonly IEqualityComparer<T> _eq_comparer;
|
||||
|
||||
@@ -121,26 +106,12 @@ namespace mbts {
|
||||
_eq_comparer = eq_comparer;
|
||||
}
|
||||
|
||||
public bool trigger(T value) =>
|
||||
_eq_comparer.Equals(value, _value);
|
||||
public bool trigger(T value) => _eq_comparer.Equals(value, _value);
|
||||
|
||||
public override string ToString() =>
|
||||
$"EqualsTrigger ({typeof(T)})";
|
||||
|
||||
public bool Equals(EqualsTrigger<T> other) =>
|
||||
other != null &&
|
||||
// check equality with both IEqualityComparers
|
||||
_eq_comparer.Equals(_value, other._value) &&
|
||||
other._eq_comparer.Equals(_value, other._value);
|
||||
|
||||
public override bool Equals(object obj) =>
|
||||
Equals(obj as EqualsTrigger<T>);
|
||||
|
||||
public override int GetHashCode() =>
|
||||
_eq_comparer.GetHashCode(_value);
|
||||
public override string ToString() => $"EqualsTrigger ({typeof(T)})";
|
||||
}
|
||||
|
||||
class AndTrigger<T> : IPredicate<T>, IEquatable<AndTrigger<T>> {
|
||||
class AndTrigger<T> : IPredicate<T> {
|
||||
readonly IPredicate<T> _first, _second;
|
||||
|
||||
public AndTrigger(IPredicate<T> first, IPredicate<T> second) {
|
||||
@@ -148,28 +119,18 @@ namespace mbts {
|
||||
_second = second;
|
||||
}
|
||||
|
||||
public bool trigger(T value) =>
|
||||
_first.trigger(value) &&
|
||||
_second.trigger(value);
|
||||
public bool trigger(T value) => _first.trigger(value) && _second.trigger(value);
|
||||
|
||||
public bool Equals(AndTrigger<T> other) =>
|
||||
other != null &&
|
||||
_first.Equals(other._first) &&
|
||||
_second.Equals(other._second);
|
||||
|
||||
public override string ToString() =>
|
||||
$"AndTrigger ({_first}, {_second})";
|
||||
public override string ToString() => $"AndTrigger ({_first}, {_second})";
|
||||
|
||||
public override bool Equals(object obj) =>
|
||||
Equals(obj as AndTrigger<T>);
|
||||
obj is AndTrigger<T> t && _first.Equals(t._first) && _second.Equals(t._second);
|
||||
|
||||
public override int GetHashCode() =>
|
||||
EqualityComparer.aggregate_hash(
|
||||
_first.GetHashCode(),
|
||||
_second.GetHashCode());
|
||||
EqualityComparer.aggregate_hash(_first.GetHashCode(), _second.GetHashCode());
|
||||
}
|
||||
|
||||
class AndTrigger<T1, T2> : IPredicate<(T1, T2)>, IEquatable<AndTrigger<T1, T2>> {
|
||||
class AndTrigger<T1, T2> : IPredicate<(T1, T2)> {
|
||||
readonly IPredicate<T1> _first;
|
||||
readonly IPredicate<T2> _second;
|
||||
|
||||
@@ -178,28 +139,18 @@ namespace mbts {
|
||||
_second = second;
|
||||
}
|
||||
|
||||
public bool trigger((T1, T2) value) =>
|
||||
_first.trigger(value.Item1) &&
|
||||
_second.trigger(value.Item2);
|
||||
|
||||
public bool Equals(AndTrigger<T1, T2> other) =>
|
||||
other != null &&
|
||||
_first.Equals(other._first) &&
|
||||
_second.Equals(other._second);
|
||||
public bool trigger((T1, T2) value) => _first.trigger(value.Item1) && _second.trigger(value.Item2);
|
||||
|
||||
public override bool Equals(object obj) =>
|
||||
Equals(obj as AndTrigger<T1, T2>);
|
||||
obj is AndTrigger<T1, T2> t && _first.Equals(t._first) && _second.Equals(t._second);
|
||||
|
||||
public override int GetHashCode() =>
|
||||
EqualityComparer.aggregate_hash(
|
||||
_first.GetHashCode(),
|
||||
_second.GetHashCode());
|
||||
EqualityComparer.aggregate_hash(_first.GetHashCode(), _second.GetHashCode());
|
||||
|
||||
public override string ToString() =>
|
||||
$"AndTrigger ({_first}, {_second})";
|
||||
public override string ToString() => $"AndTrigger ({_first}, {_second})";
|
||||
}
|
||||
|
||||
class OrTrigger<T> : IPredicate<T>, IEquatable<OrTrigger<T>> {
|
||||
class OrTrigger<T> : IPredicate<T> {
|
||||
readonly IPredicate<T> _first, _second;
|
||||
|
||||
public OrTrigger(IPredicate<T> first, IPredicate<T> second) {
|
||||
@@ -209,24 +160,16 @@ namespace mbts {
|
||||
|
||||
public bool trigger(T value) => _first.trigger(value) || _second.trigger(value);
|
||||
|
||||
public bool Equals(OrTrigger<T> other) =>
|
||||
other != null &&
|
||||
_first.Equals(other._first) &&
|
||||
_second.Equals(other._second);
|
||||
|
||||
public override bool Equals(object obj) =>
|
||||
Equals(obj as OrTrigger<T>);
|
||||
obj is OrTrigger<T> t && _first.Equals(t._first) && _second.Equals(t._second);
|
||||
|
||||
public override int GetHashCode() =>
|
||||
EqualityComparer.aggregate_hash(
|
||||
_first.GetHashCode(),
|
||||
_second.GetHashCode());
|
||||
EqualityComparer.aggregate_hash(_first.GetHashCode(), _second.GetHashCode());
|
||||
|
||||
public override string ToString() =>
|
||||
$"OrTrigger ({_first}, {_second})";
|
||||
public override string ToString() => $"OrTrigger ({_first}, {_second})";
|
||||
}
|
||||
|
||||
class OrTrigger<T1, T2> : IPredicate<(T1, T2)>, IEquatable<OrTrigger<T1, T2>> {
|
||||
class OrTrigger<T1, T2> : IPredicate<(T1, T2)> {
|
||||
readonly IPredicate<T1> _first;
|
||||
readonly IPredicate<T2> _second;
|
||||
|
||||
@@ -235,28 +178,18 @@ namespace mbts {
|
||||
_second = second;
|
||||
}
|
||||
|
||||
public bool trigger((T1, T2) value) =>
|
||||
_first.trigger(value.Item1) ||
|
||||
_second.trigger(value.Item2);
|
||||
|
||||
public bool Equals(OrTrigger<T1, T2> other) =>
|
||||
other != null &&
|
||||
_first.Equals(other._first) &&
|
||||
_second.Equals(other._second);
|
||||
public bool trigger((T1, T2) value) => _first.trigger(value.Item1) || _second.trigger(value.Item2);
|
||||
|
||||
public override bool Equals(object obj) =>
|
||||
Equals(obj as OrTrigger<T1, T2>);
|
||||
obj is OrTrigger<T1, T2> t && _first.Equals(t._first) && _second.Equals(t._second);
|
||||
|
||||
public override int GetHashCode() =>
|
||||
EqualityComparer.aggregate_hash(
|
||||
_first.GetHashCode(),
|
||||
_second.GetHashCode());
|
||||
EqualityComparer.aggregate_hash(_first.GetHashCode(), _second.GetHashCode());
|
||||
|
||||
public override string ToString() =>
|
||||
$"OrTrigger ({_first}, {_second})";
|
||||
public override string ToString() => $"OrTrigger ({_first}, {_second})";
|
||||
}
|
||||
|
||||
class GreaterThanTrigger<T> : IPredicate<T>, IEquatable<GreaterThanTrigger<T>> {
|
||||
class GreaterThanTrigger<T> : IPredicate<T> {
|
||||
readonly T _value;
|
||||
readonly IComparer<T> _comparer;
|
||||
|
||||
@@ -265,120 +198,57 @@ namespace mbts {
|
||||
_comparer = comparer;
|
||||
}
|
||||
|
||||
public bool trigger(T value) =>
|
||||
_comparer.Compare(value, _value) > 0;
|
||||
public bool trigger(T value) => _comparer.Compare(value, _value) > 0;
|
||||
|
||||
public bool Equals(GreaterThanTrigger<T> other) =>
|
||||
other != null &&
|
||||
_comparer.Equals(other._comparer) &&
|
||||
_comparer.Compare(_value, other._value) == 0;
|
||||
|
||||
public override bool Equals(object obj) =>
|
||||
Equals(obj as GreaterThanTrigger<T>);
|
||||
|
||||
public override int GetHashCode() =>
|
||||
_value.GetHashCode();
|
||||
|
||||
public override string ToString() =>
|
||||
$"GreaterThanTrigger ({_value})";
|
||||
public override string ToString() => $"GreaterThanTrigger ({_value})";
|
||||
}
|
||||
|
||||
class GreaterThanOrEqualToTrigger<T> : IPredicate<T>, IEquatable<GreaterThanOrEqualToTrigger<T>> {
|
||||
readonly T _value;
|
||||
readonly IComparer<T> _comparer;
|
||||
|
||||
public GreaterThanOrEqualToTrigger(T value, IComparer<T> comparer) {
|
||||
_value = value;
|
||||
_comparer = comparer;
|
||||
}
|
||||
|
||||
public bool trigger(T value) =>
|
||||
_comparer.Compare(value, _value) >= 0;
|
||||
|
||||
public bool Equals(GreaterThanOrEqualToTrigger<T> other) =>
|
||||
other != null &&
|
||||
_comparer.Equals(other._comparer) &&
|
||||
_comparer.Compare(_value, other._value) == 0;
|
||||
|
||||
public override bool Equals(object obj) =>
|
||||
Equals(obj as GreaterThanOrEqualToTrigger<T>);
|
||||
|
||||
public override int GetHashCode() =>
|
||||
_value.GetHashCode();
|
||||
|
||||
public override string ToString() =>
|
||||
$"GreaterThanTrigger ({_value})";
|
||||
}
|
||||
|
||||
class InSetTrigger<T> : IPredicate<T>, IEquatable<InSetTrigger<T>> {
|
||||
class InSetTrigger<T>: IPredicate<T> {
|
||||
readonly ISet<T> _set;
|
||||
|
||||
public InSetTrigger(ISet<T> set) =>
|
||||
_set = set;
|
||||
|
||||
public bool trigger(T value) =>
|
||||
_set.Contains(value);
|
||||
|
||||
public bool Equals(InSetTrigger<T> other) =>
|
||||
other != null &&
|
||||
// check SetEquals with both sets (IEqualityComparers may differ)
|
||||
_set.SetEquals(other._set) &&
|
||||
other._set.SetEquals(_set);
|
||||
public bool trigger(T value) => _set.Contains(value);
|
||||
|
||||
public override bool Equals(object obj) =>
|
||||
obj is InSetTrigger<T> t && _set.SetEquals(t._set);
|
||||
|
||||
public override int GetHashCode() =>
|
||||
EqualityComparer.auto_phase(_set.Take(8));
|
||||
_set.GetHashCode();
|
||||
|
||||
public override string ToString() =>
|
||||
$"InSetTrigger (Count: {_set.Count}, type: {typeof(T)})";
|
||||
public override string ToString() => $"InSetTrigger (Count: {_set.Count}, type: {typeof(T)})";
|
||||
}
|
||||
|
||||
class WrapperTrigger<TOuter, TInner> : IPredicate<TOuter>, IEquatable<WrapperTrigger<TOuter, TInner>> {
|
||||
class WrapperTrigger<TOutter, TInner> : IPredicate<TOutter> {
|
||||
readonly IPredicate<TInner> _inner_trigger;
|
||||
readonly Func<TOuter, TInner> _selector;
|
||||
readonly Func<TOutter, TInner> _selector;
|
||||
|
||||
public WrapperTrigger(IPredicate<TInner> inner_trigger, Func<TOuter, TInner> selector) {
|
||||
public WrapperTrigger(IPredicate<TInner> inner_trigger, Func<TOutter, TInner> selector) {
|
||||
_inner_trigger = inner_trigger;
|
||||
_selector = selector;
|
||||
}
|
||||
|
||||
public bool trigger(TOuter value) =>
|
||||
_inner_trigger.trigger(_selector(value));
|
||||
|
||||
public bool Equals(WrapperTrigger<TOuter, TInner> other) =>
|
||||
other != null &&
|
||||
_selector.Equals(other._selector) &&
|
||||
_inner_trigger.Equals(other._inner_trigger);
|
||||
public bool trigger(TOutter value) => _inner_trigger.trigger(_selector(value));
|
||||
|
||||
public override bool Equals(object obj) =>
|
||||
Equals(obj as WrapperTrigger<TOuter, TInner>);
|
||||
obj is WrapperTrigger<TOutter, TInner> t && _inner_trigger.Equals(t._inner_trigger) && _selector.Equals(t._selector);
|
||||
|
||||
public override int GetHashCode() =>
|
||||
EqualityComparer.aggregate_hash(
|
||||
_inner_trigger.GetHashCode(),
|
||||
_selector.GetHashCode());
|
||||
EqualityComparer.aggregate_hash(_inner_trigger.GetHashCode(), _selector.GetHashCode());
|
||||
|
||||
public override string ToString() =>
|
||||
$"WrapperTrigger (outer-type {typeof(TOuter)}, inner-trigger: {_inner_trigger})";
|
||||
public override string ToString() => $"WrapperTrigger (outter-type {typeof(TOutter)}, inner-trigger: {_inner_trigger})";
|
||||
}
|
||||
|
||||
class NegateTrigger<T> : IPredicate<T>, IEquatable<NegateTrigger<T>> {
|
||||
class NegateTrigger<T> : IPredicate<T> {
|
||||
readonly IPredicate<T> _trigger;
|
||||
|
||||
public NegateTrigger(IPredicate<T> trigger) =>
|
||||
_trigger = trigger;
|
||||
|
||||
public bool trigger(T value) =>
|
||||
!_trigger.trigger(value);
|
||||
|
||||
public bool Equals(NegateTrigger<T> other) =>
|
||||
other != null &&
|
||||
_trigger.Equals(other._trigger);
|
||||
public bool trigger(T value) => !_trigger.trigger(value);
|
||||
|
||||
public override bool Equals(object obj) =>
|
||||
Equals(obj as NegateTrigger<T>);
|
||||
obj is NegateTrigger<T> t && _trigger.Equals(t._trigger);
|
||||
|
||||
public override int GetHashCode() =>
|
||||
-_trigger.GetHashCode();
|
||||
@@ -386,22 +256,22 @@ namespace mbts {
|
||||
public override string ToString() => $"NegateTrigger ({_trigger})";
|
||||
}
|
||||
|
||||
class AnonymousTrigger<T> : IPredicate<T>, IEquatable<AnonymousTrigger<T>> {
|
||||
class AnonymousTrigger<T> : IPredicate<T> {
|
||||
readonly Func<T, bool> _trigger;
|
||||
|
||||
public AnonymousTrigger(Func<T, bool> trigger) =>
|
||||
_trigger = trigger;
|
||||
|
||||
public bool Equals(AnonymousTrigger<T> other) =>
|
||||
other != null &&
|
||||
_trigger.Equals(other._trigger);
|
||||
public bool trigger(T value) => _trigger(value);
|
||||
|
||||
public bool trigger(T value) =>
|
||||
_trigger(value);
|
||||
public override string ToString() => $"AnonymousTrigger ({typeof(T)})";
|
||||
}
|
||||
|
||||
public override string ToString() =>
|
||||
$"AnonymousTrigger ({typeof(T)})";
|
||||
class AnonymousMutableTrigger<T> : IPredicate<T> {
|
||||
public AnonymousMutableTrigger(Func<T, bool> predicate) => this.predicate = predicate;
|
||||
public Func<T, bool> predicate { get; set; }
|
||||
public bool trigger(T value) => predicate(value);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
using bts.proto;
|
||||
using mbts.ui.shell;
|
||||
|
||||
namespace mbts.spark {
|
||||
class DoublePredicateVM : ViewModelBase, IValueFactory<DoublePredicate> {
|
||||
double _minimum, _maximum;
|
||||
DoublePredicate.PredicateTypeOneofCase _type;
|
||||
|
||||
public DoublePredicate.PredicateTypeOneofCase type { get => _type; set => Set(ref _type, value); }
|
||||
public double minimum { get => _minimum; set => Set(ref _minimum, value); }
|
||||
public double maximum { get => _maximum; set => Set(ref _maximum, value); }
|
||||
|
||||
public DoublePredicate create() =>
|
||||
DoublePredicate.create(type, minimum, maximum);
|
||||
|
||||
public void load_from(DoublePredicate value) {
|
||||
if (value == null)
|
||||
return;
|
||||
type = value.predicate_type_case;
|
||||
switch (type) {
|
||||
case DoublePredicate.PredicateTypeOneofCase.GreaterThan: minimum = value.greater_than.Value; break;
|
||||
case DoublePredicate.PredicateTypeOneofCase.LessThan: maximum = value.less_than.Value; break;
|
||||
case DoublePredicate.PredicateTypeOneofCase.InsideRange: _load_range(value.inside_range.Value); break;
|
||||
case DoublePredicate.PredicateTypeOneofCase.OutsideRange: _load_range(value.outside_range.Value); break;
|
||||
}
|
||||
}
|
||||
|
||||
void _load_range(DoubleRange range) {
|
||||
minimum = range.minimum;
|
||||
maximum = range.maximum;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -338,7 +338,7 @@
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
|
||||
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
|
||||
<Setter Property="Padding" Value="3,3,1,3"/>
|
||||
<Setter Property="Padding" Value="6,3,5,3"/>
|
||||
<Setter Property="ScrollViewer.CanContentScroll" Value="true"/>
|
||||
<Setter Property="ScrollViewer.PanningMode" Value="Both"/>
|
||||
<Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
using bts.api;
|
||||
using bts.co2;
|
||||
|
||||
namespace mbts.spark {
|
||||
struct TradeInfo {
|
||||
public readonly SecurityImmutable security;
|
||||
public readonly TradeMessageImmutable trade;
|
||||
|
||||
public TradeInfo(SecurityImmutable security, TradeMessageImmutable trade) {
|
||||
this.security = security;
|
||||
this.trade = trade;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
using bts.co2;
|
||||
using System;
|
||||
|
||||
namespace mbts.spark {
|
||||
static class TradeMessageExtensions {
|
||||
public static double price(this TradeMessageImmutable trade) =>
|
||||
trade.Mantissa * Math.Pow(10, trade.Exponent);
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
using bts.api;
|
||||
using bts.api.core;
|
||||
using bts.co2;
|
||||
using bts.utils;
|
||||
using mbts.co2;
|
||||
using mbts.utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
|
||||
namespace mbts.spark {
|
||||
readonly struct Product : IEquatable<Product> {
|
||||
public readonly string symbol;
|
||||
public readonly SecurityType type;
|
||||
public readonly Venue exchange;
|
||||
|
||||
public Product(string symbol, SecurityType type, Venue exchange) {
|
||||
this.symbol = symbol;
|
||||
this.type = type;
|
||||
this.exchange = exchange;
|
||||
}
|
||||
|
||||
public static Product from_security(SecurityImmutable security) =>
|
||||
new Product(security.Symbol, security.security_type(), security.Exchange);
|
||||
|
||||
public bool Equals(Product other) =>
|
||||
symbol == other.symbol &&
|
||||
type == other.type &&
|
||||
exchange == other.exchange;
|
||||
|
||||
public override bool Equals(object obj) =>
|
||||
obj is Product other && Equals(other);
|
||||
|
||||
public override int GetHashCode() =>
|
||||
EqualityComparer.auto_phase(
|
||||
symbol?.GetHashCode() ?? 0,
|
||||
type.GetHashCode(),
|
||||
exchange.GetHashCode());
|
||||
|
||||
public static bool operator ==(Product a, Product b) => a.Equals(b);
|
||||
public static bool operator !=(Product a, Product b) => !a.Equals(b);
|
||||
}
|
||||
|
||||
static class TradesSource {
|
||||
public static IEnumerable<Product> aggregate_security_criteria(IEnumerable<SecurityImmutable> securities, IEnumerable<SecurityCriteria> security_criterion) =>
|
||||
securities.Where(s => security_criterion.Any(sc => ContractPickerModel.security_in_criteria(sc, s)))
|
||||
.Select(Product.from_security)
|
||||
.Distinct();
|
||||
|
||||
public static IObservable<IReadOnlyList<TradeInfo>> create(MDConsumer consumer, IObservable<IReadOnlyDictionary<uint, SecurityImmutable>> security_map, IObservable<IEnumerable<SecurityCriteria>> security_criterion, TimeSpan poll_interval) =>
|
||||
security_criterion.CombineLatest(security_map, (criterion, map) => (criterion, map))
|
||||
.Scan((channels: ImmutableDictionary<Product, ChannelConsumer>.Empty, active: new List<ChannelConsumer>(0)), (channel_data, data) => {
|
||||
var (criterion, map) = data;
|
||||
var (channels, active) = channel_data;
|
||||
var products = aggregate_security_criteria(map.Values, criterion);
|
||||
active = new List<ChannelConsumer>();
|
||||
foreach (var p in products) {
|
||||
if (!channels.TryGetValue(p, out var c)) {
|
||||
c = new ChannelConsumer(consumer.get_channel(p.symbol, p.type, p.exchange), map.Values.Where(s => Product.from_security(s).Equals(p)));
|
||||
channels = channels.Add(p, c);
|
||||
}
|
||||
active.Add(c);
|
||||
}
|
||||
return (channels, active);
|
||||
}).SelectManyInSeries(channel_data => {
|
||||
var (channels, active) = channel_data;
|
||||
return Observable.Interval(poll_interval)
|
||||
.Select(_ => active.SelectMany(c => c.read_trades()).ToList());
|
||||
});
|
||||
|
||||
class ChannelConsumer {
|
||||
readonly MDConsumerChannel _channel;
|
||||
readonly Dictionary<uint, SecurityImmutable> _securities;
|
||||
long _cursor = 0;
|
||||
|
||||
public ChannelConsumer(MDConsumerChannel channel) =>
|
||||
_channel = channel;
|
||||
|
||||
public ChannelConsumer(MDConsumerChannel channel, IEnumerable<SecurityImmutable> securities) {
|
||||
_channel = channel;
|
||||
_securities = securities.ToDictionaryCrushed(s => s.Id);
|
||||
}
|
||||
|
||||
public IEnumerable<TradeInfo> read_trades() {
|
||||
foreach (var tm in _channel.read_trades(ref _cursor)) {
|
||||
if (tm != null && _securities.TryGetValue(tm.Key, out SecurityImmutable sec))
|
||||
yield return new TradeInfo(sec, new TradeMessageImmutable(tm));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -107,9 +107,6 @@ namespace bts.spark {
|
||||
public static SparkCommand ShowActivityLog =>
|
||||
SparkCommand.create("Activity Log", Icons.Wrench, SparkTags.ShowActivityLog);
|
||||
|
||||
public static SparkCommand ShowGlobalAlerts =>
|
||||
SparkCommand.create("Alerts", Icons.Speaker, SparkTags.ShowGlobalAlertsTag);
|
||||
|
||||
public static PartialCommand<string> ShowReportIssue =>
|
||||
SparkCommand.create("Report a Problem", Icons.Support, SparkTags.Support);
|
||||
|
||||
|
||||
@@ -81,8 +81,6 @@ namespace mbts.spark
|
||||
|
||||
public static ITag<uint> SyntheticDeleteTag { get; } = new SparkTag<uint>(nameof(SyntheticDeleteTag));
|
||||
|
||||
public static ITag<Nothing> ShowGlobalAlertsTag { get; } = new SparkTag<Nothing>(nameof(ShowGlobalAlertsTag));
|
||||
|
||||
public class Nothing { Nothing() { } }
|
||||
|
||||
internal class SparkTag<T> : ITag<T> {
|
||||
|
||||
@@ -34,14 +34,13 @@ namespace bts.shell {
|
||||
Disposable.dispose(ref _dispose);
|
||||
}
|
||||
public abstract Task<IShellWindow> load(T state);
|
||||
protected virtual Task<IShellWindow> try_upgrade_state(Stream load_stream, int version) => null;
|
||||
|
||||
public Task<IShellWindow> load_window(Stream load_stream, int version) {
|
||||
if (version == this.version) {
|
||||
var t = ProtoBuf.Serializer.DeserializeWithLengthPrefix<T>(load_stream, ProtoBuf.PrefixStyle.Fixed32);
|
||||
return load(t);
|
||||
}
|
||||
return try_upgrade_state(load_stream, version);
|
||||
return null;
|
||||
}
|
||||
public void save(T data, Stream output) {
|
||||
var header = new ShellSerializationHeader() {
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
using bts.utils;
|
||||
using ProtoBuf;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace mbts.spark {
|
||||
[ProtoContract]
|
||||
class GlobalAlertSettings : IEquatable<GlobalAlertSettings> {
|
||||
[ProtoMember(1)]
|
||||
TradeAlert[] _trade_alerts;
|
||||
|
||||
public GlobalAlertSettings() { }
|
||||
public GlobalAlertSettings(IEnumerable<TradeAlert> trade_alerts) =>
|
||||
_trade_alerts = trade_alerts.ToArray();
|
||||
|
||||
public IReadOnlyList<TradeAlert> trade_alerts => _trade_alerts ?? Array.Empty<TradeAlert>();
|
||||
|
||||
public bool Equals(GlobalAlertSettings other) =>
|
||||
other != null &&
|
||||
trade_alerts.SequenceEqual(other.trade_alerts);
|
||||
|
||||
public override bool Equals(object obj) =>
|
||||
Equals(obj as GlobalAlertSettings);
|
||||
|
||||
public override int GetHashCode() =>
|
||||
EqualityComparer.auto_phase(trade_alerts);
|
||||
}
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
using bts.api;
|
||||
using bts.shell;
|
||||
using bts.utils;
|
||||
using ProtoBuf;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace mbts.spark {
|
||||
[ProtoContract]
|
||||
readonly struct TradeAlert : IEquatable<TradeAlert> {
|
||||
[ProtoMember(1)]
|
||||
public readonly TradeAlertPredicate predicate;
|
||||
[ProtoMember(2)]
|
||||
public readonly SoundNotification sound;
|
||||
[ProtoMember(3)]
|
||||
public readonly bool is_enabled;
|
||||
|
||||
public TradeAlert(
|
||||
TradeAlertPredicate predicate,
|
||||
SoundNotification sound = null,
|
||||
bool is_enabled = false) {
|
||||
this.predicate = predicate;
|
||||
this.sound = sound;
|
||||
this.is_enabled = is_enabled;
|
||||
}
|
||||
|
||||
public TradeAlert with(
|
||||
TradeAlertPredicate? predicate = null,
|
||||
SoundNotification sound = null,
|
||||
bool? is_enabled = null) =>
|
||||
//
|
||||
new TradeAlert(
|
||||
predicate ?? this.predicate,
|
||||
sound ?? this.sound,
|
||||
is_enabled ?? this.is_enabled);
|
||||
|
||||
public bool Equals(TradeAlert other) =>
|
||||
predicate.Equals(other.predicate) &&
|
||||
sound.safe_equals(other.sound) &&
|
||||
is_enabled == other.is_enabled;
|
||||
|
||||
public override bool Equals(object obj) =>
|
||||
obj is TradeAlert other && Equals(other);
|
||||
|
||||
public override int GetHashCode() =>
|
||||
EqualityComparer.auto_phase(
|
||||
predicate.GetHashCode(),
|
||||
sound?.GetHashCode() ?? 0,
|
||||
is_enabled.GetHashCode());
|
||||
|
||||
public static bool operator ==(TradeAlert a, TradeAlert b) => a.Equals(b);
|
||||
public static bool operator !=(TradeAlert a, TradeAlert b) => !a.Equals(b);
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
readonly struct TradeAlertPredicate : IEquatable<TradeAlertPredicate> {
|
||||
[ProtoMember(1)]
|
||||
public readonly SecurityCriteria security_criteria;
|
||||
[ProtoMember(2)]
|
||||
public readonly DoublePredicate quantity;
|
||||
[ProtoMember(3)]
|
||||
public readonly DoublePredicate price;
|
||||
[ProtoMember(4)]
|
||||
readonly Side[] _side;
|
||||
|
||||
public TradeAlertPredicate(
|
||||
SecurityCriteria security_criteria = null,
|
||||
DoublePredicate quantity = null,
|
||||
DoublePredicate price = null,
|
||||
Side[] side = null) {
|
||||
this.security_criteria = security_criteria;
|
||||
this.quantity = quantity;
|
||||
this.price = price;
|
||||
this._side = side;
|
||||
}
|
||||
|
||||
public IReadOnlyList<Side> side => _side ?? Array.Empty<Side>();
|
||||
|
||||
public IPredicate<TradeInfo> create() {
|
||||
IPredicate<TradeInfo> p = null;
|
||||
// security criteria
|
||||
var criteria = security_criteria;
|
||||
if (criteria.is_defined()) {
|
||||
p = p.and_safe(PredicateBuilder.create<TradeInfo>(t => ContractPickerModel.security_in_criteria(criteria, t.security)));
|
||||
}
|
||||
// quantity
|
||||
var qpred = quantity?.create();
|
||||
if (qpred != null)
|
||||
p = p.and_safe(PredicateBuilder.wrap<TradeInfo, double>(qpred, t => t.trade.Size));
|
||||
// price
|
||||
var ppred = price?.create();
|
||||
if (ppred != null)
|
||||
p = p.and_safe(PredicateBuilder.wrap<TradeInfo, double>(ppred, t => t.trade.price()));
|
||||
// side
|
||||
if (_side != null && _side.Length == 1) {
|
||||
var agg_side = _side[0].to_aggressor();
|
||||
p = p.and_safe(PredicateBuilder.create<TradeInfo>(t => agg_side == t.trade.AggressorSide));
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
public bool Equals(TradeAlertPredicate other) =>
|
||||
security_criteria.safe_equals(other.security_criteria) &&
|
||||
quantity == other.quantity &&
|
||||
price == other.price &&
|
||||
EqualityComparer.safe_sequence_equals(_side, other._side);
|
||||
|
||||
public override bool Equals(object obj) =>
|
||||
obj is TradeAlertPredicate other && Equals(other);
|
||||
|
||||
public override int GetHashCode() =>
|
||||
EqualityComparer.auto_phase(
|
||||
security_criteria?.GetHashCode() ?? 0,
|
||||
quantity.GetHashCode(),
|
||||
price.GetHashCode(),
|
||||
_side != null && _side.Length == 1 ? (int)_side[0] : 0);
|
||||
|
||||
public static bool operator ==(TradeAlertPredicate a, TradeAlertPredicate b) => a.Equals(b);
|
||||
public static bool operator !=(TradeAlertPredicate a, TradeAlertPredicate b) => !a.Equals(b);
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
using mbts.ui.settings;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace bts.settings {
|
||||
interface ISettingsMigrationRule<T> {
|
||||
bool apply(T settings, out T new_settings);
|
||||
}
|
||||
|
||||
// Migration rules of type ISettingsMigrationRule<T>, where 'T' is a common type, such as GridSettings,
|
||||
// should be exposed through explicitly named interfaces that inherit ISettingsMigrationRule<T>.
|
||||
// This avoids magic const names in the MEF import/export contracts, which would otherwise be needed to
|
||||
// determine the various windows to which such rules apply.
|
||||
interface IPAndLGridSettingsMigrationRule : ISettingsMigrationRule<GridSettings> { }
|
||||
interface IMarketViewGridSettingsMigrationRule : ISettingsMigrationRule<GridSettings> { }
|
||||
|
||||
static class SettingsMigrationHelper {
|
||||
public static bool apply<T>(T settings, IEnumerable<ISettingsMigrationRule<T>> migration_rules, out T new_settings) {
|
||||
bool applied = false;
|
||||
foreach (var r in migration_rules) {
|
||||
if (r.apply(settings, out var migrated)) {
|
||||
settings = migrated;
|
||||
applied = true;
|
||||
}
|
||||
}
|
||||
new_settings = applied ? settings : default;
|
||||
return applied;
|
||||
}
|
||||
|
||||
public static void apply_quiet<T>(ref T settings, IEnumerable<ISettingsMigrationRule<T>> migration_rules) {
|
||||
if (apply(settings, migration_rules, out var new_settings))
|
||||
settings = new_settings;
|
||||
}
|
||||
|
||||
public static ISettingsMigrationRule<T> merge<T>(this IEnumerable<ISettingsMigrationRule<T>> rules) =>
|
||||
new MergeWrapper<T>(rules);
|
||||
|
||||
public static ISettingsMigrationRule<TOuter> wrap<TInner, TOuter>(this ISettingsMigrationRule<TInner> rule, Func<TOuter, TInner> get, Func<TOuter, TInner, TOuter> set) =>
|
||||
new SelectorWrapper<TInner, TOuter>(rule, get, set);
|
||||
|
||||
class MergeWrapper<T> : ISettingsMigrationRule<T> {
|
||||
readonly IEnumerable<ISettingsMigrationRule<T>> _rules;
|
||||
|
||||
public MergeWrapper(IEnumerable<ISettingsMigrationRule<T>> rules) =>
|
||||
_rules = rules;
|
||||
|
||||
public bool apply(T settings, out T new_settings) {
|
||||
bool applied = false;
|
||||
foreach (var r in _rules) {
|
||||
if (r.apply(settings, out var migrated)) {
|
||||
settings = migrated;
|
||||
applied = true;
|
||||
}
|
||||
}
|
||||
new_settings = applied ? settings : default;
|
||||
return applied;
|
||||
}
|
||||
}
|
||||
|
||||
class SelectorWrapper<TInner, TOuter> : ISettingsMigrationRule<TOuter> {
|
||||
readonly ISettingsMigrationRule<TInner> _rule;
|
||||
readonly Func<TOuter, TInner> _get;
|
||||
readonly Func<TOuter, TInner, TOuter> _set;
|
||||
|
||||
public SelectorWrapper(ISettingsMigrationRule<TInner> rule, Func<TOuter, TInner> get, Func<TOuter, TInner, TOuter> set) {
|
||||
_rule = rule;
|
||||
_get = get;
|
||||
_set = set;
|
||||
}
|
||||
|
||||
public bool apply(TOuter settings, out TOuter new_settings) {
|
||||
var ival = _get(settings);
|
||||
if (_rule.apply(ival, out var new_ival)) {
|
||||
new_settings = _set(settings, new_ival);
|
||||
return true;
|
||||
}
|
||||
new_settings = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,11 +56,6 @@ namespace mbts.spark {
|
||||
GridSettings _pnl_grid;
|
||||
[ProtoMember(10)]
|
||||
bts.shell.orderbook.OrderBookGridSettings _order_book_grid;
|
||||
// Placeholder for PNLSettings (will be merged to master soon)
|
||||
// [ProtoMember(13)]
|
||||
// PNLSettings _pnl;
|
||||
[ProtoMember(14)]
|
||||
GlobalAlertSettings _alerts;
|
||||
|
||||
public UserSettings() { }
|
||||
|
||||
@@ -78,8 +73,6 @@ namespace mbts.spark {
|
||||
public GridSettings pnl_grid => _pnl_grid ?? (_pnl_grid = bts.shell.PAndLWindowFactory.PAndLStartupData.DefaultGridSettings);
|
||||
public bts.shell.orderbook.OrderBookGridSettings order_book_grid => _order_book_grid ?? (_order_book_grid = bts.shell.orderbook.OrderBookStartupData.Default.grid_settings_wrapper());
|
||||
|
||||
public GlobalAlertSettings alerts => _alerts ?? (_alerts = new GlobalAlertSettings());
|
||||
|
||||
public UserSettings with(LadderSettings ladder = null,
|
||||
LadderSettingsProfile[] ladder_profiles = null,
|
||||
NotificationSettings notification = null,
|
||||
@@ -88,8 +81,7 @@ namespace mbts.spark {
|
||||
GridSettings provided_fills_gridsettings = null,
|
||||
GridSettings provided_synthetic_management_gridsettings = null,
|
||||
GridSettings provided_pnl_gridsettings = null,
|
||||
bts.shell.orderbook.OrderBookGridSettings order_book_grid = null,
|
||||
GlobalAlertSettings alerts = null) {
|
||||
bts.shell.orderbook.OrderBookGridSettings order_book_grid = null) {
|
||||
var us = new UserSettings(ladder_profiles ?? _ladder_profiles) {
|
||||
_ladder = ladder ?? _ladder,
|
||||
_notification = notification ?? _notification,
|
||||
@@ -97,9 +89,8 @@ namespace mbts.spark {
|
||||
_time_and_sales_grid = time_and_sales ?? _time_and_sales_grid,
|
||||
_fills_grid = provided_fills_gridsettings ?? _fills_grid,
|
||||
_synthetic_management_grid = provided_synthetic_management_gridsettings ?? _synthetic_management_grid,
|
||||
_pnl_grid = provided_pnl_gridsettings ?? _pnl_grid,
|
||||
_pnl_grid = provided_pnl_gridsettings ?? _pnl_grid,
|
||||
_order_book_grid = order_book_grid ?? _order_book_grid,
|
||||
_alerts = alerts ?? _alerts
|
||||
};
|
||||
us._last_ladder_profile_id = Math.Max(_last_ladder_profile_id, us.ladder_profiles.Select(p => p.id).MaxOrDefault());
|
||||
return us;
|
||||
@@ -107,9 +98,8 @@ namespace mbts.spark {
|
||||
|
||||
public static class FileExtensions {
|
||||
public const string
|
||||
GRID = "grid",
|
||||
LADDER = "ladder",
|
||||
PNL = "pnl";
|
||||
GRID = "grid";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
using bts;
|
||||
using bts.settings;
|
||||
using bts.utils;
|
||||
using mbts.spark;
|
||||
using mbts.ui.settings;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
@@ -24,30 +22,16 @@ namespace mbts.persist {
|
||||
UserSettings _default_settings;
|
||||
readonly BehaviorSubject<UserSettings> _default_settings_subject = new BehaviorSubject<UserSettings>(UserSettings.factory_defaults);
|
||||
readonly object _default_settings_lock = new object();
|
||||
readonly IEnumerable<IPAndLGridSettingsMigrationRule> _pandl_migration_rules;
|
||||
readonly IEnumerable<IMarketViewGridSettingsMigrationRule> _mview_migration_rules;
|
||||
|
||||
[ImportingConstructor]
|
||||
public UserSettingsHub(
|
||||
IUserPersist persist,
|
||||
[ImportMany]IEnumerable<IPAndLGridSettingsMigrationRule> pandl_migration_rules,
|
||||
[ImportMany]IEnumerable<IMarketViewGridSettingsMigrationRule> mview_migration_rules) {
|
||||
//
|
||||
public UserSettingsHub(IUserPersist persist) {
|
||||
_persist = persist;
|
||||
_pandl_migration_rules = pandl_migration_rules ?? Enumerable.Empty<IPAndLGridSettingsMigrationRule>();
|
||||
_mview_migration_rules = mview_migration_rules ?? Enumerable.Empty<IMarketViewGridSettingsMigrationRule>();
|
||||
init();
|
||||
async void init() {
|
||||
UserSettings s;
|
||||
try {
|
||||
s = await _persist.get<UserSettings>(NAME_SPACE, DEFAULT_KEY);
|
||||
if (s != null) {
|
||||
var wrapped_rules = new[] {
|
||||
_pandl_migration_rules.merge().wrap<GridSettings, UserSettings>(us => us.pnl_grid, (us, gs) => us.with(provided_pnl_gridsettings: gs)),
|
||||
_mview_migration_rules.merge().wrap<GridSettings, UserSettings>(us => us.market_view_grid, (us, gs) => us.with(provided_market_view_gridsettings: gs))
|
||||
};
|
||||
SettingsMigrationHelper.apply_quiet(ref s, wrapped_rules);
|
||||
// TODO: move ladder sanitization logic into ISettingsMigrationRule
|
||||
s.ladder.sanitize();
|
||||
// ensure valid named ladder set (w/ distinct, non-zero ids)
|
||||
var ladder_profile_map = new Dictionary<uint, LadderSettingsProfile>();
|
||||
@@ -94,20 +78,6 @@ namespace mbts.persist {
|
||||
}
|
||||
}
|
||||
|
||||
public IWindowSettingsManager<GridSettings> create_pandl_settings_manager(GridSettings instance_settings) =>
|
||||
_create_settings_manager(
|
||||
instance_settings,
|
||||
_pandl_migration_rules,
|
||||
us => us.pnl_grid,
|
||||
(us, gs) => us.with(provided_pnl_gridsettings: gs));
|
||||
|
||||
public IWindowSettingsManager<GridSettings> create_mview_settings_manager(GridSettings instance_settings) =>
|
||||
_create_settings_manager(
|
||||
instance_settings,
|
||||
_mview_migration_rules,
|
||||
us => us.market_view_grid,
|
||||
(us, gs) => us.with(provided_market_view_gridsettings: gs));
|
||||
|
||||
public async Task save_default(UserSettings value) {
|
||||
// notify observers before persisting so the changes are reflected immediately in the ui
|
||||
lock (_default_settings_lock) {
|
||||
@@ -116,12 +86,6 @@ namespace mbts.persist {
|
||||
await _persist_async();
|
||||
}
|
||||
|
||||
IWindowSettingsManager<T> _create_settings_manager<T>(T instance_settings, IEnumerable<ISettingsMigrationRule<T>> migration_rules, Func<UserSettings, T> get, Func<UserSettings, T, UserSettings> set) where T : class {
|
||||
if (instance_settings != null && migration_rules.merge().apply(instance_settings, out var new_settings))
|
||||
instance_settings = new_settings;
|
||||
return new WindowSettingsManager<T>(this, instance_settings, get, set);
|
||||
}
|
||||
|
||||
async Task _persist_async() {
|
||||
try {
|
||||
await _persist.set(NAME_SPACE, DEFAULT_KEY, _default_settings);
|
||||
|
||||
@@ -59,22 +59,6 @@ namespace mbts.spark {
|
||||
|
||||
interface ITemplatedPropertyWrapperVM<T> : IPropertyWrapperVM<T>, ITemplateSelectorKey { }
|
||||
|
||||
static class ValueFactoryUtils {
|
||||
public static void sync_with_models<TValueFactory, TModel>(this IList<TValueFactory> target, IEnumerable<TModel> models) where TValueFactory : IValueFactory<TModel>, new() {
|
||||
target.sync_with_models(
|
||||
models,
|
||||
m => {
|
||||
var vf = new TValueFactory();
|
||||
vf.load_from(m);
|
||||
return vf;
|
||||
},
|
||||
(vf, m) => {
|
||||
vf.load_from(m);
|
||||
return vf;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static class PropertyWrapperVMUtils {
|
||||
public static T create_assigned<T>(this IPropertyWrapperVM<T> vm) where T : new() =>
|
||||
vm.create_assigned(() => new T());
|
||||
|
||||
@@ -22,7 +22,6 @@ namespace bts.proto {
|
||||
}
|
||||
|
||||
public Color color => _color.rgb_color();
|
||||
public Color argb_color => Color.FromArgb((byte)(MathUtils.within_range(0, 1, _opacity) * 255.0), color.R, color.G, color.B);
|
||||
public double opacity => _opacity;
|
||||
|
||||
public ColorOpacityProto with(uint? color = null, double? opacity = null) =>
|
||||
|
||||
@@ -16,8 +16,14 @@ namespace bts.proto {
|
||||
|
||||
public NullableColorProto(uint value) =>
|
||||
_value = value;
|
||||
public Color? value => _value?.rgb_color();
|
||||
public Color? argb => _value?.argb_color();
|
||||
|
||||
public Color? value {
|
||||
get {
|
||||
if (_value.HasValue)
|
||||
return _value.Value.rgb_color();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Equals(NullableColorProto other) =>
|
||||
EqualityComparer.nullable_equals(_value, other._value);
|
||||
|
||||
@@ -105,9 +105,6 @@
|
||||
<SolidColorBrush x:Key="ComboBox.Disabled.Editable.Button.Border" Color="Transparent"/>
|
||||
<SolidColorBrush x:Key="ComboBox.Static.Glyph" Color="#FF606060"/>
|
||||
|
||||
<!--Toggle Switch-->
|
||||
<Color x:Key="ToggleSwitchOnColor">#FF4182F6</Color>
|
||||
|
||||
<!--ListBox-->
|
||||
<Color x:Key="ListBoxItem.Selected.Active.Background.Color">#3D25A0DA</Color>
|
||||
<Color x:Key="ListBoxItem.Selected.Active.Border.Color">#FF26A0DA</Color>
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using bts.utils;
|
||||
using mbts.ui.extensions;
|
||||
|
||||
namespace mbts.ui.controls.adorners {
|
||||
public class RowSelectionAdorner : Adorner, DIYGrid.ISelectionHighlightListener, IDisposable {
|
||||
Brush _fill = DIYGrid.DefaultSelectionHighlight;
|
||||
Pen _stroke = DIYGrid.DefaultSelectionStroke;
|
||||
|
||||
DIYGridRowsPresenter _diy_rp;
|
||||
ScrollViewer _diy_scroll;
|
||||
ScrollContentPresenter _diy_scp;
|
||||
List<IDisposable> _disposables = new List<IDisposable>();
|
||||
|
||||
#region Fill
|
||||
public static readonly DependencyProperty FillProperty =
|
||||
WPFHelper.create_dp<Brush, RowSelectionAdorner>(
|
||||
nameof(Fill),
|
||||
FrameworkPropertyMetadataOptions.AffectsRender,
|
||||
(o, v) => o._fill = v,
|
||||
DIYGrid.DefaultSelectionHighlight);
|
||||
|
||||
public SolidColorBrush Fill {
|
||||
get => GetValue(FillProperty) as SolidColorBrush;
|
||||
set => SetValue(FillProperty, value);
|
||||
}
|
||||
#endregion
|
||||
#region Stroke
|
||||
public static readonly DependencyProperty StrokeProperty =
|
||||
WPFHelper.create_dp<Pen, RowSelectionAdorner>(
|
||||
nameof(Stroke),
|
||||
FrameworkPropertyMetadataOptions.AffectsRender,
|
||||
(o, v) => o._stroke = v,
|
||||
DIYGrid.DefaultSelectionStroke);
|
||||
|
||||
public Pen Stroke {
|
||||
get => GetValue(StrokeProperty) as Pen;
|
||||
set => SetValue(StrokeProperty, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
internal RowSelectionAdorner(DIYGridRowsPresenter adorned_element, ScrollViewer scroll_viewer) : base(adorned_element) {
|
||||
// we want our clicks to go through to the underlying grid
|
||||
IsHitTestVisible = false;
|
||||
|
||||
_diy_rp = adorned_element;
|
||||
_diy_scroll = scroll_viewer;
|
||||
_diy_scp = _diy_rp.descendants_of_type<ScrollContentPresenter>().empty_if_null().FirstOrDefault();
|
||||
|
||||
// We actually don't need mouse up/down or key up/down when we can just respond to selection changes
|
||||
_disposables.Add(((System.Windows.Controls.Primitives.Selector)_diy_rp).SubscribeSelectionChanged((s, e) => InvalidateVisual()));
|
||||
// so that we update the highlight as the grid scrolls
|
||||
_disposables.Add(_diy_scroll.SubscribeScrollChanged((s, e) => InvalidateVisual()));
|
||||
}
|
||||
|
||||
|
||||
protected override void OnRender(DrawingContext drawingContext) {
|
||||
base.OnRender(drawingContext);
|
||||
if (AdornedElement is ListBox lb && lb.SelectedItems != null && lb.SelectedItems.Count > 0) {
|
||||
foreach(var lbi in lb.SelectedItems) {
|
||||
if (_diy_rp.ItemContainerGenerator.ContainerFromItem(lbi) is FrameworkElement row && row.IsVisible) {
|
||||
var top_left = lb.GetChildTopLeft(row);
|
||||
var top_is_within_bounds = top_left.Y >= 0;
|
||||
|
||||
// Note that the top of the row can still technically be visible but we wont show
|
||||
// the adorner rectangle b/c the bottom of the row will be obscured by the scrollbar
|
||||
var bottom_is_within_bounds = lb.GetChildBounds(row).BottomRight.Y <= _diy_scroll.ActualHeight;
|
||||
if (top_is_within_bounds && bottom_is_within_bounds) {
|
||||
// we use the scroll content presenter in the X point calculation instead of the row
|
||||
// because of the OB which has a right side panel that takes up some space over
|
||||
// row width
|
||||
drawingContext.DrawRectangle(_fill,
|
||||
_stroke,
|
||||
new Rect(top_left, new Point(top_left.X + _diy_scp.ActualWidth, top_left.Y + row.ActualHeight)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void on_selection_fill(SolidColorBrush selection_fill) {
|
||||
Fill = selection_fill;
|
||||
Stroke = new Pen(selection_fill.Color.to_solid_brush(), 1);
|
||||
InvalidateVisual();
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
_disposables.DisposeAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,7 @@
|
||||
using bts.proto;
|
||||
using bts.utils;
|
||||
using mbts.spark;
|
||||
using mbts.ui.controls.color_picker;
|
||||
using mbts.ui.shell;
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
@@ -40,42 +38,16 @@ namespace mbts.ui.controls {
|
||||
class ColorPickerPopupVM : ViewModelBase, IValueFactory<SolidColorBrush> {
|
||||
readonly Property<Color> _color;
|
||||
readonly Property<double> _opacity;
|
||||
ObservableCollection<ColorPaletteVM> _palettes;
|
||||
bool _bind_opacity_to_alpha;
|
||||
IDisposable _opacity_alpha_binding;
|
||||
|
||||
public ColorPickerPopupVM() {
|
||||
_color = backing(nameof(color), Colors.Black);
|
||||
_opacity = backing(nameof(opacity), 1.0);
|
||||
palettes = new ObservableCollection<ColorPaletteVM>(ColorPalette.all_presets().Select(p => new ColorPaletteVM(p)));
|
||||
}
|
||||
|
||||
public Color color { get => _color.Value; set => _color.set(value); }
|
||||
public double opacity { get => _opacity.Value; set => _opacity.set(value); }
|
||||
public ObservableCollection<ColorPaletteVM> palettes => _palettes ?? (_palettes = new ObservableCollection<ColorPaletteVM>(ColorPalette.all_presets().Select(p => new ColorPaletteVM(p))));
|
||||
public bool bind_opacity_to_alpha {
|
||||
get => _bind_opacity_to_alpha;
|
||||
set {
|
||||
if (_bind_opacity_to_alpha != value) {
|
||||
Disposable.dispose(ref _opacity_alpha_binding);
|
||||
_bind_opacity_to_alpha = value;
|
||||
if (value) {
|
||||
_opacity.Changed += opacity_changed;
|
||||
_color.Changed += color_changed;
|
||||
color_changed(default, color);
|
||||
void opacity_changed(double oldval, double newval) {
|
||||
_color.Changed -= color_changed;
|
||||
_color.set(color.with((byte)(MathUtils.within_range(0, 1, newval) * 255.0)));
|
||||
_color.Changed += color_changed;
|
||||
}
|
||||
void color_changed(Color oldval, Color newval) {
|
||||
_opacity.Changed -= opacity_changed;
|
||||
_opacity.set(newval.A / 255.0);
|
||||
_opacity.Changed += opacity_changed;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public ObservableCollection<ColorPaletteVM> palettes { get; }
|
||||
|
||||
public SolidColorBrush create() => new SolidColorBrush(color) { Opacity = opacity }.frozen();
|
||||
public void load_from(SolidColorBrush brush) {
|
||||
|
||||
@@ -304,6 +304,11 @@ namespace mbts.ui.controls {
|
||||
}
|
||||
drawingContext.Pop();
|
||||
}
|
||||
if (bg != null) {
|
||||
var bgo = GetBackgroundOverlay(this);
|
||||
if (bgo != null)
|
||||
drawingContext.DrawRectangle(bgo, null, rect);
|
||||
}
|
||||
}
|
||||
|
||||
bool _point_horizontally_over_text(double x) {
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
using bts;
|
||||
using mbts.spark;
|
||||
using mbts.spark;
|
||||
using mbts.spark.filters;
|
||||
using mbts.ui.settings;
|
||||
using mbts.ui.shell;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Reflection;
|
||||
@@ -10,7 +11,7 @@ using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace mbts.ui.controls {
|
||||
namespace mbts.ui.controls {
|
||||
// Column types are nested inside the DIYGrid
|
||||
partial class DIYGrid {
|
||||
public static class DefaultColumnWidths {
|
||||
@@ -112,7 +113,6 @@ namespace mbts.ui.controls {
|
||||
StartPnL = "Start PnL",
|
||||
DayLong = "Day Long",
|
||||
DayShort = "Day Short",
|
||||
RawDayPos = "Raw Day Pos",
|
||||
DayPnL = "Day PnL",
|
||||
TotalNetPos = "Total Net Pos",
|
||||
TotalPnL = "Total PnL",
|
||||
@@ -165,7 +165,7 @@ namespace mbts.ui.controls {
|
||||
}
|
||||
|
||||
public interface ISelectionHighlightListener {
|
||||
void on_selection_fill(SolidColorBrush selection_fill);
|
||||
void on_selection_fill(Brush selection_fill);
|
||||
}
|
||||
|
||||
// To include extra data with your column type you are encouraged to derive from DIYGridColumn
|
||||
@@ -173,19 +173,16 @@ namespace mbts.ui.controls {
|
||||
//readonly ExternalProperty<FontWeight?> _font_weigh
|
||||
FontWeight _fallback_font_weight;
|
||||
Color _fallback_font_color;
|
||||
Color _fallback_background_color;
|
||||
double _fallback_font_size;
|
||||
FontFamily _fallback_font_family;
|
||||
FontWeight? _font_weight_override;
|
||||
Color? _font_color_override;
|
||||
Color? _background_color_override;
|
||||
object _header_copy;
|
||||
|
||||
protected ColumnBase() {
|
||||
var gs = GridSettings.Default;
|
||||
_fallback_font_weight = gs.font_weight;
|
||||
_fallback_font_color = gs.font_color.rgb_color();
|
||||
_fallback_background_color = gs.background_color.argb_color();
|
||||
_fallback_font_size = gs.font_size;
|
||||
_fallback_font_family = new FontFamily(gs.font_family);
|
||||
original_width = double.NaN;
|
||||
@@ -218,9 +215,6 @@ namespace mbts.ui.controls {
|
||||
public FontWeight font_weight => font_weight_override ?? _fallback_font_weight;
|
||||
public Color font_color => font_color_override ?? _fallback_font_color;
|
||||
public Brush font_brush => new SolidColorBrush(font_color);
|
||||
public Color? background_color_override { get => _background_color_override; set => _set_font_param(ref _background_color_override, value); }
|
||||
public Color? background_color => background_color_override ?? _fallback_background_color;
|
||||
public Brush background_brush => background_color?.to_brush();
|
||||
// TODO: find a better way to store the header-property in a thread-safe manner - a value for GridSettings.display_name, more specifically.
|
||||
// Since save_state is called from a different thread in IShared/SlowWindows, it is not safe to access the DependencyProperty store from that method.
|
||||
// By updating a local _header_copy field in the Header setter, the DependencyProperty lookup may be bypassed when appropriate (see create method below).
|
||||
@@ -230,7 +224,7 @@ namespace mbts.ui.controls {
|
||||
}
|
||||
|
||||
public GridColumnSettings create() =>
|
||||
new GridColumnSettings(id, _header_copy?.ToString(), is_visible, is_collapsed, font_weight_override, font_color_override, ActualWidth, horizontal_alignment, horizontal_offset, background_color_override);
|
||||
new GridColumnSettings(id, _header_copy?.ToString(), is_visible, is_collapsed, font_weight_override, font_color_override, ActualWidth, horizontal_alignment, horizontal_offset);
|
||||
|
||||
void _set_font_param<T>(ref T field, T value) {
|
||||
field = value;
|
||||
@@ -241,7 +235,7 @@ namespace mbts.ui.controls {
|
||||
if (settings == null)
|
||||
return;
|
||||
using (ReflectionHelper.detect_property_changes(this, _raise_prop_changed, nameof(is_visible), nameof(is_collapsed), nameof(font_weight_override), nameof(font_weight),
|
||||
nameof(font_color_override), nameof(font_color), nameof(font_brush), nameof(font_family), nameof(font_size), nameof(horizontal_alignment), nameof(horizontal_offset), nameof(background_color_override), nameof(background_color), nameof(background_brush))) {
|
||||
nameof(font_color_override), nameof(font_color), nameof(font_brush), nameof(font_family), nameof(font_size), nameof(horizontal_alignment), nameof(horizontal_offset))) {
|
||||
//
|
||||
id = settings.id;
|
||||
if (!string.IsNullOrEmpty(settings.display_name))
|
||||
@@ -252,7 +246,6 @@ namespace mbts.ui.controls {
|
||||
font_color_override = settings.font_color_override;
|
||||
horizontal_alignment = settings.horizontal_alignment;
|
||||
horizontal_offset = settings.horizontal_offset;
|
||||
background_color_override = settings.background_color_override;
|
||||
}
|
||||
if (original_width != settings.width) {
|
||||
original_width = settings.width;
|
||||
@@ -262,12 +255,11 @@ namespace mbts.ui.controls {
|
||||
}
|
||||
|
||||
// 'fallback' font-params (aka 'grid-level' font-params)
|
||||
public void set_fallback_font_params(FontFamily font_family, double font_size, Color font_color, FontWeight font_weight, Color background_color) {
|
||||
using (ReflectionHelper.detect_property_changes(this, _raise_prop_changed, nameof(font_family), nameof(font_size), nameof(font_color), nameof(font_weight), nameof(background_color), nameof(background_brush))) {
|
||||
public void set_fallback_font_params(FontFamily font_family, double font_size, Color font_color, FontWeight font_weight) {
|
||||
using (ReflectionHelper.detect_property_changes(this, _raise_prop_changed, nameof(font_family), nameof(font_size), nameof(font_color), nameof(font_weight))) {
|
||||
_fallback_font_family = font_family;
|
||||
_fallback_font_size = font_size;
|
||||
_fallback_font_color = font_color;
|
||||
_fallback_background_color = background_color;
|
||||
_fallback_font_weight = font_weight;
|
||||
}
|
||||
font_params_changed?.Invoke();
|
||||
@@ -281,25 +273,9 @@ namespace mbts.ui.controls {
|
||||
}
|
||||
|
||||
internal abstract class ColumnBase<TColKey> : ColumnBase, IKeyedColumn<TColKey>, IGridColumn {
|
||||
TColKey _key;
|
||||
bool _key_initialized;
|
||||
// need a default constructor to support declaration in xaml
|
||||
protected ColumnBase() { }
|
||||
protected ColumnBase(TColKey key) => this.key = key;
|
||||
|
||||
public TColKey key {
|
||||
get => _key;
|
||||
set {
|
||||
if (Equals(_key, value))
|
||||
return;
|
||||
Log.assert(!_key_initialized, "Column key already initialized.");
|
||||
_key_initialized = true;
|
||||
_key = value;
|
||||
on_key_initialized(value);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void on_key_initialized(TColKey key) { }
|
||||
public TColKey key { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+15
-5
@@ -7,7 +7,7 @@ using System.Windows.Media;
|
||||
|
||||
namespace mbts.ui.controls {
|
||||
partial class DIYGrid {
|
||||
internal class HyperlinkElementColumn<TRowVM, TKey> : FrameworkElementColumnBase<TKey>, IFrameworkElementColumn, IFilterableColumn, IKeyedColumn<TKey> {
|
||||
internal class HyperlinkElementColumn<TRowVM, TKey> : FrameworkElementColumnBase<TKey>, IFrameworkElementColumn, IFilterableColumn, IKeyedColumn<TKey>, ISelectionHighlightListener {
|
||||
static readonly Thickness _default_padding = new Thickness(6, 0, 6, 0);
|
||||
readonly BindingBase _binding, _command_binding;
|
||||
|
||||
@@ -22,17 +22,27 @@ namespace mbts.ui.controls {
|
||||
public IDataGridColumnFilter filter { get; }
|
||||
public string filter_template { get; }
|
||||
public Thickness padding { get; set; } = _default_padding;
|
||||
|
||||
public Brush background { get; set; }
|
||||
public Brush background_overlay { get; set; } = DefaultSelectionHighlight;
|
||||
|
||||
public void on_selection_fill(Brush selection_fill) =>
|
||||
background_overlay = selection_fill;
|
||||
|
||||
public override FrameworkElement view_factory() {
|
||||
var hl = new HyperlinkCell { Padding = padding };
|
||||
hl.SetBinding(HyperlinkCell.ForegroundProperty, new Binding(nameof(font_brush)) { Source = this });
|
||||
var hl = new HyperlinkCell { Foreground = font_brush, Background = background, Padding = padding };
|
||||
hl.SetBinding(HyperlinkCell.TextProperty, _binding);
|
||||
hl.SetBinding(HyperlinkCell.FontFamilyProperty, new Binding(nameof(font_family)) { Source = this });
|
||||
hl.SetBinding(HyperlinkCell.FontSizeProperty, new Binding(nameof(font_size)) { Source = this });
|
||||
hl.SetBinding(HyperlinkCell.HorizontalAlignmentProperty, new Binding(nameof(horizontal_alignment)) { Source = this });
|
||||
hl.SetBinding(HyperlinkCell.CommandProperty, _command_binding);
|
||||
hl.SetBinding(HyperlinkCell.WidthProperty, new Binding(nameof(ActualWidth)) { Source = this });
|
||||
hl.SetBinding(HyperlinkCell.BackgroundProperty, new Binding(nameof(background_brush)) { Source = this });
|
||||
if (background_overlay != null) {
|
||||
hl.SetBinding(BackgroundOverlayProperty, new Binding(nameof(ListBoxItem.IsSelected)) {
|
||||
Converter = new AnonymousValueConverter<bool, Brush>(val => val ? background_overlay : null),
|
||||
Mode = BindingMode.OneWay,
|
||||
RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(ListBoxItem), 1)
|
||||
});
|
||||
}
|
||||
return hl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace mbts.ui.controls {
|
||||
Func<TRowVM, Brush> get_brush = null)
|
||||
: base() {
|
||||
_get_text = get_text;
|
||||
Func<TRowVM, Brush> get_default_background_brush = (TRowVM _) => this.background_brush;
|
||||
Func<TRowVM, Brush> get_default_background_brush = (TRowVM _) => this.background;
|
||||
_get_background_brush = get_brush ?? get_default_background_brush;
|
||||
this.filter = filter;
|
||||
this.filter_template = filter_template;
|
||||
@@ -42,6 +42,7 @@ namespace mbts.ui.controls {
|
||||
|
||||
public IDataGridColumnFilter filter { get; }
|
||||
public string filter_template { get; }
|
||||
public Brush background { get; set; }
|
||||
public VerticalAlignment vertical_alignment { get; set; } = VerticalAlignment.Center;
|
||||
public Thickness padding { get; set; } = DefaultCellPadding;
|
||||
public GlyphContext glyph_context { get; private set; }
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
using bts;
|
||||
using bts.utils;
|
||||
using mbts.spark.filters;
|
||||
using mbts.ui.controls.adorners;
|
||||
using mbts.ui.extensions;
|
||||
using mbts.ui.utility;
|
||||
using bts.utils;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
@@ -12,13 +12,14 @@ using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Controls.Primitives;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using mbts.utils;
|
||||
using System.Reactive.Linq;
|
||||
|
||||
namespace mbts.ui.controls {
|
||||
interface IHandleGridSorting {
|
||||
@@ -125,7 +126,6 @@ namespace mbts.ui.controls {
|
||||
double _row_height = DIYGrid.DefaultRowHeight;
|
||||
DIYGrid _grid;
|
||||
DragRectAdorner _drag_rect_adorner;
|
||||
RowSelectionAdorner _rsa;
|
||||
ScrollViewer _scroll_viewer;
|
||||
Point? _drag_start;
|
||||
double _drag_start_vertical_drift;
|
||||
@@ -148,18 +148,12 @@ namespace mbts.ui.controls {
|
||||
_drag_rect_adorner.SetResourceReference(DragRectAdorner.StrokeProperty, DIYGrid.SelectionStrokeKey);
|
||||
_drag_rect_adorner.attach();
|
||||
}
|
||||
_rsa = new RowSelectionAdorner(this, _scroll_viewer);
|
||||
_rsa.attach();
|
||||
};
|
||||
Unloaded += (s, e) => {
|
||||
if (_drag_rect_adorner != null) {
|
||||
_drag_rect_adorner.detach();
|
||||
Disposable.dispose(ref _drag_rect_adorner);
|
||||
}
|
||||
if (_rsa != null) {
|
||||
_rsa.detach();
|
||||
Disposable.dispose(ref _rsa);
|
||||
}
|
||||
};
|
||||
void on_row_height_changed(double v) => _row_height = v;
|
||||
}
|
||||
@@ -267,12 +261,6 @@ namespace mbts.ui.controls {
|
||||
o.Resources[SelectionStrokeKey] = new Pen(o.SelectionHighlight.Color.to_solid_brush().frozen(), 1).frozen();
|
||||
foreach (var col in o.Columns.empty_if_null().OfType<ISelectionHighlightListener>())
|
||||
col.on_selection_fill(v);
|
||||
var ad_layer = System.Windows.Documents.AdornerLayer.GetAdornerLayer(o);
|
||||
if (ad_layer != null) {
|
||||
foreach (var ad in ad_layer.descendants_of_type<ISelectionHighlightListener>()) {
|
||||
ad.on_selection_fill(v);
|
||||
}
|
||||
}
|
||||
}, (o, v) => v as SolidColorBrush ?? DefaultSelectionHighlight,
|
||||
DefaultSelectionHighlight);
|
||||
|
||||
@@ -574,11 +562,6 @@ namespace mbts.ui.controls {
|
||||
|
||||
public static readonly DependencyProperty HeaderTotalSizeHackProperty = DependencyProperty.Register("HeaderTotalSizeHack", typeof(double), typeof(DIYGrid), new FrameworkPropertyMetadata(0.0, dp_invalidate_column_layout));
|
||||
|
||||
public double HeaderTotalSizeHack {
|
||||
get => (double)GetValue(HeaderTotalSizeHackProperty);
|
||||
set => SetValue(HeaderTotalSizeHackProperty, value);
|
||||
}
|
||||
|
||||
private static void dp_invalidate_column_layout(DependencyObject d, DependencyPropertyChangedEventArgs unused) {
|
||||
var grid = d as DIYGrid;
|
||||
if (grid != null) {
|
||||
@@ -611,61 +594,6 @@ namespace mbts.ui.controls {
|
||||
// Public Properties
|
||||
#region Public Properties
|
||||
|
||||
#region ColumnKey
|
||||
public static readonly DependencyProperty ColumnKeyProperty =
|
||||
DependencyProperty.RegisterAttached("ColumnKey",
|
||||
typeof(object),
|
||||
typeof(DIYGrid),
|
||||
new PropertyMetadata((d, e) => {
|
||||
if (!(d is UIElement child))
|
||||
return;
|
||||
if (VisualTreeHelper.GetParent(child) is Grid panel) {
|
||||
if (GetSyncWithColumnGeometrySource(d) is DIYGrid diy_grid || panel.try_find_ancestor_of_type(out diy_grid))
|
||||
ColumnGeometrySyncHelper.reset_panel_child(child, diy_grid, e.NewValue);
|
||||
}
|
||||
}));
|
||||
|
||||
public static object GetColumnKey(DependencyObject d) => d.GetValue(ColumnKeyProperty);
|
||||
public static void SetColumnKey(DependencyObject d, object value) => d.SetValue(ColumnKeyProperty, value);
|
||||
#endregion
|
||||
|
||||
#region SyncWithColumnGeometrySource
|
||||
public static readonly DependencyProperty SyncWithColumnGeometrySourceProperty =
|
||||
DependencyProperty.RegisterAttached(
|
||||
"SyncWithColumnGeometrySource",
|
||||
typeof(DIYGrid),
|
||||
typeof(DIYGrid),
|
||||
new PropertyMetadata(ColumnGeometrySyncHelper.reset_sync));
|
||||
|
||||
public static DIYGrid GetSyncWithColumnGeometrySource(DependencyObject d) => d.GetValue(SyncWithColumnGeometrySourceProperty) as DIYGrid;
|
||||
public static void SetSyncWithColumnGeometrySource(DependencyObject d, DIYGrid value) => d.SetValue(SyncWithColumnGeometrySourceProperty, value);
|
||||
#endregion
|
||||
|
||||
#region SyncWithColumnGeometry
|
||||
public static readonly DependencyProperty SyncWithColumnGeometryProperty =
|
||||
DependencyProperty.RegisterAttached(
|
||||
"SyncWithColumnGeometry",
|
||||
typeof(bool),
|
||||
typeof(DIYGrid),
|
||||
new PropertyMetadata(false, ColumnGeometrySyncHelper.reset_sync));
|
||||
|
||||
public static bool GetSyncWithColumnGeometry(DependencyObject d) => (bool)d.GetValue(SyncWithColumnGeometryProperty);
|
||||
public static void SetSyncWithColumnGeometry(DependencyObject d, bool value) => d.SetValue(SyncWithColumnGeometryProperty, value);
|
||||
|
||||
static readonly DependencyPropertyKey SyncWithColumnGeometryHookPropertyKey =
|
||||
DependencyProperty.RegisterAttachedReadOnly(
|
||||
"SyncWithColumnGeometryHook",
|
||||
typeof(IDisposable),
|
||||
typeof(DIYGrid),
|
||||
new PropertyMetadata(FrameworkElementLifeDependencyHelper.DisposeOld));
|
||||
|
||||
internal static IDisposable GetSyncWithColumnGeometryHook(DependencyObject d) =>
|
||||
d.GetValue(SyncWithColumnGeometryHookPropertyKey.DependencyProperty) as IDisposable;
|
||||
|
||||
static void SetSyncWithColumnGeometryHook(DependencyObject d, IDisposable value) =>
|
||||
d.SetValue(SyncWithColumnGeometryHookPropertyKey, value);
|
||||
#endregion
|
||||
|
||||
public static readonly DependencyProperty BackgroundOverlayProperty =
|
||||
DependencyProperty.RegisterAttached(
|
||||
"BackgroundOverlay",
|
||||
@@ -855,7 +783,7 @@ namespace mbts.ui.controls {
|
||||
/// Compute the geometry of all visible (or partially visible columns) columns
|
||||
/// This effectively replaces the entire Measure/Arrange pass of the grid rows (and enables column virtualization)
|
||||
/// Mouse events will be hit tested against this as well
|
||||
internal IEnumerable<ColumnGeometry> GetColumnGeometry(bool include_obscured_cols = false) {
|
||||
internal IEnumerable<ColumnGeometry> GetColumnGeometry() {
|
||||
if (_header_scroll_viewer != null && ActualWidth > 0) {
|
||||
double frozen_offset = 0;
|
||||
int vis_frozen_col_count = _get_visible_frozen_column_count();
|
||||
@@ -877,7 +805,7 @@ namespace mbts.ui.controls {
|
||||
double left = Math.Max(offset, frozen_offset);
|
||||
double right = offset + col.ActualWidth;
|
||||
// If right < frozen_offset column is completely obscured
|
||||
if (include_obscured_cols || (right > frozen_offset && left <= ActualWidth)) {
|
||||
if (right > frozen_offset && left <= ActualWidth) {
|
||||
// fully visible
|
||||
yield return new ColumnGeometry() {
|
||||
column = col,
|
||||
@@ -955,98 +883,6 @@ namespace mbts.ui.controls {
|
||||
}
|
||||
#endregion
|
||||
|
||||
static class ColumnGeometrySyncHelper {
|
||||
public static void reset_panel_child(UIElement child, DIYGrid diy_grid) =>
|
||||
reset_panel_child(child, diy_grid, GetColumnKey(child));
|
||||
|
||||
public static void reset_panel_child(UIElement child, DIYGrid diy_grid, object col_key) {
|
||||
if (col_key == null)
|
||||
return;
|
||||
var key_str = col_key.ToString();
|
||||
if (diy_grid._visible_columns.TryGetFirst(c => string.Equals(c.id, key_str, StringComparison.CurrentCultureIgnoreCase), out var target_col, out int target_pos)) {
|
||||
// set clip to null in case this child was hidden before (see 'else' block).
|
||||
child.Clip = null;
|
||||
Grid.SetColumn(child, target_pos);
|
||||
} else {
|
||||
// hide element by setting clip to 0x0 rect, rather than visibility to collapsed,
|
||||
// to avoid incidentally breaking visibility bindings, triggers, animations, etc.
|
||||
child.Clip = new RectangleGeometry(new Rect(0, 0, 0, 0));
|
||||
}
|
||||
}
|
||||
|
||||
public static void reset_sync(DependencyObject d, DependencyPropertyChangedEventArgs e) {
|
||||
if (!GetSyncWithColumnGeometry(d))
|
||||
SetSyncWithColumnGeometryHook(d, null);
|
||||
if (!(d is Grid g))
|
||||
return;
|
||||
var hook = new DisposableContainer();
|
||||
if (g.IsLoaded)
|
||||
hook.replace(impl());
|
||||
SetSyncWithColumnGeometryHook(d, Disposable.Create(g.SubscribeLoaded(on_loaded), hook));
|
||||
//
|
||||
void on_loaded(object sender, RoutedEventArgs loaded_e) {
|
||||
if (GetSyncWithColumnGeometry(d))
|
||||
hook.replace(impl());
|
||||
}
|
||||
IDisposable impl() {
|
||||
// Avoid resetting children on every collection change, as that could be very noisy and hurt perf.
|
||||
// In the visible columns CollectionChanged handler, call reset_children inside a Dispatcher.InvokeAsync request
|
||||
// to effectively wait for potential batch-changes to finish.
|
||||
bool resetting_children = false;
|
||||
if (!(GetSyncWithColumnGeometrySource(d) is DIYGrid diy_grid) && !g.try_find_ancestor_of_type(out diy_grid))
|
||||
return null;
|
||||
g.SetBinding(Grid.WidthProperty, new Binding(nameof(HeaderTotalSizeHack)) { Source = diy_grid });
|
||||
reset_children();
|
||||
on_layout_changed(diy_grid.GetColumnGeometry(true).ToList());
|
||||
diy_grid.ColumnLayoutChanged += on_layout_changed;
|
||||
diy_grid._visible_columns.CollectionChanged += on_vis_columns_changed;
|
||||
return Disposable.Create(() => {
|
||||
diy_grid.ColumnLayoutChanged -= on_layout_changed;
|
||||
diy_grid._visible_columns.CollectionChanged -= on_vis_columns_changed;
|
||||
});
|
||||
//
|
||||
void on_vis_columns_changed(object sender, NotifyCollectionChangedEventArgs vce) {
|
||||
if (!resetting_children) {
|
||||
// avoid resetting children on every collection change, as that could be very noisy and hurt perf.
|
||||
resetting_children = true;
|
||||
diy_grid.Dispatcher.InvokeAsync(() => {
|
||||
on_layout_changed(diy_grid.GetColumnGeometry().ToList());
|
||||
reset_children();
|
||||
resetting_children = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
void on_layout_changed(List<ColumnGeometry> layout) {
|
||||
// TODO: figure out how to make the ColumnLayoutChanged event lazy, or somehow avoid making 2 lists of columns
|
||||
// (the layout param does not include obscured cols, so we have to create a new list of ColumnGeoemtry to satisfy our needs).
|
||||
layout = (from col in diy_grid._visible_columns
|
||||
join pos in diy_grid.GetColumnGeometry(true)
|
||||
on col.id equals pos.column.id
|
||||
select pos).ToList();
|
||||
|
||||
int n_removed = g.ColumnDefinitions.Count - layout.Count;
|
||||
if (n_removed > 0)
|
||||
g.ColumnDefinitions.RemoveRange(g.ColumnDefinitions.Count - n_removed, n_removed);
|
||||
int i = 0;
|
||||
for (; i < g.ColumnDefinitions.Count; i++) {
|
||||
var col_pos = layout[i];
|
||||
var col = g.ColumnDefinitions[i];
|
||||
if (col.Width.GridUnitType == GridUnitType.Pixel && col.Width.Value == col_pos.visible_width)
|
||||
continue;
|
||||
col.Width = new GridLength(col_pos.visible_width, GridUnitType.Pixel);
|
||||
}
|
||||
for (; i < layout.Count; i++) {
|
||||
g.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(layout[i].visible_width, GridUnitType.Pixel) });
|
||||
}
|
||||
}
|
||||
void reset_children() {
|
||||
foreach (UIElement child in g.Children)
|
||||
reset_panel_child(child, diy_grid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DataContextHooks : IDisposable {
|
||||
List<IDisposable> _hooks = new List<IDisposable>(4);
|
||||
|
||||
|
||||
@@ -29,6 +29,22 @@
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
/>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="Selector.IsSelectionActive" Value="False"/>
|
||||
<Condition Property="IsSelected" Value="True"/>
|
||||
</MultiTrigger.Conditions>
|
||||
<Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static bts:DIYGrid.InactiveSelectionHighlightKey}}"/>
|
||||
</MultiTrigger>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="Selector.IsSelectionActive" Value="True"/>
|
||||
<Condition Property="IsSelected" Value="True"/>
|
||||
</MultiTrigger.Conditions>
|
||||
<Setter Property="Background" TargetName="Bd" Value="{Binding SelectionHighlight, RelativeSource={RelativeSource AncestorType=bts:DIYGrid}}"/>
|
||||
</MultiTrigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
@@ -45,9 +61,6 @@
|
||||
<Setter Property="IsTextSearchEnabled" Value="False"/>
|
||||
</Style>
|
||||
<Style TargetType="bts:DIYGrid">
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="BorderBrush" Value="Transparent"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="Focusable" Value="False"/>
|
||||
<Setter Property="SelectionMode" Value="Extended"/>
|
||||
<Setter Property="ColumnHeaderStyle">
|
||||
@@ -291,72 +304,66 @@
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type bts:DIYGrid}">
|
||||
<Border
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<!-- Frozen -->
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<!-- fill the space above the frozen columns -->
|
||||
<Rectangle Fill="{mbts:BTSColor Name=header}" Height="{Binding Path=ActualHeight, ElementName=_super_headers}" HorizontalAlignment="Stretch" Margin="0,0,-1,-1"/>
|
||||
<!-- colums set in code behind -->
|
||||
<GridViewHeaderRowPresenter x:Name="_header_frozen" Grid.Row="1" Grid.Column="0" AllowsColumnReorder="False" VerticalAlignment="Bottom" ColumnHeaderContainerStyle="{TemplateBinding ColumnHeaderStyle}" UseLayoutRounding="True"/>
|
||||
<ScrollViewer
|
||||
x:Name="_header_scroll_viewer"
|
||||
Grid.Column="1"
|
||||
Grid.ColumnSpan="2"
|
||||
Grid.Row="0"
|
||||
Grid.RowSpan="2"
|
||||
HorizontalScrollBarVisibility="Hidden"
|
||||
VerticalScrollBarVisibility="Hidden"
|
||||
>
|
||||
<!-- setting the bg color for this stackpanel is a work-around for the vertical white lines that appear on high-dpi displays between some columns -->
|
||||
<!-- in reality, there is some high dpi rounding thing going on, but that's a battle for another day -->
|
||||
<StackPanel Background="{mbts:BTSColor Name=header}">
|
||||
<!-- We don't need super headers over the fixed columns -->
|
||||
<StackPanel Orientation="Horizontal" x:Name="_super_headers"/>
|
||||
<GridViewHeaderRowPresenter x:Name="_header_scroll" AllowsColumnReorder="False" ColumnHeaderContainerStyle="{TemplateBinding ColumnHeaderStyle}" UseLayoutRounding="True"/>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
<bts:DIYGridRowsPresenter
|
||||
x:Name="_row_presenter"
|
||||
Grid.Row="2"
|
||||
Grid.ColumnSpan="2"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Hidden"
|
||||
VirtualizingPanel.VirtualizationMode="Recycling"
|
||||
AlternationCount="2"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderThickness="0"
|
||||
ItemsSource="{TemplateBinding Rows}"
|
||||
ItemTemplate="{TemplateBinding RowTemplate}"
|
||||
SelectedItem="{Binding CurrentRow, RelativeSource={RelativeSource TemplatedParent}}"
|
||||
SelectionMode="{TemplateBinding SelectionMode}"
|
||||
>
|
||||
<bts:DIYGridRowsPresenter.Resources>
|
||||
<Style TargetType="ListBoxItem" BasedOn="{StaticResource DIYGridRowStyle}"/>
|
||||
</bts:DIYGridRowsPresenter.Resources>
|
||||
<bts:DIYGridRowsPresenter.Template>
|
||||
<ControlTemplate TargetType="{x:Type bts:DIYGridRowsPresenter}">
|
||||
<ScrollViewer Focusable="False">
|
||||
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
|
||||
</ScrollViewer>
|
||||
</ControlTemplate>
|
||||
</bts:DIYGridRowsPresenter.Template>
|
||||
</bts:DIYGridRowsPresenter>
|
||||
<ScrollBar Grid.Row="3" Grid.Column="1" Orientation="Horizontal" LargeChange="10" x:Name="_scroll_horiz" Visibility="{TemplateBinding RowsHorizontalScrollBarVisibility}"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<!-- Frozen -->
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<!-- fill the space above the frozen columns -->
|
||||
<Rectangle Fill="{mbts:BTSColor Name=header}" Height="{Binding Path=ActualHeight, ElementName=_super_headers}" HorizontalAlignment="Stretch" Margin="0,0,-1,-1"/>
|
||||
<!-- colums set in code behind -->
|
||||
<GridViewHeaderRowPresenter x:Name="_header_frozen" Grid.Row="1" Grid.Column="0" AllowsColumnReorder="False" VerticalAlignment="Bottom" ColumnHeaderContainerStyle="{TemplateBinding ColumnHeaderStyle}" UseLayoutRounding="True"/>
|
||||
<ScrollViewer
|
||||
x:Name="_header_scroll_viewer"
|
||||
Grid.Column="1"
|
||||
Grid.ColumnSpan="2"
|
||||
Grid.Row="0"
|
||||
Grid.RowSpan="2"
|
||||
HorizontalScrollBarVisibility="Hidden"
|
||||
VerticalScrollBarVisibility="Hidden"
|
||||
>
|
||||
<!-- setting the bg color for this stackpanel is a work-around for the vertical white lines that appear on high-dpi displays between some columns -->
|
||||
<!-- in reality, there is some high dpi rounding thing going on, but that's a battle for another day -->
|
||||
<StackPanel Background="{mbts:BTSColor Name=header}">
|
||||
<!-- We don't need super headers over the fixed columns -->
|
||||
<StackPanel Orientation="Horizontal" x:Name="_super_headers"/>
|
||||
<GridViewHeaderRowPresenter x:Name="_header_scroll" AllowsColumnReorder="False" ColumnHeaderContainerStyle="{TemplateBinding ColumnHeaderStyle}" UseLayoutRounding="True"/>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
<bts:DIYGridRowsPresenter
|
||||
x:Name="_row_presenter"
|
||||
Grid.Row="2"
|
||||
Grid.ColumnSpan="2"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Hidden"
|
||||
VirtualizingPanel.VirtualizationMode="Recycling"
|
||||
AlternationCount="2"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderThickness="0"
|
||||
ItemsSource="{TemplateBinding Rows}"
|
||||
ItemTemplate="{TemplateBinding RowTemplate}"
|
||||
SelectedItem="{Binding CurrentRow, RelativeSource={RelativeSource TemplatedParent}}"
|
||||
SelectionMode="{TemplateBinding SelectionMode}"
|
||||
>
|
||||
<bts:DIYGridRowsPresenter.Resources>
|
||||
<Style TargetType="ListBoxItem" BasedOn="{StaticResource DIYGridRowStyle}"/>
|
||||
</bts:DIYGridRowsPresenter.Resources>
|
||||
<bts:DIYGridRowsPresenter.Template>
|
||||
<ControlTemplate TargetType="{x:Type bts:DIYGridRowsPresenter}">
|
||||
<ScrollViewer Focusable="False">
|
||||
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
|
||||
</ScrollViewer>
|
||||
</ControlTemplate>
|
||||
</bts:DIYGridRowsPresenter.Template>
|
||||
</bts:DIYGridRowsPresenter>
|
||||
<ScrollBar Grid.Row="3" Grid.Column="1" Orientation="Horizontal" LargeChange="10" x:Name="_scroll_horiz" Visibility="{TemplateBinding RowsHorizontalScrollBarVisibility}"/>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
|
||||
@@ -60,9 +60,6 @@ namespace mbts.ui.settings {
|
||||
[DefaultValue(_default_horizontal_offset)]
|
||||
readonly double _horizontal_offset;
|
||||
|
||||
[ProtoMember(10)]
|
||||
readonly NullableColorProto _background_color_override;
|
||||
|
||||
public GridColumnSettings() {
|
||||
_is_visible = _default_is_visible;
|
||||
_is_collapsed = _default_is_collapsed;
|
||||
@@ -72,7 +69,7 @@ namespace mbts.ui.settings {
|
||||
}
|
||||
|
||||
public GridColumnSettings(string id, string display_name = null, bool is_visible = _default_is_visible, bool is_collapsed = _default_is_collapsed, FontWeight? font_weight_override = null, Color? font_color_override = null,
|
||||
double width = _default_width, HorizontalAlignment horizontal_alignment = _default_horizontal_alignment, double horizontal_offset = _default_horizontal_offset, Color? background_color_override = null) {
|
||||
double width = _default_width, HorizontalAlignment horizontal_alignment = _default_horizontal_alignment, double horizontal_offset = _default_horizontal_offset) {
|
||||
_id = id;
|
||||
_display_name = display_name;
|
||||
_is_visible = is_visible;
|
||||
@@ -82,7 +79,6 @@ namespace mbts.ui.settings {
|
||||
_width = width;
|
||||
_horizontal_alignment = horizontal_alignment;
|
||||
_horizontal_offset = horizontal_offset;
|
||||
_background_color_override = new NullableColorProto(background_color_override);
|
||||
}
|
||||
|
||||
public string id => _id;
|
||||
@@ -94,11 +90,10 @@ namespace mbts.ui.settings {
|
||||
public HorizontalAlignment horizontal_alignment => _horizontal_alignment;
|
||||
public double horizontal_offset => _horizontal_offset;
|
||||
public string display_name => _display_name;
|
||||
public Color? background_color_override => _background_color_override.argb;
|
||||
|
||||
public GridColumnSettings with(
|
||||
string display_name = null, bool? is_visible = null, bool? is_collapsed = null, NullableFontWeightProto? font_weight_override = null, NullableColorProto? font_color_override = null,
|
||||
double? width = null, HorizontalAlignment? horizontal_alignment = null, double? horizontal_offset = null, NullableColorProto? background_color_override = null) =>
|
||||
double? width = null, HorizontalAlignment? horizontal_alignment = null, double? horizontal_offset = null) =>
|
||||
//
|
||||
new GridColumnSettings(_id,
|
||||
display_name ?? this.display_name,
|
||||
@@ -108,8 +103,7 @@ namespace mbts.ui.settings {
|
||||
(font_color_override ?? _font_color_override).value,
|
||||
width ?? this.width,
|
||||
horizontal_alignment ?? this.horizontal_alignment,
|
||||
horizontal_offset ?? this.horizontal_offset,
|
||||
(background_color_override ?? _background_color_override).argb);
|
||||
horizontal_offset ?? this.horizontal_offset);
|
||||
|
||||
public bool Equals(GridColumnSettings other) =>
|
||||
other != null &&
|
||||
@@ -122,8 +116,7 @@ namespace mbts.ui.settings {
|
||||
width == other.width &&
|
||||
horizontal_alignment == other.horizontal_alignment &&
|
||||
horizontal_offset == other.horizontal_offset &&
|
||||
display_name == other.display_name &&
|
||||
background_color_override.Equals(other.background_color_override);
|
||||
display_name == other.display_name;
|
||||
|
||||
public override bool Equals(object obj) =>
|
||||
Equals(obj as GridColumnSettings);
|
||||
@@ -139,8 +132,7 @@ namespace mbts.ui.settings {
|
||||
width.GetHashCode(),
|
||||
horizontal_alignment.GetHashCode(),
|
||||
horizontal_offset.GetHashCode(),
|
||||
display_name.GetHashCode(),
|
||||
background_color_override.GetHashCode()
|
||||
display_name.GetHashCode()
|
||||
);
|
||||
|
||||
public override string ToString() {
|
||||
|
||||
@@ -22,13 +22,11 @@ namespace mbts.spark {
|
||||
/// </para>
|
||||
/// For flag behavior implementation, see <see cref="GridSettingsVM{TRowData, TPredicateBuilder}.ColumnVM.apply_flags(GridColumnSettingsFeatureFlags)"/>
|
||||
/// </summary>
|
||||
[Flags]
|
||||
enum GridColumnSettingsFeatureFlags {
|
||||
None = 0,
|
||||
ImmutableFontColor = 1,
|
||||
ImmutableFontWeight = 1 << 1,
|
||||
ImmutableHeader = 1 << 2,
|
||||
ImmutableBackgroundColor = 1 << 3
|
||||
ImmutableHeader = 1 << 2
|
||||
}
|
||||
|
||||
partial class GridSettingsVM<TRowData, TPredicateData, TSettingsData> {
|
||||
@@ -52,9 +50,7 @@ namespace mbts.spark {
|
||||
_can_override_font_color = true,
|
||||
_is_font_color_overridden,
|
||||
_can_override_font_weight = true,
|
||||
_is_font_weight_overridden,
|
||||
_can_override_background_color = true,
|
||||
_is_background_color_overridden;
|
||||
_is_font_weight_overridden;
|
||||
FontWeight _font_weight_override;
|
||||
GridColumnSettings _settings;
|
||||
|
||||
@@ -82,10 +78,6 @@ namespace mbts.spark {
|
||||
public bool can_override_font_weight { get => _can_override_font_weight; private set => Set(ref _can_override_font_weight, value); }
|
||||
public bool is_font_weight_overridden { get => _is_font_weight_overridden; set => Set(ref _is_font_weight_overridden, value); }
|
||||
public FontWeight font_weight_override { get => _font_weight_override; set => Set(ref _font_weight_override, value); }
|
||||
// background color override
|
||||
public bool can_override_background_color { get => _can_override_background_color; private set => Set(ref _can_override_background_color, value); }
|
||||
public bool is_background_color_overridden { get => _is_background_color_overridden; set => Set(ref _is_background_color_overridden, value); }
|
||||
public ColorPickerPopupVM background_color_override { get; } = new ColorPickerPopupVM() { bind_opacity_to_alpha = true };
|
||||
// animations
|
||||
public ObservableEditableCollection<PredicateCellAnimationVM> predicate_animations { get; }
|
||||
|
||||
@@ -97,9 +89,7 @@ namespace mbts.spark {
|
||||
is_font_weight_overridden ? new NullableFontWeightProto(font_weight_override) : default,
|
||||
is_font_color_overridden ? new NullableColorProto(font_color_override.color) : default,
|
||||
width,
|
||||
horizontal_alignment,
|
||||
background_color_override: is_background_color_overridden ?
|
||||
new NullableColorProto(background_color_override.color) : default);
|
||||
horizontal_alignment);
|
||||
var animations_lookup = predicate_animations.ToFlatLookup(pa => pa.predicate.create_assigned(owner._predicate_data_factory), pa => pa.animations.Select(a => a.create()));
|
||||
return (settings, animations_lookup);
|
||||
}
|
||||
@@ -121,11 +111,6 @@ namespace mbts.spark {
|
||||
if (is_font_weight_overridden) {
|
||||
_font_weight_override = _settings.font_weight_override.Value;
|
||||
}
|
||||
// background color
|
||||
is_background_color_overridden = _settings.background_color_override.HasValue;
|
||||
if (is_background_color_overridden) {
|
||||
background_color_override.color = _settings.background_color_override.Value;
|
||||
}
|
||||
predicate_animations.SetItems(value.animations.empty_if_null().Select(g => {
|
||||
var vm = new PredicateCellAnimationVM(id, owner._create_column_predicate_vm(id), owner._predicate_data_factory);
|
||||
vm.load_from((g.Key, g));
|
||||
@@ -144,9 +129,6 @@ namespace mbts.spark {
|
||||
// font weight
|
||||
can_override_font_weight = (flags & GridColumnSettingsFeatureFlags.ImmutableFontWeight) == 0;
|
||||
is_font_weight_overridden &= can_override_font_weight;
|
||||
// background color
|
||||
can_override_background_color = (flags & GridColumnSettingsFeatureFlags.ImmutableBackgroundColor) == 0;
|
||||
is_background_color_overridden &= can_override_background_color;
|
||||
}
|
||||
|
||||
public override string ToString() => $"{display_name} ({id})";
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using bts.collections;
|
||||
using bts.proto;
|
||||
using bts.proto;
|
||||
using bts.utils;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.converters;
|
||||
@@ -7,7 +6,6 @@ using ProtoBuf;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Media;
|
||||
@@ -19,14 +17,9 @@ namespace mbts.ui.settings {
|
||||
const double _default_font_size = 11;
|
||||
const uint _default_font_color = 0;
|
||||
const ushort _default_font_weight = 400;
|
||||
const uint _default_background_color = 0x00FFFFFF; // Colors.Transparent
|
||||
const bool _default_enable_background_color = false;
|
||||
|
||||
public static GridSettings Default =>
|
||||
new GridSettings(
|
||||
selection_highlight: new ColorOpacityProto(FallbackValues.selection_highlight.Color, FallbackValues.selection_highlight.Opacity),
|
||||
background_color: _default_background_color, //new ColorOpacityProto(FallbackValues.background_color.Color, FallbackValues.background_color.Opacity));
|
||||
enable_background_color: _default_enable_background_color);
|
||||
new GridSettings(selection_highlight: new ColorOpacityProto(FallbackValues.selection_highlight.Color, FallbackValues.selection_highlight.Opacity));
|
||||
|
||||
// corrects any inappropriate values that would render the grid unusable (usually nulls)
|
||||
// NOTE: if you add a property to GridSettings that could have such a value, please add a conditional-correction for it below)
|
||||
@@ -46,7 +39,6 @@ namespace mbts.ui.settings {
|
||||
public static Color font_color => _default_font_color.rgb_color();
|
||||
public static FontWeight font_weight => FontWeight.FromOpenTypeWeight(_default_font_weight);
|
||||
public static SolidColorBrush selection_highlight => DIYGrid.DefaultSelectionHighlight;
|
||||
public static uint background_color => _default_background_color;
|
||||
}
|
||||
|
||||
public static IValueConverter selection_highlight_converter => ValueConverter.create<GridSettings, SolidColorBrush>(gs => gs.selection_highlight.to_brush().frozen());
|
||||
@@ -73,26 +65,17 @@ namespace mbts.ui.settings {
|
||||
List<GridColumnSettings> _columns;
|
||||
[ProtoMember(7)]
|
||||
List<SortingCriteria> _sorting_criterion;
|
||||
[ProtoMember(8)]
|
||||
readonly uint _background_color;
|
||||
[ProtoMember(9)]
|
||||
[DefaultValue(false)]
|
||||
readonly bool _enable_background_color;
|
||||
|
||||
public GridSettings() {
|
||||
_font_family = _default_font_family;
|
||||
_font_size = _default_font_size;
|
||||
_font_weight = _default_font_weight;
|
||||
_font_color = _default_font_color;
|
||||
|
||||
// new setting in proto defaulted here
|
||||
_background_color = _default_background_color;
|
||||
_enable_background_color = _default_enable_background_color;
|
||||
}
|
||||
|
||||
public GridSettings(
|
||||
string font_family = _default_font_family, double font_size = _default_font_size, FontWeight font_weight = default, uint font_color = _default_font_color,
|
||||
ColorOpacityProto? selection_highlight = null, IEnumerable<GridColumnSettings> columns = null, IEnumerable<SortingCriteria> sorting_criterion = null, uint background_color = _default_background_color, bool enable_background_color = _default_enable_background_color) {
|
||||
ColorOpacityProto? selection_highlight = null, IEnumerable<GridColumnSettings> columns = null, IEnumerable<SortingCriteria> sorting_criterion = null) {
|
||||
//
|
||||
_font_family = font_family;
|
||||
_font_size = font_size;
|
||||
@@ -101,8 +84,6 @@ namespace mbts.ui.settings {
|
||||
_selection_highlight = selection_highlight ?? new ColorOpacityProto(0, 1);
|
||||
_columns = new List<GridColumnSettings>(columns.empty_if_null());
|
||||
_sorting_criterion = new List<SortingCriteria>(sorting_criterion.empty_if_null());
|
||||
_background_color = background_color;
|
||||
_enable_background_color = enable_background_color;
|
||||
}
|
||||
|
||||
public string font_family => _font_family;
|
||||
@@ -110,17 +91,14 @@ namespace mbts.ui.settings {
|
||||
public uint font_color => _font_color;
|
||||
public FontWeight font_weight => FontWeight.FromOpenTypeWeight(_font_weight);
|
||||
public ColorOpacityProto selection_highlight => _selection_highlight;
|
||||
public uint background_color => _background_color;
|
||||
public bool enable_background_color => _enable_background_color;
|
||||
public IReadOnlyList<GridColumnSettings> columns => _columns ?? (_columns = new List<GridColumnSettings>(0));
|
||||
[Obsolete] // TODO: remove sorting_criterion from GridSettings entirely. This should be part of IShellWindow's StartupData, since it is an instance-specific setting, and prob not part of the shared defaults
|
||||
public IReadOnlyList<SortingCriteria> sorting_criterion => _sorting_criterion ?? (_sorting_criterion = new List<SortingCriteria>(0));
|
||||
|
||||
public static GridSettings get_default() =>
|
||||
new GridSettings(_default_font_family, _default_font_size, FontWeights.Normal, _default_font_color, new ColorOpacityProto(DIYGrid.DefaultSelectionHighlight.Color, DIYGrid.DefaultSelectionHighlight.Opacity),
|
||||
background_color: _default_background_color);
|
||||
new GridSettings(_default_font_family, _default_font_size, FontWeights.Normal, _default_font_color, new ColorOpacityProto(Colors.MediumSlateBlue, 0.4));
|
||||
|
||||
public GridSettings with(IEnumerable<GridColumnSettings> columns = null, IEnumerable<SortingCriteria> sorting_criterion = null, string font_family = null, double font_size = double.NaN, bool? enable_background_color = null) =>
|
||||
public GridSettings with(IEnumerable<GridColumnSettings> columns = null, IEnumerable<SortingCriteria> sorting_criterion = null, string font_family = null, double font_size = double.NaN) =>
|
||||
new GridSettings(
|
||||
font_family ?? _font_family,
|
||||
DoubleHelper.IsNaNOrInfinityOr(font_size, 0) ? _font_size : font_size,
|
||||
@@ -128,9 +106,7 @@ namespace mbts.ui.settings {
|
||||
_font_color,
|
||||
_selection_highlight,
|
||||
columns ?? _columns,
|
||||
sorting_criterion ?? _sorting_criterion,
|
||||
_background_color,
|
||||
enable_background_color ?? _enable_background_color);
|
||||
sorting_criterion ?? _sorting_criterion);
|
||||
|
||||
public GridSettings clone() => with();
|
||||
|
||||
@@ -143,10 +119,8 @@ namespace mbts.ui.settings {
|
||||
selection_highlight == other.selection_highlight &&
|
||||
EqualityComparer.safe_sequence_equals(columns, other.columns) &&
|
||||
#pragma warning disable CS0612 // Type or member is obsolete
|
||||
EqualityComparer.safe_sequence_equals(sorting_criterion, other.sorting_criterion) &&
|
||||
EqualityComparer.safe_sequence_equals(sorting_criterion, other.sorting_criterion);
|
||||
#pragma warning restore CS0612 // Type or member is obsolete
|
||||
background_color == other.background_color &&
|
||||
enable_background_color == other.enable_background_color;
|
||||
|
||||
public override bool Equals(object obj) =>
|
||||
Equals(obj as GridSettings);
|
||||
@@ -160,37 +134,7 @@ namespace mbts.ui.settings {
|
||||
selection_highlight.GetHashCode(),
|
||||
EqualityComparer.safe_aggregate_hash(columns),
|
||||
#pragma warning disable CS0612 // Type or member is obsolete
|
||||
EqualityComparer.safe_aggregate_hash(sorting_criterion),
|
||||
EqualityComparer.safe_aggregate_hash(sorting_criterion));
|
||||
#pragma warning restore CS0612 // Type or member is obsolete
|
||||
background_color.GetHashCode(),
|
||||
enable_background_color.GetHashCode());
|
||||
}
|
||||
|
||||
static class GridSettingsExtensions {
|
||||
public static bool add_missing_columns(this GridSettings grid, IEnumerable<GridColumnSettings> required_columns, out GridSettings result) {
|
||||
var col_lookup = new Lazy<ILookup<string, GridColumnSettings>>(() => required_columns.ToLookup(c => c.id));
|
||||
return add_missing_columns(grid, required_columns.Select(c => c.id), id => col_lookup.Value.TryGetValue(id, out var matches) ? matches.FirstOrDefault() : null, out result);
|
||||
}
|
||||
|
||||
public static bool add_missing_columns(this GridSettings grid, IEnumerable<string> col_ids, Func<string, GridColumnSettings> col_factory, out GridSettings result) {
|
||||
// add the new column (RawDayPos), if absent.
|
||||
var cols_to_add = new List<GridColumnSettings>(0);
|
||||
foreach (var col_id in col_ids) {
|
||||
if (!grid.columns.Any(c => c.id == col_id)) {
|
||||
var default_col = col_factory(col_id);
|
||||
if (default_col != null)
|
||||
cols_to_add.Add(default_col);
|
||||
}
|
||||
}
|
||||
if (cols_to_add.Count > 0) {
|
||||
var col_list = grid.columns.ToList();
|
||||
col_list.AddRange(cols_to_add);
|
||||
grid = grid.with(col_list);
|
||||
result = grid;
|
||||
return true;
|
||||
}
|
||||
result = grid;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@ namespace mbts.ui {
|
||||
readonly DelegateProperty<FontFamily> _font_family;
|
||||
readonly DelegateProperty<FontWeight> _font_weight;
|
||||
readonly DelegateProperty<double> _font_size;
|
||||
readonly DelegateProperty<SolidColorBrush> _font_color, _selection_highlight, _background_color;
|
||||
readonly DelegateProperty<SolidColorBrush> _font_color, _selection_highlight;
|
||||
readonly SetMap<IPredicate<TRowData>, AnimationDefinitionWrapper<TRow>> _animation_defs = new SetMap<IPredicate<TRowData>, AnimationDefinitionWrapper<TRow>>();
|
||||
readonly Func<TPredicateData> _predicate_data_factory;
|
||||
readonly Func<TPredicateData, IPredicate<TRowData>> _predicate_factory;
|
||||
@@ -77,7 +77,6 @@ namespace mbts.ui {
|
||||
_font_size = backing(nameof(font_size), () => grid_settings.font_size, nameof(font_size));
|
||||
_font_color = backing(nameof(font_color), () => grid_settings.font_color.rgb_solid_brush().frozen(), nameof(grid_settings));
|
||||
_selection_highlight = backing(nameof(selection_highlight), () => grid_settings.selection_highlight.to_brush().frozen(), nameof(grid_settings));
|
||||
_background_color = backing(nameof(background_color), () => grid_settings.background_color.argb_color().to_solid_brush().frozen(), nameof(grid_settings));
|
||||
}
|
||||
|
||||
public void Dispose() =>
|
||||
@@ -89,7 +88,6 @@ namespace mbts.ui {
|
||||
public double font_size => _font_size.Value;
|
||||
public SolidColorBrush font_color => _font_color.Value;
|
||||
public SolidColorBrush selection_highlight => _selection_highlight.Value;
|
||||
public SolidColorBrush background_color => _background_color.Value;
|
||||
|
||||
public GridSettingsVM<TRowData, TPredicateData, TSettingsData> create_vm(GridSettings settings) {
|
||||
var vm = new GridSettingsVM<TRowData, TPredicateData, TSettingsData>(_bus, _orders_model, _predicate_data_factory, _notification_predicate_factory, _create_predicate_vm, _create_column_predicate_vm, _create_settings_obj, _column_flags.ToDictionary(kv => kv.Key, kv => kv.Value.flags));
|
||||
@@ -114,7 +112,7 @@ namespace mbts.ui {
|
||||
var grid = value.general ?? GridSettings.Default;
|
||||
_grid_settings.set(grid);
|
||||
foreach (ColumnBase col in _columns)
|
||||
col.set_fallback_font_params(new FontFamily(grid.font_family), grid.font_size, grid.font_color.rgb_color(), grid.font_weight, grid.background_color.argb_color());
|
||||
col.set_fallback_font_params(new FontFamily(grid.font_family), grid.font_size, grid.font_color.rgb_color(), grid.font_weight);
|
||||
_predicate_animations_data = value.predicate_animations;
|
||||
_animation_defs.clear();
|
||||
foreach (var pa in _predicate_animations_data) {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using bts.api;
|
||||
using bts.collections;
|
||||
using bts.proto;
|
||||
using bts.shell;
|
||||
using bts.utils;
|
||||
using mbts.collections;
|
||||
@@ -57,7 +56,7 @@ namespace mbts.spark {
|
||||
readonly Property<FontWeight> _font_weight;
|
||||
readonly Property<string> _font_display_name;
|
||||
readonly Property<double> _font_size;
|
||||
readonly Property<bool> _show_predicate_animations, _show_cell_predicate_animations, _show_background_color;
|
||||
readonly Property<bool> _show_predicate_animations, _show_cell_predicate_animations;
|
||||
readonly OrdersModel _orders_model;
|
||||
readonly IReadOnlyDictionary<string, GridColumnSettingsFeatureFlags> _column_flags;
|
||||
|
||||
@@ -84,7 +83,6 @@ namespace mbts.spark {
|
||||
_font_display_name = backing(nameof(font_display_name), () => TextUtils.display_name(font_family, font_size), new[] { nameof(font_family), nameof(font_size) });
|
||||
_show_predicate_animations = backing(nameof(show_predicate_animations), true);
|
||||
_show_cell_predicate_animations = backing(nameof(show_cell_predicate_animations), true);
|
||||
_show_background_color = backing(nameof(show_background_color), false);
|
||||
_current_column = backing<IColumnVM>(nameof(current_column));
|
||||
predicate_animations = new ObservableEditableCollection<PredicateRowAnimationVM>(() =>
|
||||
new PredicateRowAnimationVM(_create_predicate_vm(), _predicate_data_factory), true);
|
||||
@@ -102,8 +100,6 @@ namespace mbts.spark {
|
||||
public FontWeight font_weight { get => _font_weight.Value; set => _font_weight.set(value); }
|
||||
public ColorPickerPopupVM font_color { get; } = new ColorPickerPopupVM();
|
||||
public ColorPickerPopupVM selection_highlight { get; } = new ColorPickerPopupVM();
|
||||
public ColorPickerPopupVM background_color { get; } = new ColorPickerPopupVM();
|
||||
public bool show_background_color => _show_background_color.Value;
|
||||
public double font_size { get => _font_size.Value; set => _font_size.set(value); }
|
||||
public bool enable_sound_triggers => true;
|
||||
public bool show_predicate_animations { get => _show_predicate_animations.Value; set => _show_predicate_animations.set(value); }
|
||||
@@ -134,8 +130,7 @@ namespace mbts.spark {
|
||||
foreach (IGrouping<TPredicateData, CellAnimation> g in animations)
|
||||
animation_map.AddOrUpdate(g.Key, p => animation_tuple(c: g), (p, t) => t.cell_animations.AddRange(g));
|
||||
}
|
||||
return _create_settings_obj(new GridSettings(font_family.name(), font_size, font_weight, font_color.color.rgb_color(), selection_highlight.to_proto(), col_settings,
|
||||
background_color: background_color.color.with((byte)(MathUtils.within_range(0, 1, background_color.opacity) * 255.0)).rgb_color()),
|
||||
return _create_settings_obj(new GridSettings(font_family.name(), font_size, font_weight, font_color.color.rgb_color(), selection_highlight.to_proto(), col_settings),
|
||||
animation_map.Select(kv => PredicateAnimations.create_data<TRowData, TPredicateData>(kv.Key, kv.Value.cell_animations, kv.Value.row_animations, kv.Value.sounds)));
|
||||
//
|
||||
(List<RowAnimation> row_animations, List<SoundNotification> sounds, List<CellAnimation> cell_animations) animation_tuple(IEnumerable<RowAnimation> r = null, IEnumerable<SoundNotification> s = null, IGrouping<TPredicateData, CellAnimation> c = null) =>
|
||||
@@ -147,14 +142,6 @@ namespace mbts.spark {
|
||||
_font_weight.set(sd.general.font_weight);
|
||||
font_color.load_from(sd.general.font_color.rgb_solid_brush());
|
||||
selection_highlight.load_from(sd.general.selection_highlight.to_brush());
|
||||
_show_background_color.set(sd.general.enable_background_color);
|
||||
if (sd.general.enable_background_color) {
|
||||
// note that when the OB is moved to allow background colors
|
||||
// we can just background_color.color instead of load_from
|
||||
// so that we get all the alpha goodness similar to SimpleGridSettingsVM
|
||||
// assuming bind_opacity_to_alpha on the picker.
|
||||
background_color.load_from(sd.general.background_color.argb_color().to_solid_brush());
|
||||
}
|
||||
_font_size.set(sd.general.font_size);
|
||||
var col_animations = new Dictionary<string, Dictionary<TPredicateData, List<CellAnimation>>>();
|
||||
predicate_animations.Clear();
|
||||
|
||||
@@ -642,8 +642,6 @@
|
||||
<RowDefinition Height="{Binding Source={StaticResource VerticalPush}}"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="{Binding Source={StaticResource VerticalPush}}"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="{Binding Source={StaticResource VerticalPush}}"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
@@ -674,16 +672,6 @@
|
||||
Visibility="{Binding DataContext.current_column.is_font_color_overridden, Converter={StaticResource BooleanToVisibilityConverter}, RelativeSource={RelativeSource AncestorType=usp:GridSettingsView}}"
|
||||
/>
|
||||
<ComboBox Grid.Row="8" Grid.Column="2" Style="{StaticResource DisabledPlaceholderComboBox}" Visibility="{Binding current_column.is_font_color_overridden, Converter={StaticResource FalseVisibilityConverter}}"/>
|
||||
<CheckBox Grid.Row="10" VerticalAlignment="Center" IsEnabled="{Binding current_column.can_override_background_color}" IsChecked="{Binding current_column.is_background_color_overridden}" Visibility="{Binding show_background_color, Converter={StaticResource BoolToVisibility}}">Override Background Color</CheckBox>
|
||||
<StackPanel Grid.Row="10" Grid.Column="2" Visibility="{Binding show_background_color, Converter={StaticResource BoolToVisibility}}">
|
||||
<c:ColorPickerPopup
|
||||
Grid.Row="10"
|
||||
Grid.Column="2"
|
||||
DataContext="{Binding current_column.background_color_override}"
|
||||
Visibility="{Binding DataContext.current_column.is_background_color_overridden, Converter={StaticResource BooleanToVisibilityConverter}, RelativeSource={RelativeSource AncestorType=usp:GridSettingsView}}"
|
||||
/>
|
||||
<ComboBox Grid.Row="10" Grid.Column="2" Style="{StaticResource DisabledPlaceholderComboBox}" Visibility="{Binding current_column.is_background_color_overridden, Converter={StaticResource FalseVisibilityConverter}}"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
<!--TODO: Make this visible when cell animations are ready-->
|
||||
|
||||
@@ -21,9 +21,6 @@ namespace mbts.ui {
|
||||
if ((flags & GridColumnSettingsFeatureFlags.ImmutableFontWeight) != 0) {
|
||||
stores_per_flag.Add(new ImmutableFontWeightOverrideStore());
|
||||
}
|
||||
if ((flags & GridColumnSettingsFeatureFlags.ImmutableBackgroundColor) != 0) {
|
||||
stores_per_flag.Add(new ImmutableBackgroundColorOverrideStore());
|
||||
}
|
||||
return new CompositeImmutableSettingsStore(stores_per_flag, flags);
|
||||
}
|
||||
|
||||
@@ -69,19 +66,6 @@ namespace mbts.ui {
|
||||
_value = new NullableFontWeightProto(settings.font_weight_override);
|
||||
}
|
||||
|
||||
class ImmutableBackgroundColorOverrideStore : ImmutableColumnSettingStore {
|
||||
NullableColorProto _value;
|
||||
|
||||
public ImmutableBackgroundColorOverrideStore()
|
||||
: base(GridColumnSettingsFeatureFlags.ImmutableBackgroundColor) { }
|
||||
|
||||
public override GridColumnSettings apply(GridColumnSettings settings) =>
|
||||
settings.with(background_color_override: _value);
|
||||
|
||||
public override void store(GridColumnSettings settings) =>
|
||||
_value = new NullableColorProto(settings.background_color_override);
|
||||
}
|
||||
|
||||
class CompositeImmutableSettingsStore : ImmutableColumnSettingStore {
|
||||
readonly IReadOnlyList<ImmutableColumnSettingStore> _stores;
|
||||
public CompositeImmutableSettingsStore(IReadOnlyList<ImmutableColumnSettingStore> stores, GridColumnSettingsFeatureFlags flags)
|
||||
|
||||
@@ -21,9 +21,7 @@ namespace mbts.spark {
|
||||
_can_override_font_color = true,
|
||||
_is_font_color_overridden,
|
||||
_can_override_font_weight = true,
|
||||
_is_font_weight_overridden,
|
||||
_can_override_background_color = true,
|
||||
_is_background_color_overridden;
|
||||
_is_font_weight_overridden;
|
||||
FontWeight _font_weight_override;
|
||||
GridColumnSettings _settings;
|
||||
|
||||
@@ -45,23 +43,16 @@ namespace mbts.spark {
|
||||
public bool can_override_font_weight { get => _can_override_font_weight; private set => Set(ref _can_override_font_weight, value); }
|
||||
public bool is_font_weight_overridden { get => _is_font_weight_overridden; set => Set(ref _is_font_weight_overridden, value); }
|
||||
public FontWeight font_weight_override { get => _font_weight_override; set => Set(ref _font_weight_override, value); }
|
||||
// background color override
|
||||
public bool can_override_background_color { get => _can_override_background_color; private set => Set(ref _can_override_background_color, value); }
|
||||
public bool is_background_color_overridden { get => _is_background_color_overridden; set => Set(ref _is_background_color_overridden, value); }
|
||||
public ColorPickerPopupVM background_color_override { get; } = new ColorPickerPopupVM();
|
||||
|
||||
public GridColumnSettings create() =>
|
||||
_settings.with(
|
||||
can_override_display_name ? display_name : null,
|
||||
is_visible,
|
||||
null,
|
||||
can_override_font_weight ? (is_font_weight_overridden ? new NullableFontWeightProto(font_weight_override) : new NullableFontWeightProto()) : (NullableFontWeightProto?)null,
|
||||
can_override_font_color ? (is_font_color_overridden ? new NullableColorProto(font_color_override.color) : new NullableColorProto()) : (NullableColorProto?)null,
|
||||
is_font_weight_overridden ? new NullableFontWeightProto(font_weight_override) : (NullableFontWeightProto?)null,
|
||||
is_font_color_overridden ? new NullableColorProto(font_color_override.color) : (NullableColorProto?)null,
|
||||
width,
|
||||
horizontal_alignment,
|
||||
background_color_override:
|
||||
can_override_background_color ? (is_background_color_overridden ? new NullableColorProto(background_color_override.color) : new NullableColorProto()) : (NullableColorProto ?)null
|
||||
);
|
||||
horizontal_alignment);
|
||||
|
||||
public void load_from(GridColumnSettings value) {
|
||||
_settings = value;
|
||||
@@ -80,14 +71,6 @@ namespace mbts.spark {
|
||||
if (is_font_weight_overridden) {
|
||||
_font_weight_override = _settings.font_weight_override.Value;
|
||||
}
|
||||
// background color
|
||||
is_background_color_overridden = _settings.background_color_override.HasValue;
|
||||
if (is_background_color_overridden) {
|
||||
background_color_override.load_from(
|
||||
new SolidColorBrush(_settings.background_color_override.Value) {
|
||||
//Opacity = _settings.background_color_override.Value.opacity
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void apply_flags(GridColumnSettingsFeatureFlags flags) {
|
||||
@@ -101,9 +84,6 @@ namespace mbts.spark {
|
||||
// font weight
|
||||
can_override_font_weight = (flags & GridColumnSettingsFeatureFlags.ImmutableFontWeight) == 0;
|
||||
is_font_weight_overridden &= can_override_font_weight;
|
||||
// background color
|
||||
can_override_background_color = (flags & GridColumnSettingsFeatureFlags.ImmutableBackgroundColor) == 0;
|
||||
is_background_color_overridden &= can_override_background_color;
|
||||
}
|
||||
|
||||
public override string ToString() => $"{display_name} ({id})";
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace mbts.ui {
|
||||
readonly DelegateProperty<FontFamily> _font_family;
|
||||
readonly DelegateProperty<FontWeight> _font_weight;
|
||||
readonly DelegateProperty<double> _font_size;
|
||||
readonly DelegateProperty<SolidColorBrush> _font_color, _selection_highlight, _background_color;
|
||||
readonly DelegateProperty<SolidColorBrush> _font_color, _selection_highlight;
|
||||
readonly IReadOnlyDictionary<string, ImmutableColumnSettingStore> _column_flags;
|
||||
|
||||
public SimpleGridSettingsManager(ObservableCollection<ColumnBase> columns)
|
||||
@@ -35,7 +35,6 @@ namespace mbts.ui {
|
||||
_font_size = backing(nameof(font_size), () => grid_settings.font_size, nameof(font_size));
|
||||
_font_color = backing(nameof(font_color), () => grid_settings.font_color.rgb_solid_brush().frozen(), nameof(grid_settings));
|
||||
_selection_highlight = backing(nameof(selection_highlight), () => grid_settings.selection_highlight.to_brush().frozen(), nameof(grid_settings));
|
||||
_background_color = backing(nameof(background_color), () => grid_settings.background_color.argb_color().to_solid_brush().frozen(), nameof(grid_settings));
|
||||
_row_vms = row_vms;
|
||||
}
|
||||
|
||||
@@ -45,7 +44,6 @@ namespace mbts.ui {
|
||||
public double font_size => _font_size.Value;
|
||||
public SolidColorBrush font_color => _font_color.Value;
|
||||
public SolidColorBrush selection_highlight => _selection_highlight.Value;
|
||||
public SolidColorBrush background_color => _background_color.Value;
|
||||
|
||||
public SimpleGridSettingsVM create_vm(GridSettings settings) {
|
||||
var vm = new SimpleGridSettingsVM(_column_flags.ToDictionary(kv => kv.Key, kv => kv.Value.flags));
|
||||
@@ -88,7 +86,7 @@ namespace mbts.ui {
|
||||
grid = grid ?? GridSettings.Default;
|
||||
_grid_settings.set(grid);
|
||||
foreach (ColumnBase col in _columns)
|
||||
col.set_fallback_font_params(new FontFamily(grid.font_family), grid.font_size, grid.font_color.rgb_color(), grid.font_weight, grid.background_color.argb_color());
|
||||
col.set_fallback_font_params(new FontFamily(grid.font_family), grid.font_size, grid.font_color.rgb_color(), grid.font_weight);
|
||||
// ** in the unfortunate event that we botch backwards compatability, and the columns don't bear a 1:1 relationship with the column-settings
|
||||
// don't add insult to injury by crashing **
|
||||
if (grid_settings.columns != null) {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using bts.collections;
|
||||
using bts.proto;
|
||||
using bts.utils;
|
||||
using mbts.ui;
|
||||
using mbts.ui.controls;
|
||||
@@ -37,7 +36,7 @@ namespace mbts.spark {
|
||||
readonly Property<FontWeight> _font_weight;
|
||||
readonly Property<string> _font_display_name;
|
||||
readonly Property<double> _font_size;
|
||||
readonly Property<bool> _show_predicate_animations, _show_cell_predicate_animations, _show_background_color;
|
||||
readonly Property<bool> _show_predicate_animations, _show_cell_predicate_animations;
|
||||
readonly DeferrableActionWrapper _update;
|
||||
readonly IReadOnlyDictionary<string, GridColumnSettingsFeatureFlags> _column_flags;
|
||||
|
||||
@@ -49,7 +48,6 @@ namespace mbts.spark {
|
||||
_font_display_name = backing(nameof(font_display_name), () => TextUtils.display_name(font_family, font_size), new[] { nameof(font_family), nameof(font_size) });
|
||||
_show_predicate_animations = backing(nameof(show_predicate_animations), true);
|
||||
_show_cell_predicate_animations = backing(nameof(show_cell_predicate_animations), true);
|
||||
_show_background_color = backing(nameof(show_background_color), false);
|
||||
_current_column = backing<ISimpleColumnVM>(nameof(current_column));
|
||||
// update
|
||||
_update = new DeferrableActionWrapper(() => result_changed?.Invoke());
|
||||
@@ -80,9 +78,7 @@ namespace mbts.spark {
|
||||
public FontFamily font_family { get => _font_family.Value; set => _font_family.set(value); }
|
||||
public FontWeight font_weight { get => _font_weight.Value; set => _font_weight.set(value); }
|
||||
public ColorPickerPopupVM font_color { get; } = new ColorPickerPopupVM();
|
||||
public ColorPickerPopupVM selection_highlight { get; } = new ColorPickerPopupVM(){ bind_opacity_to_alpha = true };
|
||||
public ColorPickerPopupVM background_color { get; } = new ColorPickerPopupVM() { bind_opacity_to_alpha = true };
|
||||
public bool show_background_color => _show_background_color.Value;
|
||||
public ColorPickerPopupVM selection_highlight { get; } = new ColorPickerPopupVM();
|
||||
public double font_size { get => _font_size.Value; set => _font_size.set(value); }
|
||||
public bool enable_sound_triggers => false;
|
||||
public bool show_predicate_animations { get => _show_predicate_animations.Value; set => _show_predicate_animations.set(value); }
|
||||
@@ -102,20 +98,12 @@ namespace mbts.spark {
|
||||
get {
|
||||
yield return font_color;
|
||||
yield return selection_highlight;
|
||||
yield return background_color;
|
||||
yield return this;
|
||||
}
|
||||
}
|
||||
|
||||
// Note that one of the responsibilites of create() is via instance_vm.get_result() in the ShellSettingsDialogVM.export_to_custom_cmd,
|
||||
// which is what handles saving .grid files. This means if you have introduced any optional args, say for example enable_background_color
|
||||
// that is not yet a feature of all windows at this point in time, 8/28/2019, you need to be sure it's explicity set if the proto
|
||||
// constructor parameter default has been initialized to different default value.
|
||||
public GridSettings create() =>
|
||||
new GridSettings(font_family.name(), font_size, font_weight, font_color.color.rgb_color(), selection_highlight.to_proto(),
|
||||
columns.Select(c => c.create()),
|
||||
background_color: show_background_color ? background_color.color.rgb_color() : GridSettings.FallbackValues.background_color,
|
||||
enable_background_color: show_background_color);
|
||||
new GridSettings(font_family.name(), font_size, font_weight, font_color.color.rgb_color(), selection_highlight.to_proto(), columns.Select(c => c.create()));
|
||||
|
||||
public void load_from(GridSettings value) {
|
||||
if (value == null)
|
||||
@@ -124,11 +112,7 @@ namespace mbts.spark {
|
||||
_font_family.set(new FontFamily(value.font_family));
|
||||
_font_weight.set(value.font_weight);
|
||||
font_color.load_from(value.font_color.rgb_solid_brush());
|
||||
selection_highlight.color = value.selection_highlight.argb_color;
|
||||
_show_background_color.set(value.enable_background_color);
|
||||
if (value.enable_background_color) {
|
||||
background_color.color = value.background_color.argb_color();
|
||||
}
|
||||
selection_highlight.load_from(value.selection_highlight.to_brush());
|
||||
_font_size.set(value.font_size);
|
||||
var curr_col_id = current_column?.id;
|
||||
columns.SetItems(value.columns.Select(col => {
|
||||
|
||||
@@ -110,11 +110,10 @@ namespace mbts.spark.filters {
|
||||
|
||||
class DynamicItemsFilter<TRow, TValue, TState> : FilterVMBase<TRow, TValue, TState>, IDisposable {
|
||||
readonly IEqualityComparer<TValue> _equality_comparer;
|
||||
readonly IComparer<TValue> _comparer;
|
||||
readonly Func<ISet<TValue>, TState> _get_state;
|
||||
readonly Func<TState, IObservable<IEnumerable<TValue>>> _load_state;
|
||||
readonly HashSet<TValue> _items_set, _selected_items_set;
|
||||
readonly ObservableCollection<TValue> _items = new ObservableCollection<TValue>();
|
||||
readonly ObservableCollection<TValue> _unsorted_items;
|
||||
bool _override_desired_selection;
|
||||
IDisposable _desired_items_sub, _items_sub;
|
||||
|
||||
@@ -130,7 +129,7 @@ namespace mbts.spark.filters {
|
||||
//
|
||||
_items_sub = items.OnDispatcher().Subscribe(new_items => {
|
||||
_items_set.SetItems(new_items.Concat(_selected_items_set));
|
||||
_sync_items();
|
||||
_unsorted_items.sync_with(_items_set);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -149,7 +148,7 @@ namespace mbts.spark.filters {
|
||||
_items_set.Remove(v);
|
||||
foreach (var v in update.added)
|
||||
_items_set.Add(v);
|
||||
_sync_items();
|
||||
_unsorted_items.sync_with(_items_set);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -165,12 +164,11 @@ namespace mbts.spark.filters {
|
||||
_get_state = get_state;
|
||||
_load_state = load_state;
|
||||
_equality_comparer = equality_comparer;
|
||||
_comparer = comparer;
|
||||
_selected_items_set = new HashSet<TValue>(_equality_comparer);
|
||||
_items_set = new HashSet<TValue>(_equality_comparer);
|
||||
items = ObservableAutoWrapper.create_read_only_wrapper(_items);
|
||||
_unsorted_items = new ObservableCollection<TValue>();
|
||||
items = comparer != null ? ObservableAutoWrapper.create_sorted_wrapper(_unsorted_items, comparer) : ObservableAutoWrapper.create_read_only_wrapper(_unsorted_items);
|
||||
selected_items.CollectionChanged += _on_selected_items_CollectionChanged;
|
||||
_sync_items();
|
||||
}
|
||||
|
||||
public IReadOnlyObservableCollection<TValue> items { get; }
|
||||
@@ -240,11 +238,9 @@ namespace mbts.spark.filters {
|
||||
var copy = new HashSet<TValue>(_selected_items_set, _equality_comparer);
|
||||
_selected_items_set.SetItems(dsi);
|
||||
using (selected_items.SuspendHandler(_on_selected_items_CollectionChanged)) {
|
||||
var to_add = _selected_items_set.Where(_items_set.Add);
|
||||
if (_comparer != null) {
|
||||
_items.InsertSorted(to_add, _comparer);
|
||||
} else {
|
||||
_items.AddRange(to_add);
|
||||
foreach (var item in _selected_items_set) {
|
||||
if (_items_set.Add(item))
|
||||
_unsorted_items.Add(item);
|
||||
}
|
||||
selected_items.sync_with(_selected_items_set);
|
||||
}
|
||||
@@ -252,14 +248,6 @@ namespace mbts.spark.filters {
|
||||
_raise_filter_changed();
|
||||
}
|
||||
}
|
||||
|
||||
void _sync_items() {
|
||||
if (_comparer != null) {
|
||||
_items.sync_with(_items_set.Ordered(_comparer));
|
||||
} else {
|
||||
_items.sync_with(_items_set);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TableUpdateItemsFilter<TRow, TValue, TKey, TState> : FilterVMBase<TRow, TValue, TState>, IObserver<TableUpdate<TValue, TKey>> where TKey : struct, IEquatable<TKey> {
|
||||
|
||||
@@ -101,8 +101,7 @@ namespace mbts.ui.controls
|
||||
case NotifyCollectionChangedAction.Replace:
|
||||
if (e.OldItems != null) foreach (var item in e.OldItems) _set_is_selected(item, false);
|
||||
if (e.NewItems != null) foreach (var item in e.NewItems) _set_is_selected(item, true); break;
|
||||
case NotifyCollectionChangedAction.Reset:
|
||||
if (ItemsSource != null) foreach (var item in ItemsSource) _set_is_selected(item, false); break;
|
||||
case NotifyCollectionChangedAction.Reset: foreach (var item in ItemsSource) _set_is_selected(item, false); break;
|
||||
}
|
||||
_update_is_all_selected();
|
||||
}
|
||||
|
||||
@@ -16,9 +16,6 @@ namespace mbts.ui.converters {
|
||||
|
||||
public static IMultiValueConverter create<TIn1, TIn2, TIn3, TOut>(Func<TIn1, TIn2, TIn3, TOut> convert) =>
|
||||
new AnonymousMultiValueConverter<TIn1, TIn2, TIn3, TOut>(convert);
|
||||
|
||||
public static IMultiValueConverter create<TIn1, TIn2, TIn3, TIn4, TOut>(Func<TIn1, TIn2, TIn3, TIn4, TOut> convert) =>
|
||||
new AnonymousMultiValueConverter<TIn1, TIn2, TIn3, TIn4, TOut>(convert);
|
||||
}
|
||||
|
||||
static class ValueConverters {
|
||||
@@ -95,17 +92,4 @@ namespace mbts.ui.converters {
|
||||
|
||||
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
class AnonymousMultiValueConverter<TIn1, TIn2, TIn3, TIn4, TOut> : IMultiValueConverter {
|
||||
readonly Func<TIn1, TIn2, TIn3, TIn4, TOut> _convert;
|
||||
|
||||
public AnonymousMultiValueConverter(Func<TIn1, TIn2, TIn3, TIn4, TOut> convert) => _convert = convert;
|
||||
|
||||
public TOut ErrorValue { get; set; }
|
||||
|
||||
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) =>
|
||||
ConvertUtils.try_convert_args(values, out TIn1 a, out TIn2 b, out TIn3 c, out TIn4 d) ? _convert(a, b, c, d) : ErrorValue;
|
||||
|
||||
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) => throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace mbts.ui.converters {
|
||||
class AppendTextConverter : IValueConverter {
|
||||
public string Prefix { get; set; }
|
||||
public string Suffix { get; set; }
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
|
||||
var sb = new StringBuilder(value?.ToString());
|
||||
// don't need to check for null (Insert/Append support it)
|
||||
sb.Insert(0, Prefix);
|
||||
sb.Append(Suffix);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) =>
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
@@ -24,9 +24,6 @@ namespace mbts.ui.converters {
|
||||
public string Format { get; set; }
|
||||
public string NaNText { get; set; } = string.Empty;
|
||||
|
||||
public static string convert(double value, string format, string nan_text = "-") =>
|
||||
double.IsNaN(value) ? nan_text : value.ToString(format);
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
|
||||
var text = (value as string ?? string.Empty).Trim().ToLower();
|
||||
if (text == string.Empty || text == "nan" || text == NaNText) return double.NaN;
|
||||
|
||||
@@ -156,19 +156,6 @@ namespace mbts.ui.extensions {
|
||||
}
|
||||
}
|
||||
|
||||
public static IDisposable SubscribePreviewKeyUp(this UIElement target, KeyEventHandler callback) {
|
||||
target.PreviewKeyUp += callback;
|
||||
return Disposable.Create(() => target.PreviewKeyUp -= callback);
|
||||
}
|
||||
|
||||
public static IDisposable SubscribePreviewKeyUp(this UIElement target, Key key, Action callback) {
|
||||
return SubscribePreviewKeyUp(target, on_preview_key_up);
|
||||
void on_preview_key_up(object sender, KeyEventArgs e) {
|
||||
if (e.Key == key)
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
public static IObservable<KeyEventArgs> SubscribePreviewKeyDown(this UIElement target) =>
|
||||
Observable.Create<KeyEventArgs>(o => {
|
||||
target.PreviewKeyDown += on_preview_key_down;
|
||||
@@ -210,13 +197,6 @@ namespace mbts.ui.extensions {
|
||||
}
|
||||
}
|
||||
|
||||
public static class SelectorExtensions {
|
||||
public static IDisposable SubscribeSelectionChanged(this System.Windows.Controls.Primitives.Selector target, SelectionChangedEventHandler callback) {
|
||||
target.SelectionChanged += callback;
|
||||
return Disposable.Create(() => target.SelectionChanged -= callback);
|
||||
}
|
||||
}
|
||||
|
||||
public static class ScrollViewerExtensions {
|
||||
public static IDisposable SubscribeScrollChanged(this ScrollViewer target, ScrollChangedEventHandler callback) {
|
||||
target.ScrollChanged += callback;
|
||||
|
||||
@@ -66,18 +66,11 @@ namespace mbts.ui {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: replace this method with 'argb_color'
|
||||
// - before that, need to verify that no settings/colors become 'invisible' as a result.
|
||||
public static Color rgb_color(this UInt32 color) {
|
||||
var bytes = BitConverter.GetBytes(color);
|
||||
return Color.FromRgb(bytes[2], bytes[1], bytes[0]);
|
||||
}
|
||||
|
||||
public static Color argb_color(this uint color) {
|
||||
var bytes = BitConverter.GetBytes(color);
|
||||
return Color.FromArgb(bytes[3], bytes[2], bytes[1], bytes[0]);
|
||||
}
|
||||
|
||||
public static SolidColorBrush to_brush(this BTSColor color) =>
|
||||
new SolidColorBrush(color);
|
||||
|
||||
@@ -110,9 +103,6 @@ namespace mbts.ui {
|
||||
return brush;
|
||||
}
|
||||
|
||||
public static Color with(this Color color, byte alpha) =>
|
||||
Color.FromArgb(alpha, color.R, color.G, color.B);
|
||||
|
||||
public static SolidColorBrush with(this SolidColorBrush brush, double opacity) {
|
||||
brush.Opacity = opacity;
|
||||
return brush;
|
||||
@@ -246,12 +236,12 @@ namespace mbts.ui {
|
||||
throw new NotImplementedException("This ICommand does not implement IRaiseCanExecuteChanged");
|
||||
}
|
||||
|
||||
public static Task ExecuteAsync(this ICommand command, object parameter) {
|
||||
public static async Task ExecuteAsync(this ICommand command, object parameter) {
|
||||
ExceptionAssert.Argument.NotNull(command, nameof(command));
|
||||
if (command is IAsyncCommand acmd)
|
||||
return acmd.Execute(parameter);
|
||||
await acmd.Execute(parameter);
|
||||
else
|
||||
return Task.Run(() => command.Execute(parameter));
|
||||
await Task.Run(() => command.Execute(parameter));
|
||||
}
|
||||
|
||||
public static void TryExecuteIfCan(this ICommand command, object parameter) {
|
||||
|
||||
@@ -619,47 +619,47 @@
|
||||
|
||||
<!-- Toggleswitch -->
|
||||
<Style TargetType="ToggleButton" x:Key="ToggleSwitch">
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="Foreground" Value="Black" />
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="ToolTip" Value="{Binding IsChecked, RelativeSource={RelativeSource Self}, Converter={StaticResource ToggleSwitchToolTipConverter}}"/>
|
||||
<Setter Property="BorderBrush" Value="Black" />
|
||||
<Setter Property="Margin" Value="2,0,2,0" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="ToggleButton">
|
||||
<Grid>
|
||||
<Grid Margin="{TemplateBinding Margin}">
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal"/>
|
||||
<VisualState x:Name="Normal" />
|
||||
<VisualState x:Name="MouseOver">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="background" Storyboard.TargetProperty="Opacity" To="0.7" BeginTime="0" Duration="0"/>
|
||||
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="0.8" Duration="0:0:0.1" />
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Pressed"/>
|
||||
<VisualState x:Name="Disabled">
|
||||
<VisualState x:Name="Pressed">
|
||||
<Storyboard>
|
||||
<ColorAnimation Storyboard.TargetName="background" Storyboard.TargetProperty="Background.Color" To="#555" Duration="0:0:0" />
|
||||
<ColorAnimation Storyboard.TargetName="background" Storyboard.TargetProperty="Background.Color" To="#c8ff44" Duration="0:0:0.5" />
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Disabled">
|
||||
<!--<Storyboard>
|
||||
<ColorAnimation Storyboard.TargetName="background" Storyboard.TargetProperty="Background.Color" To="#555" Duration="0:0:0" />
|
||||
</Storyboard>-->
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
<VisualStateGroup x:Name="CheckStates">
|
||||
<VisualState x:Name="Checked">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="lever" Storyboard.TargetProperty="(Canvas.Left)" From="3" To="40" Duration="0:0:0.2">
|
||||
<DoubleAnimation Storyboard.TargetName="lever" Storyboard.TargetProperty="(Canvas.Left)" To="40" Duration="0:0:0.2">
|
||||
<DoubleAnimation.EasingFunction>
|
||||
<CubicEase EasingMode="EaseIn" />
|
||||
</DoubleAnimation.EasingFunction>
|
||||
</DoubleAnimation>
|
||||
<StringAnimationUsingKeyFrames Storyboard.TargetName="text" Storyboard.TargetProperty="(TextBlock.Text)">
|
||||
<DiscreteStringKeyFrame KeyTime="0" Value="On"/>
|
||||
</StringAnimationUsingKeyFrames>
|
||||
<ColorAnimation Storyboard.TargetName="background" Storyboard.TargetProperty="Background.Color" To="{StaticResource ToggleSwitchOnColor}" BeginTime="0" Duration="0"/>
|
||||
<ColorAnimation Storyboard.TargetName="background" Storyboard.TargetProperty="BorderBrush.Color" To="{StaticResource ToggleSwitchOnColor}" BeginTime="0" Duration="0"/>
|
||||
<ColorAnimation Storyboard.TargetName="text" Storyboard.TargetProperty="Foreground.Color" To="White" BeginTime="0" Duration="0"/>
|
||||
<ColorAnimation Storyboard.TargetName="lever" Storyboard.TargetProperty="Fill.Color" To="White" BeginTime="0" Duration="0"/>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Unchecked">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="lever" Storyboard.TargetProperty="(Canvas.Left)" From="40" To="3" Duration="0:0:0.2">
|
||||
<DoubleAnimation Storyboard.TargetName="lever" Storyboard.TargetProperty="(Canvas.Left)" To="3" Duration="0:0:0.2">
|
||||
<DoubleAnimation.EasingFunction>
|
||||
<CubicEase EasingMode="EaseIn" />
|
||||
</DoubleAnimation.EasingFunction>
|
||||
@@ -673,37 +673,45 @@
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
<Border
|
||||
x:Name="background"
|
||||
Width="54"
|
||||
Height="18"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="8"
|
||||
VerticalAlignment="{TemplateBinding VerticalAlignment}"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
|
||||
Opacity="1"
|
||||
>
|
||||
<Border.Background>
|
||||
<SolidColorBrush Color="Transparent"/>
|
||||
</Border.Background>
|
||||
<Border.BorderBrush>
|
||||
<SolidColorBrush Color="Black"/>
|
||||
</Border.BorderBrush>
|
||||
<Border x:Name="background"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="8"
|
||||
Background="{TemplateBinding Background}"
|
||||
Width="54" Height="18">
|
||||
<Canvas>
|
||||
<Ellipse x:Name="lever" VerticalAlignment="Center" Width="9" Height="9" Canvas.Top="3" Canvas.Left="3">
|
||||
<Ellipse.Fill>
|
||||
<SolidColorBrush Color="Black"/>
|
||||
</Ellipse.Fill>
|
||||
</Ellipse>
|
||||
<Ellipse x:Name="lever" VerticalAlignment="Center" Width="9" Height="9" Fill="Black" Canvas.Top="3" Canvas.Left="3" />
|
||||
</Canvas>
|
||||
</Border>
|
||||
<TextBlock x:Name="text" Grid.Column="1" FontWeight="SemiBold" Text="Off" HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="Black"/>
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock x:Name="text" Grid.Column="1" Text="Off" HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||||
</Grid>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsEnabled" Value="False">
|
||||
<Setter Property="Foreground" Value="White" />
|
||||
<Setter Property="Background" Value="#555" />
|
||||
</Trigger>
|
||||
<Trigger Property="IsChecked" Value="True">
|
||||
<Setter TargetName="text" Property="Text" Value="On" />
|
||||
<Setter TargetName="lever" Property="Canvas.Top" Value="4" />
|
||||
</Trigger>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="IsChecked" Value="True" />
|
||||
<Condition Property="IsEnabled" Value="True" />
|
||||
</MultiTrigger.Conditions>
|
||||
<Setter TargetName="background" Property="BorderThickness" Value="0" />
|
||||
<Setter TargetName="background" Property="Background" Value="#080" />
|
||||
<Setter Property="Foreground" Value="White" />
|
||||
</MultiTrigger>
|
||||
<Trigger Property="IsChecked" Value="{x:Null}">
|
||||
<Setter TargetName="text" Property="Text" Value="" />
|
||||
</Trigger>
|
||||
<Trigger Property="IsPressed" Value="true">
|
||||
<Setter Property="Foreground" Value="DarkGray" />
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
<cv:ValueMap x:Key="ReverseVisibilityConverter" Default="{u:Visibility Collapsed}">
|
||||
<cv:KeyValue Key="{u:Visibility Collapsed}" Value="{u:Visibility Visible}"/>
|
||||
</cv:ValueMap>
|
||||
<cv:BooleanToObjectConverter x:Key="ToggleSwitchToolTipConverter" ValueForFalse="Turn On" ValueForTrue="Turn Off"/>
|
||||
<cv:EnumDescriptionConverter x:Key="EnumDescriptionConverter" />
|
||||
<cv:EnumFlagsDescriptionConverter x:Key="EnumFlagsDescriptionConverter"/>
|
||||
<cv:EnumerableDoubleStringConverter x:Key="EnumerableDoublesConverter" />
|
||||
|
||||
@@ -143,7 +143,6 @@
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Color x:Key="HeaderBackgroundBorderColor">#FFAAD1FF</Color>
|
||||
<Style x:Key="BTSDialog" TargetType="{x:Type Window}" BasedOn="{StaticResource BTSWindow}">
|
||||
<Setter Property="MinHeight" Value="100" />
|
||||
<Setter Property="MinWidth" Value="100" />
|
||||
@@ -157,10 +156,8 @@
|
||||
<RowDefinition Height="1" />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel HorizontalAlignment="Left" VerticalAlignment="Stretch" Orientation="Horizontal">
|
||||
<Image SnapsToDevicePixels="True" Height="14" Margin="4,2" VerticalAlignment="Center" HorizontalAlignment="Left" Stretch="Uniform" Source="{TemplateBinding Icon}" RenderOptions.BitmapScalingMode="HighQuality" />
|
||||
<TextBlock Text="{TemplateBinding Title}" VerticalAlignment="Center" HorizontalAlignment="Stretch" x:Name="_title" />
|
||||
</StackPanel>
|
||||
<Image SnapsToDevicePixels="True" Height="20" VerticalAlignment="Center" HorizontalAlignment="Left" Stretch="Uniform" Source="{TemplateBinding Icon}" RenderOptions.BitmapScalingMode="HighQuality" />
|
||||
<TextBlock Text="{TemplateBinding Title}" VerticalAlignment="Center" HorizontalAlignment="Stretch" Margin="22,2,2,2" x:Name="_title" />
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Background="{TemplateBinding Background}">
|
||||
<Button Style="{StaticResource RoundMinimizeButton}" HorizontalAlignment="Right" Command="{x:Static SystemCommands.MinimizeWindowCommand}"
|
||||
Visibility="{Binding Path=(u:WindowStyle.ShowMinimizeButton),RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource BoolToVisibility}}"/>
|
||||
@@ -171,7 +168,7 @@
|
||||
</StackPanel>
|
||||
<Rectangle Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
|
||||
<Rectangle.Fill>
|
||||
<SolidColorBrush Color="{DynamicResource HeaderBackgroundBorderColor}"/>
|
||||
<SolidColorBrush Color="#AAD1FF"/>
|
||||
</Rectangle.Fill>
|
||||
</Rectangle>
|
||||
<AdornerDecorator Grid.Row="2">
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
Topmost="{Binding Topmost}"
|
||||
x:Name="window"
|
||||
Background="{StaticResource CommonBackground}"
|
||||
Title="BTS Spark"
|
||||
Title="{Binding Title}"
|
||||
d:DataContext="{d:DesignInstance shell:LauncherDesignTimeVM, IsDesignTimeCreatable=True}">
|
||||
<Window.Resources>
|
||||
<ResourceDictionary>
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
<Window
|
||||
x:Class="mbts.ui.spark.GlobalAlertsDialog"
|
||||
x:ClassModifier="internal"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:u="clr-namespace:mbts.ui"
|
||||
xmlns:c="clr-namespace:mbts.ui.controls"
|
||||
xmlns:usp="clr-namespace:mbts.ui.spark"
|
||||
u:WindowStyle.SizeToContent="WidthAndHeight"
|
||||
u:WindowStyle.AllowResize="True"
|
||||
Content="{Binding}"
|
||||
Icon="{x:Static u:Icons.Speaker}"
|
||||
Title="Alerts"
|
||||
Style="{StaticResource BTSDialog}"
|
||||
>
|
||||
<Window.ContentTemplate>
|
||||
<DataTemplate DataType="{x:Type usp:GlobalAlertsVM}">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<usp:GlobalAlertsView/>
|
||||
<c:DialogSkirt Grid.Row="1" Padding="{StaticResource DialogSkirtPadding}">
|
||||
<c:DialogSkirt.RightItems>
|
||||
<x:Array Type="{x:Type Button}">
|
||||
<Button Margin="{StaticResource DialogSkirtButtonMargin}" Command="{Binding apply_cmd}">
|
||||
Apply
|
||||
</Button>
|
||||
<Button Margin="{StaticResource DialogSkirtButtonMargin}" Command="{Binding ok_cmd}" IsDefault="True">
|
||||
OK
|
||||
</Button>
|
||||
<Button Margin="{StaticResource DialogSkirtButtonMargin}" Command="{Binding cancel_cmd}" IsCancel="True">
|
||||
Cancel
|
||||
</Button>
|
||||
</x:Array>
|
||||
</c:DialogSkirt.RightItems>
|
||||
</c:DialogSkirt>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</Window.ContentTemplate>
|
||||
</Window>
|
||||
@@ -1,14 +0,0 @@
|
||||
using mbts.ui.utility;
|
||||
using System.Windows;
|
||||
|
||||
namespace mbts.ui.spark {
|
||||
/// <summary>
|
||||
/// Interaction logic for GlobalAlertsDialog.xaml
|
||||
/// </summary>
|
||||
partial class GlobalAlertsDialog : Window {
|
||||
public GlobalAlertsDialog() {
|
||||
InitializeComponent();
|
||||
WindowUtilities.listen_for_close_requests(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,246 +0,0 @@
|
||||
<UserControl
|
||||
x:Class="mbts.ui.spark.GlobalAlertsView"
|
||||
x:ClassModifier="internal"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mbts="clr-namespace:mbts"
|
||||
xmlns:sp="clr-namespace:mbts.spark"
|
||||
xmlns:u="clr-namespace:mbts.ui"
|
||||
xmlns:c="clr-namespace:mbts.ui.controls"
|
||||
xmlns:cv="clr-namespace:mbts.ui.converters"
|
||||
xmlns:stg="clr-namespace:mbts.ui.settings"
|
||||
xmlns:usp="clr-namespace:mbts.ui.spark"
|
||||
xmlns:sys="clr-namespace:System;assembly=mscorlib"
|
||||
>
|
||||
<Grid>
|
||||
<Grid.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="/view/Controls/DIYGrid/Settings/Animation/SoundAnimationView.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<sys:Double x:Key="SecurityWidth">130</sys:Double>
|
||||
<sys:Double x:Key="DoublePredicateWidth">210</sys:Double>
|
||||
<sys:Double x:Key="SideWidth">100</sys:Double>
|
||||
<sys:Double x:Key="SoundWidth">163</sys:Double>
|
||||
<Thickness x:Key="CellPadding">10,8</Thickness>
|
||||
<!--Converters-->
|
||||
<cv:AppendTextConverter x:Key="DropDownSharedSizeGroupConverter" Suffix="_DropDownWidth"/>
|
||||
<AlternationConverter x:Key="AlternatingBackgroundConverter">
|
||||
<SolidColorBrush Color="{u:BTSColor Grid.input_cell_background}"/>
|
||||
<SolidColorBrush Color="{u:BTSColor Grid.alternate_row_background}"/>
|
||||
</AlternationConverter>
|
||||
<!--Styles-->
|
||||
<Style x:Key="DisabledColumnHeader" TargetType="GridViewColumnHeader">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="GridViewColumnHeader">
|
||||
<Border HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="{x:Static u:BTSColors.header_brush}"/>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<!--Data Templates-->
|
||||
<DataTemplate x:Key="DoublePredicateTypeTemplate">
|
||||
<TextBlock Text="{Binding Converter={x:Static usp:GlobalAlertsView.DoublePredicateTypeDisplayNameConverter}}"/>
|
||||
</DataTemplate>
|
||||
<DataTemplate x:Key="ColumnHeaderTemplate">
|
||||
<TextBlock Text="{Binding}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</DataTemplate>
|
||||
<DataTemplate x:Key="None"/>
|
||||
<DataTemplate x:Key="DisabledTickUpDown">
|
||||
<c:TickUpDown IsEnabled="False">
|
||||
<TextBlock Foreground="Gray" Margin="4,0,0,0">(N/A)</TextBlock>
|
||||
</c:TickUpDown>
|
||||
</DataTemplate>
|
||||
<DataTemplate x:Key="TickUpDownDouble">
|
||||
<c:TickUpDownDouble Value="{Binding}"/>
|
||||
</DataTemplate>
|
||||
<DataTemplate x:Key="DoubleMinimum" DataType="{x:Type sp:DoublePredicateVM}">
|
||||
<c:TickUpDownDouble Value="{Binding minimum}"/>
|
||||
</DataTemplate>
|
||||
<DataTemplate x:Key="DoubleMaximum" DataType="{x:Type sp:DoublePredicateVM}">
|
||||
<c:TickUpDownDouble Value="{Binding maximum}"/>
|
||||
</DataTemplate>
|
||||
<DataTemplate x:Key="DoubleRange" DataType="{x:Type sp:DoublePredicateVM}">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<c:TickUpDownDouble Value="{Binding minimum}"/>
|
||||
<TextBlock Grid.Column="1" VerticalAlignment="Center" Margin="2,0" Text="and"/>
|
||||
<c:TickUpDownDouble Grid.Column="2" Value="{Binding maximum}"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
<DataTemplate DataType="{x:Type sp:DoublePredicateVM}">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="{Binding Path='(c:DIYGrid.ColumnKey)', RelativeSource={RelativeSource AncestorType=ContentControl}, Converter={StaticResource DropDownSharedSizeGroupConverter}}"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<ComboBox ItemsSource="{u:EnumBindingSource {u:Nested mbts:DoublePredicate.PredicateTypeOneofCase}}" SelectedItem="{Binding type}" ItemTemplate="{StaticResource DoublePredicateTypeTemplate}"/>
|
||||
<ContentControl Grid.Column="1" Margin="6,0,0,0" Content="{Binding}">
|
||||
<ContentControl.ContentTemplate>
|
||||
<Binding Path="type">
|
||||
<Binding.Converter>
|
||||
<cv:ValueMap>
|
||||
<cv:KeyValue Key="{x:Static None, MemberType={u:Nested mbts:DoublePredicate.PredicateTypeOneofCase}}" Value="{StaticResource DisabledTickUpDown}"/>
|
||||
<cv:KeyValue Key="{x:Static LessThan, MemberType={u:Nested mbts:DoublePredicate.PredicateTypeOneofCase}}" Value="{StaticResource DoubleMaximum}"/>
|
||||
<cv:KeyValue Key="{x:Static GreaterThan, MemberType={u:Nested mbts:DoublePredicate.PredicateTypeOneofCase}}" Value="{StaticResource DoubleMinimum}"/>
|
||||
<cv:KeyValue Key="{x:Static InsideRange, MemberType={u:Nested mbts:DoublePredicate.PredicateTypeOneofCase}}" Value="{StaticResource DoubleRange}"/>
|
||||
<cv:KeyValue Key="{x:Static OutsideRange, MemberType={u:Nested mbts:DoublePredicate.PredicateTypeOneofCase}}" Value="{StaticResource DoubleRange}"/>
|
||||
</cv:ValueMap>
|
||||
</Binding.Converter>
|
||||
</Binding>
|
||||
</ContentControl.ContentTemplate>
|
||||
</ContentControl>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
<DataTemplate DataType="{x:Type stg:SoundAnimationVM}">
|
||||
<c:SoundAnimationView Bus="{DynamicResource Bus}"/>
|
||||
</DataTemplate>
|
||||
<DataTemplate DataType="{x:Type sys:Boolean}">
|
||||
<CheckBox IsChecked="{Binding}"/>
|
||||
</DataTemplate>
|
||||
<DataTemplate x:Key="SecurityCriteriaTemplate">
|
||||
<c:ContractPickerPopup
|
||||
c:DIYGrid.ColumnKey="Security"
|
||||
Margin="{StaticResource CellPadding}"
|
||||
AllowEmpty="True"
|
||||
CloseOnOK="True"
|
||||
CloseOnCancel="True"
|
||||
MaxWidth="180"
|
||||
DataContext="{Binding predicate}"
|
||||
SecurityMap="{Binding DataContext.security_map, RelativeSource={RelativeSource AncestorType=usp:GlobalAlertsView}}"
|
||||
Header="{Binding security_criteria}"
|
||||
ShowDialogSkirt="True"
|
||||
ToolTip="{Binding security_criteria, Converter={x:Static c:ContractPickerPopup.SecurityCriteriaTextConverter}, Mode=OneWay}"
|
||||
OKClick="{x:Static c:ContractPickerPopup.FlushSelectedCriteriaHandler}"
|
||||
SelectedCriteria="{Binding security_criteria, UpdateSourceTrigger=Explicit}"
|
||||
>
|
||||
<c:ContractPickerPopup.HeaderTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Converter={x:Static c:ContractPickerPopup.SecurityCriteriaTextConverter}}" TextTrimming="CharacterEllipsis"/>
|
||||
</DataTemplate>
|
||||
</c:ContractPickerPopup.HeaderTemplate>
|
||||
</c:ContractPickerPopup>
|
||||
</DataTemplate>
|
||||
<DataTemplate DataType="{x:Type usp:TradeAlertsVM}">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<c:DIYGrid
|
||||
Grid.Row="0"
|
||||
Grid.IsSharedSizeScope="True"
|
||||
BorderBrush="Black"
|
||||
BorderThickness="1"
|
||||
Rows="{Binding alerts}"
|
||||
RowsHorizontalScrollBarVisibility="Collapsed"
|
||||
SelectionMode="Single"
|
||||
>
|
||||
<c:DIYGrid.Columns>
|
||||
<c:DIYGridColumnCollection>
|
||||
<usp:GlobalAlertsColumn key="Delete" HeaderContainerStyle="{StaticResource DisabledColumnHeader}" Width="23" is_visible="True"/>
|
||||
<usp:GlobalAlertsColumn key="IsEnabled" HeaderContainerStyle="{StaticResource DisabledColumnHeader}" Width="66" is_visible="True"/>
|
||||
<usp:GlobalAlertsColumn key="Security" Header="Security" Width="{StaticResource SecurityWidth}" is_visible="True"/>
|
||||
<usp:GlobalAlertsColumn key="Price" Header="Price Condition" Width="{StaticResource DoublePredicateWidth}" is_visible="True"/>
|
||||
<usp:GlobalAlertsColumn key="Quantity" Header="Size Condition" Width="{StaticResource DoublePredicateWidth}" is_visible="True"/>
|
||||
<usp:GlobalAlertsColumn key="Side" Header="Side" Width="{StaticResource SideWidth}" is_visible="True"/>
|
||||
<usp:GlobalAlertsColumn key="Sound" Header="Sound" Width="{StaticResource SoundWidth}" is_visible="True"/>
|
||||
</c:DIYGridColumnCollection>
|
||||
</c:DIYGrid.Columns>
|
||||
<c:DIYGrid.RowTemplate>
|
||||
<DataTemplate DataType="{x:Type usp:TradeAlertVM}">
|
||||
<Grid c:DIYGrid.SyncWithColumnGeometry="True">
|
||||
<Button
|
||||
c:DIYGrid.ColumnKey="Delete"
|
||||
HorizontalAlignment="Center"
|
||||
Command="{Binding DataContext.delete_alert_cmd, RelativeSource={RelativeSource AncestorType=c:DIYGrid}}"
|
||||
CommandParameter="{Binding}"
|
||||
Style="{StaticResource RoundCloseButton}"
|
||||
ToolTip="Delete"
|
||||
/>
|
||||
<ToggleButton c:DIYGrid.ColumnKey="IsEnabled" IsChecked="{Binding is_enabled}" Style="{StaticResource ToggleSwitch}"/>
|
||||
<c:ContractPickerPopup
|
||||
c:DIYGrid.ColumnKey="Security"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource CellPadding}"
|
||||
AllowEmpty="True"
|
||||
CloseOnOK="True"
|
||||
CloseOnCancel="True"
|
||||
MaxWidth="180"
|
||||
DataContext="{Binding predicate}"
|
||||
SecurityMap="{Binding DataContext.security_map, RelativeSource={RelativeSource AncestorType=usp:GlobalAlertsView}}"
|
||||
Header="{Binding security_criteria}"
|
||||
ShowDialogSkirt="True"
|
||||
ToolTip="{Binding security_criteria, Converter={x:Static c:ContractPickerPopup.SecurityCriteriaTextConverter}, Mode=OneWay}"
|
||||
OKClick="{x:Static c:ContractPickerPopup.FlushSelectedCriteriaHandler}"
|
||||
SelectedCriteria="{Binding security_criteria, UpdateSourceTrigger=Explicit}"
|
||||
>
|
||||
<c:ContractPickerPopup.HeaderTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Converter={x:Static c:ContractPickerPopup.SecurityCriteriaTextConverter}}" TextTrimming="CharacterEllipsis"/>
|
||||
</DataTemplate>
|
||||
</c:ContractPickerPopup.HeaderTemplate>
|
||||
</c:ContractPickerPopup>
|
||||
<ContentControl c:DIYGrid.ColumnKey="Price" Margin="{StaticResource CellPadding}" Content="{Binding predicate.price}"/>
|
||||
<ContentControl c:DIYGrid.ColumnKey="Quantity" Margin="{StaticResource CellPadding}" Content="{Binding predicate.quantity}"/>
|
||||
<ComboBox
|
||||
c:DIYGrid.ColumnKey="Side"
|
||||
Margin="{StaticResource CellPadding}"
|
||||
ItemsSource="{Binding predicate.side_options}"
|
||||
SelectedItem="{Binding predicate.side}"
|
||||
ItemTemplate="{StaticResource EnumFlagsDescriptionTemplate}"
|
||||
/>
|
||||
<Grid c:DIYGrid.ColumnKey="Sound" Margin="{StaticResource CellPadding}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<ComboBox
|
||||
ItemsSource="{Binding sound.system_sound_options}"
|
||||
SelectedItem="{Binding sound.system_sound}"
|
||||
ItemTemplate="{StaticResource EnumDescriptionTemplate}"
|
||||
HorizontalAlignment="Stretch"
|
||||
/>
|
||||
<Button
|
||||
Grid.Column="1"
|
||||
Margin="4,0,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
Command="{x:Static c:SoundAnimationView.PreviewSoundCommand}"
|
||||
CommandParameter="{Binding sound.system_sound}"
|
||||
Style="{StaticResource RoundPlayButton}"
|
||||
/>
|
||||
</Grid>
|
||||
<Rectangle Grid.Column="0" HorizontalAlignment="Right" Width="1" Fill="Gray"/>
|
||||
<Rectangle Grid.Column="1" HorizontalAlignment="Right" Width="1" Fill="Gray"/>
|
||||
<Rectangle Grid.Column="2" HorizontalAlignment="Right" Width="1" Fill="Gray"/>
|
||||
<Rectangle Grid.Column="3" HorizontalAlignment="Right" Width="1" Fill="Gray"/>
|
||||
<Rectangle Grid.Column="4" HorizontalAlignment="Right" Width="1" Fill="Gray"/>
|
||||
<Rectangle Grid.Column="5" HorizontalAlignment="Right" Width="1" Fill="Gray"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</c:DIYGrid.RowTemplate>
|
||||
</c:DIYGrid>
|
||||
<TextBlock Grid.Row="1" HorizontalAlignment="Left" Margin="4,8">
|
||||
<Hyperlink Command="{Binding add_alert_cmd}">
|
||||
Click to Add Alert
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
<!--<DataTemplate DataType-->
|
||||
</ResourceDictionary>
|
||||
</Grid.Resources>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<TabControl>
|
||||
<TabItem Header="Trade Alerts" Content="{Binding trade_alerts}">
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -1,41 +0,0 @@
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.converters;
|
||||
using mbts.ui.utilities;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace mbts.ui.spark {
|
||||
/// <summary>
|
||||
/// Interaction logic for ShellSettingsDialog.xaml
|
||||
/// </summary>
|
||||
partial class GlobalAlertsView : UserControl {
|
||||
public static readonly IValueConverter
|
||||
DoublePredicateTypeDisplayNameConverter = ValueConverter.create<DoublePredicate.PredicateTypeOneofCase, string>(t => {
|
||||
switch (t) {
|
||||
case DoublePredicate.PredicateTypeOneofCase.InsideRange: return "Between";
|
||||
case DoublePredicate.PredicateTypeOneofCase.OutsideRange: return "Not Between";
|
||||
case DoublePredicate.PredicateTypeOneofCase.None: return "(None)";
|
||||
default: return DisplayNameHelper.camel_to_title_case(t.ToString());
|
||||
}
|
||||
});
|
||||
|
||||
public GlobalAlertsView() =>
|
||||
InitializeComponent();
|
||||
|
||||
public enum Columns {
|
||||
None = 0,
|
||||
Delete,
|
||||
IsEnabled,
|
||||
Security,
|
||||
Price,
|
||||
Quantity,
|
||||
Side,
|
||||
Sound
|
||||
}
|
||||
}
|
||||
|
||||
class GlobalAlertsColumn : DIYGrid.ColumnBase<GlobalAlertsView.Columns> {
|
||||
protected override void on_key_initialized(GlobalAlertsView.Columns key) =>
|
||||
id = key.ToString();
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
using mbts.spark;
|
||||
using mbts.ui.shell;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace mbts.ui.spark {
|
||||
class GlobalAlertsVM : ViewModelBase, IValueFactory<GlobalAlertSettings>, IRequestViewClose {
|
||||
IReadOnlyDictionary<uint, SecurityMD> _security_map = DictionaryHelper.empty<uint, SecurityMD>();
|
||||
|
||||
public GlobalAlertsVM() {
|
||||
ok_cmd = new DelegateCommand(() => {
|
||||
apply();
|
||||
RequestClose?.Invoke(this, EventArgs.Empty);
|
||||
});
|
||||
apply_cmd = new DelegateCommand(apply);
|
||||
cancel_cmd = new DelegateCommand(() => RequestClose?.Invoke(this, EventArgs.Empty));
|
||||
void apply() =>
|
||||
request_commit?.Invoke();
|
||||
}
|
||||
|
||||
public event Action request_commit;
|
||||
public event EventHandler RequestClose;
|
||||
|
||||
public TradeAlertsVM trade_alerts { get; } = new TradeAlertsVM();
|
||||
public ICommand apply_cmd { get; }
|
||||
public ICommand ok_cmd { get; }
|
||||
public ICommand cancel_cmd { get; }
|
||||
|
||||
public IReadOnlyDictionary<uint, SecurityMD> security_map {
|
||||
get => _security_map;
|
||||
set {
|
||||
_security_map = value;
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public GlobalAlertSettings create() =>
|
||||
new GlobalAlertSettings(trade_alerts.create().ToArray());
|
||||
|
||||
public void load_from(GlobalAlertSettings value) {
|
||||
trade_alerts.load_from(value.trade_alerts);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
using bts.shell;
|
||||
using mbts.ui.shell;
|
||||
using mbts.ui.utility;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace mbts.spark {
|
||||
class SoundNotificationVM : DisposableViewModelBase {
|
||||
SoundNotification.SystemSound _system_sound;
|
||||
|
||||
public SoundNotificationVM() {
|
||||
}
|
||||
|
||||
public SoundNotification.SystemSound system_sound { get => _system_sound; set => Set(ref _system_sound, value); }
|
||||
public IReadOnlyList<SoundNotification.SystemSound> system_sound_options => EnumHelper.get_values<SoundNotification.SystemSound>();
|
||||
|
||||
public SoundNotification create() =>
|
||||
new SoundNotification {
|
||||
system_sound = system_sound
|
||||
};
|
||||
|
||||
public void load_from(SoundNotification value) {
|
||||
switch (value.sound_type_case) {
|
||||
case SoundNotification.SoundTypeOneofCase.System: system_sound = value.system_sound.Value; break;
|
||||
case SoundNotification.SoundTypeOneofCase.Uri: /*todo*/ break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
using bts.api;
|
||||
using mbts.spark;
|
||||
using mbts.ui.shell;
|
||||
using mbts.ui.utility;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace mbts.ui.spark {
|
||||
class TradeAlertPredicateVM : ViewModelBase, IValueFactory<TradeAlertPredicate> {
|
||||
SecurityCriteria _security_criteria;
|
||||
Side _side;
|
||||
|
||||
public SecurityCriteria security_criteria { get => _security_criteria; set => Set(ref _security_criteria, value); }
|
||||
public DoublePredicateVM price { get; } = new DoublePredicateVM();
|
||||
public DoublePredicateVM quantity { get; } = new DoublePredicateVM();
|
||||
public Side side { get => _side; set => Set(ref _side, value); }
|
||||
public IEnumerable<Side> side_options {
|
||||
get {
|
||||
yield return Side.Buy;
|
||||
yield return Side.Sell;
|
||||
yield return Side.Buy | Side.Sell;
|
||||
}
|
||||
}
|
||||
|
||||
public void load_from(TradeAlertPredicate value) {
|
||||
security_criteria = value.security_criteria;
|
||||
quantity.load_from(value.quantity);
|
||||
price.load_from(value.price);
|
||||
side = value.side.Aggregate(Side.Undefined, (flags, s) => flags | s);
|
||||
}
|
||||
|
||||
public TradeAlertPredicate create() =>
|
||||
new TradeAlertPredicate(
|
||||
security_criteria,
|
||||
quantity.create(),
|
||||
price.create(),
|
||||
EnumHelper.split_flags(side).ToArray());
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using mbts.spark;
|
||||
using mbts.ui.shell;
|
||||
|
||||
namespace mbts.ui.spark {
|
||||
class TradeAlertVM : ViewModelBase, IValueFactory<TradeAlert> {
|
||||
bool _is_enabled;
|
||||
|
||||
public TradeAlertPredicateVM predicate { get; } = new TradeAlertPredicateVM();
|
||||
public SoundNotificationVM sound { get; } = new SoundNotificationVM();
|
||||
public bool is_enabled { get => _is_enabled; set => Set(ref _is_enabled, value); }
|
||||
|
||||
public TradeAlert create() =>
|
||||
new TradeAlert(
|
||||
predicate.create(),
|
||||
sound.create(),
|
||||
is_enabled);
|
||||
|
||||
public void load_from(TradeAlert value) {
|
||||
predicate.load_from(value.predicate);
|
||||
sound.load_from(value.sound);
|
||||
is_enabled = value.is_enabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
using bts.api;
|
||||
using bts.shell;
|
||||
using mbts.spark;
|
||||
using mbts.ui.shell;
|
||||
using mbts.utils;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace mbts.ui.spark {
|
||||
class TradeAlertsVM : ViewModelBase, IValueFactory<IReadOnlyList<TradeAlert>> {
|
||||
public TradeAlertsVM() {
|
||||
add_alert_cmd = new DelegateCommand(() => {
|
||||
var a = new TradeAlertVM();
|
||||
a.load_from(_default_alert());
|
||||
alerts.Add(a);
|
||||
});
|
||||
delete_alert_cmd = new DelegateCommand<TradeAlertVM>(a => alerts.Remove(a));
|
||||
}
|
||||
|
||||
public ObservableCollection<TradeAlertVM> alerts { get; } = new ObservableCollection<TradeAlertVM>();
|
||||
|
||||
public ICommand add_alert_cmd { get; }
|
||||
public ICommand delete_alert_cmd { get; }
|
||||
|
||||
public IReadOnlyList<TradeAlert> create() =>
|
||||
alerts.Select(a => a.create()).ToList();
|
||||
|
||||
public void load_from(IReadOnlyList<TradeAlert> value) =>
|
||||
alerts.sync_with_models(value);
|
||||
|
||||
static TradeAlert _default_alert() =>
|
||||
new TradeAlert(
|
||||
new TradeAlertPredicate(side: new[] { Side.Buy, Side.Sell }),
|
||||
new SoundNotification {
|
||||
system_sound = SoundNotification.SystemSound.BellCounter
|
||||
},
|
||||
true);
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using bts.collections;
|
||||
using bts.utils;
|
||||
using mbts.collections;
|
||||
using mbts.persist;
|
||||
using mbts.ui;
|
||||
using mbts.ui.controls;
|
||||
using mbts.ui.shell;
|
||||
using mbts.ui.spark;
|
||||
using mbts.ui.utility;
|
||||
using mbts.utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace mbts.spark {
|
||||
class TradeSoundsVM : DisposableViewModelBase {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -13,11 +13,17 @@
|
||||
u:WindowStyle.AllowResize="True"
|
||||
u:WindowStyle.SizeToContent="WidthAndHeight"
|
||||
u:WindowStyle.TitleContent="{Binding}"
|
||||
Title="{Binding title, StringFormat='{}{0} Settings'}"
|
||||
Icon="{x:Static u:Icons.Admin}"
|
||||
Style="{StaticResource BTSDialog}"
|
||||
Style="{StaticResource BTSWindowTitleTemplate}"
|
||||
UseLayoutRounding="True"
|
||||
>
|
||||
<u:WindowStyle.TitleContentTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal" Margin="-2,-2,0,1">
|
||||
<Image HorizontalAlignment="Left" VerticalAlignment="Stretch" Height="17" Margin="0,-2,6,0" Source="{x:Static u:Icons.Admin}"/>
|
||||
<TextBlock Margin="0,-1,0,0" Text="{Binding title, StringFormat='{}{0} Settings'}"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</u:WindowStyle.TitleContentTemplate>
|
||||
<Window.Resources>
|
||||
<!--Primitive Constants-->
|
||||
<sys:Double x:Key="VerticalPush1">4</sys:Double>
|
||||
@@ -43,8 +49,6 @@
|
||||
<RowDefinition Height="{Binding Source={StaticResource VerticalPush1}}"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="{Binding Source={StaticResource VerticalPush1}}"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="{Binding Source={StaticResource VerticalPush1}}"/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
@@ -65,10 +69,8 @@
|
||||
</Grid>
|
||||
<TextBlock Grid.Row="2">Font Color (default)</TextBlock>
|
||||
<c:ColorPickerPopupWithOpacitySlider Grid.Row="2" Grid.Column="2" HorizontalAlignment="Left" Width="{StaticResource ControlLongWidth}" DataContext="{Binding font_color}" CanUserEditOpacity="False"/>
|
||||
<TextBlock Grid.Row="4" Visibility="{Binding show_background_color, Converter={StaticResource BoolToVisibility}}">Background Color (default)</TextBlock>
|
||||
<c:ColorPickerPopupWithOpacitySlider Grid.Row="4" Grid.Column="2" HorizontalAlignment="Left" Width="{StaticResource ControlLongWidth}" DataContext="{Binding background_color}" Visibility="{Binding DataContext.show_background_color, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=usp:GridSettingsView}, Converter={StaticResource BoolToVisibility}}" />
|
||||
<TextBlock Grid.Row="6">Selection Highlight</TextBlock>
|
||||
<c:ColorPickerPopupWithOpacitySlider Grid.Row="6" Grid.Column="2" HorizontalAlignment="Left" Width="{StaticResource ControlLongWidth}" DataContext="{Binding selection_highlight}"/>
|
||||
<TextBlock Grid.Row="4">Selection Highlight</TextBlock>
|
||||
<c:ColorPickerPopupWithOpacitySlider Grid.Row="4" Grid.Column="2" HorizontalAlignment="Left" Width="{StaticResource ControlLongWidth}" DataContext="{Binding selection_highlight}"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
|
||||
@@ -178,7 +180,8 @@
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<c:ActionMenu HorizontalAlignment="Right" Margin="0,2,0,0" DataContext="{Binding DataContext, RelativeSource={RelativeSource AncestorType=usp:ShellSettingsDialog}}" ItemsSource="{Binding action_menu_items}"/>
|
||||
<Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Top" Height="1" Fill="{StaticResource HeaderBackgroundBorderBrush}"/>
|
||||
<c:ActionMenu HorizontalAlignment="Right" Margin="0,2,0,0" ItemsSource="{Binding instance_vm.action_menu_items}"/>
|
||||
<usp:GridSettingsView Grid.Row="1" Margin="8,-8,8,8" GridSectionContent="{Binding}" GridSectionContentTemplate="{StaticResource GridSectionCoreContentTemplate}"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
@@ -210,6 +213,7 @@
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Rectangle Grid.ColumnSpan="5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Fill="{StaticResource HeaderBackgroundBrush}" Stroke="{StaticResource HeaderBackgroundBorderBrush}" StrokeThickness="1"/>
|
||||
<TextBlock Grid.Column="1" VerticalAlignment="Center">Profile:</TextBlock>
|
||||
<ComboBox Grid.Column="2" Margin="8,4" ItemsSource="{Binding Source={StaticResource ProfilesViewSource}}" SelectedItem="{Binding selected_profile}" ItemTemplateSelector="{StaticResource ProfileItemTemplateSelector}">
|
||||
<ComboBox.GroupStyle>
|
||||
@@ -396,7 +400,8 @@
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<c:ActionMenu HorizontalAlignment="Right" Margin="0,2,0,0" DataContext="{Binding DataContext, RelativeSource={RelativeSource AncestorType=usp:ShellSettingsDialog}}" ItemsSource="{Binding action_menu_items}"/>
|
||||
<Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Top" Height="1" Fill="{StaticResource HeaderBackgroundBorderBrush}"/>
|
||||
<c:ActionMenu HorizontalAlignment="Right" Margin="0,2,0,0" ItemsSource="{Binding instance_vm.action_menu_items}"/>
|
||||
<usp:GridSettingsView Grid.Row="1" Margin="8,-8,8,8" DataContext="{Binding core_settings_vm}" GridSectionContent="{Binding DataContext.instance_vm, RelativeSource={RelativeSource AncestorType=usp:ShellSettingsDialog}}">
|
||||
<usp:GridSettingsView.GridSectionContentTemplate>
|
||||
<DataTemplate>
|
||||
@@ -456,4 +461,4 @@
|
||||
</c:DialogSkirt.Background>
|
||||
</c:DialogSkirt>
|
||||
</Grid>
|
||||
</Window>
|
||||
</Window>
|
||||
@@ -136,8 +136,8 @@ namespace mbts.spark {
|
||||
var skip_local = profiles.WrapSkip(1);
|
||||
var skip_local_wrapper = skip_local.WrapSelector(
|
||||
p => new SettingsProfile<T>(p.id, p.name, p.value.create(), p.security_criteria),
|
||||
ns => new ProfileVM(ns.id, vm_factory(ns.value), ns.name, ns.security_criteria, target_value: ns.value));
|
||||
profiles.Add(_local_profile = new ProfileVM(0, vm_factory(init), "Custom", template_key: ShellSettingsDialog.LocalProfileItemTemplateKey, target_value: init));
|
||||
ns => new ProfileVM(ns.id, vm_factory(ns.value), ns.name, ns.security_criteria));
|
||||
profiles.Add(_local_profile = new ProfileVM(0, vm_factory(init), "Custom", template_key: ShellSettingsDialog.LocalProfileItemTemplateKey));
|
||||
_selected_profile = backing(nameof(selected_profile), _local_profile);
|
||||
// dialog skirt items
|
||||
_apply_item = new DialogSkirtItem("Apply", new DelegateCommand(apply));
|
||||
@@ -325,37 +325,26 @@ namespace mbts.spark {
|
||||
//
|
||||
SecurityCriteria _security_criteria;
|
||||
SettingsProfile<T> _committed, _last_result;
|
||||
T _target_value;
|
||||
string _name, _template_key;
|
||||
bool _is_selected;
|
||||
IDisposable _dispose;
|
||||
|
||||
public ProfileVM(uint id, TVM value, string name, SecurityCriteria security_criteria = null, string template_key = null, T target_value = null) {
|
||||
public ProfileVM(uint id, TVM value, string name, SecurityCriteria security_criteria = null, string template_key = null) {
|
||||
this.id = id;
|
||||
this.value = value;
|
||||
_name = name;
|
||||
_security_criteria = security_criteria;
|
||||
_template_key = template_key;
|
||||
_target_value = target_value;
|
||||
_committed = _last_result = create();
|
||||
_is_dirty = backing(nameof(is_dirty), () => !_last_result.Equals(_committed));
|
||||
_refresh_is_dirty = new DeferrableActionWrapper(_is_dirty.refresh);
|
||||
var sub = value.Subscribe(v => {
|
||||
_last_result = _last_result.with(value: v);
|
||||
_refresh_is_dirty.execute();
|
||||
_on_value_changed();
|
||||
});
|
||||
_dispose = value is IDisposable d ? Disposable.Create(sub, d) : sub;
|
||||
}
|
||||
|
||||
void _on_value_changed() {
|
||||
if (_target_value != null && is_dirty && _last_result.value.Equals(_target_value)) {
|
||||
_target_value = null;
|
||||
_committed = _last_result;
|
||||
_refresh_is_dirty.execute();
|
||||
}
|
||||
}
|
||||
|
||||
public uint id { get; }
|
||||
public TVM value { get; }
|
||||
public string name {
|
||||
@@ -391,26 +380,21 @@ namespace mbts.spark {
|
||||
/// assign directly to <see cref="_last_result"/>, to capture props that aren't visible in the dialog (i.e., props that are manipulated in the main window)
|
||||
_last_result = data;
|
||||
}
|
||||
_on_value_changed();
|
||||
}
|
||||
|
||||
public void load_value_from(T value) {
|
||||
using (_refresh_is_dirty.defer_execution())
|
||||
this.value.load_from(value);
|
||||
_on_value_changed();
|
||||
}
|
||||
|
||||
public void commit(SettingsProfile<T> data) {
|
||||
using (_refresh_is_dirty.defer_execution())
|
||||
_load_from(_committed = data);
|
||||
_on_value_changed();
|
||||
}
|
||||
|
||||
public void commit_current() {
|
||||
_committed = _last_result;
|
||||
_target_value = _committed.value;
|
||||
_refresh_is_dirty.execute();
|
||||
_on_value_changed();
|
||||
}
|
||||
|
||||
public void revert() {
|
||||
@@ -418,7 +402,6 @@ namespace mbts.spark {
|
||||
_load_from(_committed);
|
||||
_last_result = _committed;
|
||||
}
|
||||
_on_value_changed();
|
||||
}
|
||||
|
||||
public IObservable<bool> subscribe_is_dirty() => _is_dirty.observe_current(true);
|
||||
@@ -431,7 +414,6 @@ namespace mbts.spark {
|
||||
public override string ToString() => _name;
|
||||
|
||||
void _load_from(SettingsProfile<T> data) {
|
||||
_target_value = data.value;
|
||||
name = data.name;
|
||||
security_criteria = data.security_criteria;
|
||||
value.load_from(data.value);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using mbts.utils;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@@ -10,6 +9,8 @@ namespace mbts.ui.shell
|
||||
{
|
||||
public class WorkspaceViewModel : ViewModelBase
|
||||
{
|
||||
private const string INITIAL_VISUAL_STATE = "Normal";
|
||||
|
||||
WorkspacesModel _parent;
|
||||
public WorkspaceViewModel(WorkspacesModel parent, string name, WorkspaceDefinition definition) {
|
||||
_parent = parent;
|
||||
@@ -18,12 +19,45 @@ namespace mbts.ui.shell
|
||||
}
|
||||
|
||||
public string Name { get; private set; }
|
||||
public DateTime LastSaveTime => Definition.last_save_lldatetime.lldatetime().ToLocalTime();
|
||||
public bool IsShared { get { return Definition.is_shared; } }
|
||||
public object Icon {
|
||||
get { return Icons.Window; }
|
||||
}
|
||||
|
||||
private string _vstate = INITIAL_VISUAL_STATE;
|
||||
public string VisualState {
|
||||
get {
|
||||
// this unconventional getter acts as a reset after the VisualStateManager has played
|
||||
// i.e. if we overwrite a workspace then save, then close the window, when we open the
|
||||
// window back up, we'll see item animate b/c it has never been reset from the animate
|
||||
// state. This is necessary b/c the lifetime of Workspaces is not tied to the lifetime
|
||||
// of the View, rather it lives in the Model and is only instantiated on startup via MEF
|
||||
var return_val = _vstate;
|
||||
_vstate = INITIAL_VISUAL_STATE;
|
||||
return return_val;
|
||||
}
|
||||
set {
|
||||
_vstate = value;
|
||||
RaisePropertyChanged("VisualState");
|
||||
}
|
||||
}
|
||||
|
||||
internal WorkspaceDefinition Definition { get; private set; }
|
||||
|
||||
public ICommand ActivateCommand {
|
||||
get {
|
||||
return new DelegateCommand(param => {
|
||||
var unused = _parent.load_workspace(Definition, Name);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public ICommand DeleteCommand {
|
||||
get {
|
||||
return new DelegateCommand(param => {
|
||||
_parent.delete_workspace(Name, Definition);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,6 @@
|
||||
xmlns:local="clr-namespace:mbts.ui.shell"
|
||||
mc:Ignorable="d"
|
||||
xmlns:controls="clr-namespace:mbts.ui.controls"
|
||||
xmlns:sys="clr-namespace:System;assembly=mscorlib"
|
||||
xmlns:ui="clr-namespace:mbts.ui"
|
||||
Width="250"
|
||||
MaxHeight="700"
|
||||
@@ -14,108 +13,54 @@
|
||||
ui:WindowStyle.ShowMinimizeButton="False"
|
||||
Style="{StaticResource BTSDialog}"
|
||||
Title="Workspaces"
|
||||
Topmost="True"
|
||||
>
|
||||
<Window.Resources>
|
||||
<ResourceDictionary>
|
||||
<Style x:Key="CommonList" TargetType="{x:Type ItemsControl}">
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="BorderThickness" Value="0" />
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
</Window.Resources>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel Grid.Row="1" Margin="5">
|
||||
<GroupBox Header="Existing Workspaces">
|
||||
<StackPanel>
|
||||
<ComboBox x:Name="workspaces_cb" Margin="4" Grid.IsSharedSizeScope="True" ItemsSource="{DynamicResource header_and_data}" SelectedValue="{Binding selected_workspace, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
|
||||
<!-- Inspiration for the table like appearance with headers taken from
|
||||
https://stackoverflow.com/questions/5807899/multiple-item-combo-box-with-headers
|
||||
-->
|
||||
<ComboBox.Resources>
|
||||
<CompositeCollection x:Key="header_and_data">
|
||||
<ComboBoxItem IsEnabled="False">
|
||||
<Grid TextElement.FontWeight="Bold">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="NameGroup"/>
|
||||
<ColumnDefinition Width="5"/>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LastSaveTime_DateGroup" />
|
||||
<ColumnDefinition Width="5"/>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LastSaveTime_TimeGroup" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" Text="Name"/>
|
||||
<TextBlock Grid.Column="2" Grid.ColumnSpan="3" Text="Last Save Time"/>
|
||||
</Grid>
|
||||
</ComboBoxItem>
|
||||
<Separator/>
|
||||
<CollectionContainer Collection="{Binding Source={x:Reference workspaces_cb}, Path=DataContext.Workspaces}"/>
|
||||
</CompositeCollection>
|
||||
<DataTemplate DataType="{x:Type local:WorkspaceViewModel}">
|
||||
<Window.Resources>
|
||||
<ResourceDictionary>
|
||||
<Style x:Key="CommonList" TargetType="{x:Type ItemsControl}">
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="BorderThickness" Value="0" />
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
</Window.Resources>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="NameGroup"/>
|
||||
<ColumnDefinition Width="5"/>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LastSaveTime_DateGroup" />
|
||||
<ColumnDefinition Width="5"/>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LastSaveTime_TimeGroup" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" Text="{Binding Name}"/>
|
||||
<TextBlock Grid.Column="2" HorizontalAlignment="Right">
|
||||
<TextBlock.Style>
|
||||
<Style TargetType="TextBlock">
|
||||
<Setter Property="Text" Value="{Binding LastSaveTime, StringFormat=d}" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding LastSaveTime}" Value="{x:Static sys:DateTime.MinValue}">
|
||||
<Setter Property="Text" Value="" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</TextBlock.Style>
|
||||
</TextBlock>
|
||||
<TextBlock Grid.Column="4" HorizontalAlignment="Right">
|
||||
<TextBlock.Style>
|
||||
<Style TargetType="TextBlock">
|
||||
<Setter Property="Text" Value="{Binding LastSaveTime, StringFormat=T}" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding LastSaveTime}" Value="{x:Static sys:DateTime.MinValue}">
|
||||
<Setter Property="Text" Value="" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</TextBlock.Style>
|
||||
</TextBlock>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Border BorderThickness="0,0,0,0" BorderBrush="{ui:BTSColor UI.Button.normal_border}">
|
||||
<Grid Margin="5 7 5 7">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock>Save Current Workspace As:</TextBlock>
|
||||
<Grid Grid.Row="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBox Margin="4" Text="{Binding SaveName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Stretch" />
|
||||
<Button Grid.Column="1" HorizontalAlignment="Right" Margin="4" Command="{Binding SaveWorkspace}" IsDefault="True" MinWidth="60">Save</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Border>
|
||||
<ScrollViewer Grid.Row="1" Grid.ColumnSpan="2" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Auto">
|
||||
<ItemsControl ItemsSource="{Binding Workspaces}" Style="{StaticResource CommonList}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<controls:WorkspaceItemControl />
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ScrollViewer>
|
||||
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ComboBox.Resources>
|
||||
</ComboBox>
|
||||
<UniformGrid Rows="1" Margin="4" >
|
||||
<Button Grid.Column="1" MinWidth="60" Command="{Binding LoadCommand}">Load</Button>
|
||||
<Button Grid.Column="2" MinWidth="60" Command="{Binding SaveCommand}">Save</Button>
|
||||
<Button Grid.Column="3" MinWidth="60" Command="{Binding DeleteCommand}">Delete</Button>
|
||||
</UniformGrid>
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
<Border BorderThickness="0,0,0,0" BorderBrush="{ui:BTSColor UI.Button.normal_border}">
|
||||
<Grid Margin="5 7 5 7">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid Grid.Row="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBox Margin="4" Text="{Binding SaveName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Stretch" />
|
||||
<Button Grid.Column="2" MinWidth="60" Command="{Binding SaveNewWorkspace}">Create New</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<!--controls:DialogSkirtOKCancel Grid.Row="1" Grid.ColumnSpan="2"/-->
|
||||
</Grid>
|
||||
</Window>
|
||||
@@ -8,15 +8,12 @@ using System.Threading.Tasks;
|
||||
using mbts;
|
||||
using mbts.ui;
|
||||
using mbts.ui.shell;
|
||||
using bts.spark;
|
||||
using System.Windows;
|
||||
|
||||
namespace mbts.ui.shell
|
||||
{
|
||||
public class WorkspacesDialogViewModel : ViewModelBaseWithValidation
|
||||
{
|
||||
private WorkspacesModel _work_model;
|
||||
IRaiseCanExecuteChangedCommand _load_command, _delete_command, _save_command;
|
||||
|
||||
public WorkspacesDialogViewModel(WorkspacesModel wm)
|
||||
{
|
||||
@@ -28,74 +25,36 @@ namespace mbts.ui.shell
|
||||
public bool IsPublicWorkspace { get; set; }
|
||||
public bool ShowWorkspaces { get; set; }
|
||||
|
||||
WorkspaceViewModel _selected_workspace;
|
||||
public WorkspaceViewModel selected_workspace {
|
||||
get => _selected_workspace;
|
||||
set {
|
||||
if (Set(ref _selected_workspace, value)) {
|
||||
LoadCommand.TryRaiseCanExecuteChanged();
|
||||
DeleteCommand.TryRaiseCanExecuteChanged();
|
||||
SaveCommand.TryRaiseCanExecuteChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _command_can_execute() => selected_workspace != null;
|
||||
private void _clear_error_state() => clear_error(nameof(SaveName));
|
||||
|
||||
public ICommand LoadCommand => _load_command ??
|
||||
(_load_command = new DelegateCommand(
|
||||
() => {
|
||||
_work_model.load_workspace(selected_workspace.Definition, selected_workspace.Name).ignore();
|
||||
_clear_error_state();
|
||||
}, _command_can_execute));
|
||||
public ICommand DeleteCommand => _delete_command ??
|
||||
(_delete_command = new DelegateCommand(
|
||||
() => {
|
||||
if (BTSMessageBox.Show(
|
||||
$"Delete the \"{selected_workspace.Name}\" workspace?",
|
||||
"Delete Existing Workspace Confirmation",
|
||||
BTSMessageBoxButtons.Yes | BTSMessageBoxButtons.No,
|
||||
MessageBoxImage.Question) == BTSMessageBoxButtons.Yes) {
|
||||
_work_model.delete_workspace(selected_workspace.Name, selected_workspace.Definition).ignore();
|
||||
_clear_error_state();
|
||||
}
|
||||
}, _command_can_execute));
|
||||
public ICommand SaveCommand => _save_command ??
|
||||
(_save_command = new DelegateCommand(
|
||||
() => {
|
||||
if (BTSMessageBox.Show(
|
||||
$"Overwrite \"{selected_workspace.Name}\" with current layout?",
|
||||
"Save Existing Workspace Confirmation",
|
||||
BTSMessageBoxButtons.Yes | BTSMessageBoxButtons.No,
|
||||
MessageBoxImage.Question) == BTSMessageBoxButtons.Yes) {
|
||||
_work_model.save_workspace(selected_workspace.Name, ispublic:selected_workspace.Definition.is_shared);
|
||||
_clear_error_state();
|
||||
}
|
||||
}, _command_can_execute));
|
||||
|
||||
public ICommand SaveNewWorkspace
|
||||
public ICommand SaveWorkspace
|
||||
{
|
||||
get
|
||||
{
|
||||
return new DelegateCommand(arg => {
|
||||
_clear_error_state();
|
||||
// Check for errors in the input
|
||||
if (string.IsNullOrEmpty(SaveName)) {
|
||||
set_error(nameof(SaveName), "Workspace must be named");
|
||||
if (string.IsNullOrEmpty(SaveName))
|
||||
{
|
||||
set_error("SaveName", "Workspace must be named");
|
||||
return;
|
||||
}
|
||||
if (SaveName.Contains(WorkspacesModel.ACTIVE_WORKSPACE_PREFIX)) {
|
||||
set_error(nameof(SaveName), "Workspace name cannot contains '{0}'".fmt(WorkspacesModel.ACTIVE_WORKSPACE_PREFIX));
|
||||
if (SaveName.Contains(WorkspacesModel.ACTIVE_WORKSPACE_PREFIX))
|
||||
{
|
||||
set_error("SaveName", "Workspace name cannot contains '{0}'".fmt(WorkspacesModel.ACTIVE_WORKSPACE_PREFIX));
|
||||
return;
|
||||
}
|
||||
if (Workspaces.Any(x => x.Name.Replace(WorkspacesModel.SHARED_MONIKER, string.Empty) == SaveName && x.IsShared == IsPublicWorkspace)) {
|
||||
set_error(nameof(SaveName), $"Workspace already exists '{SaveName}'");
|
||||
return;
|
||||
else
|
||||
{
|
||||
clear_error("SaveName");
|
||||
}
|
||||
var save_candidate_already_exists = Workspaces.Any(x => x.Name.Replace(WorkspacesModel.SHARED_MONIKER, string.Empty) == SaveName && x.IsShared == IsPublicWorkspace);
|
||||
// Save
|
||||
_work_model.save_workspace(SaveName, ispublic: IsPublicWorkspace);
|
||||
|
||||
// if we just overwrote something, have the vm reflect it
|
||||
if (save_candidate_already_exists)
|
||||
Workspaces.Where(x => x.Name.Replace(WorkspacesModel.SHARED_MONIKER, string.Empty) == SaveName && x.IsShared == IsPublicWorkspace).ForEach(x => x.VisualState = "Overridden");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -179,7 +179,6 @@ namespace mbts.ui.shell
|
||||
public void save_workspace(string name, bool persist=true, bool ispublic=false) {
|
||||
WorkspaceDefinition workspace = get_workspace();
|
||||
workspace.is_shared = ispublic;
|
||||
workspace.last_save_lldatetime = LLDateTime.now().value;
|
||||
if (ispublic) { name += SHARED_MONIKER; }
|
||||
|
||||
// Remove the old one if a workspace by this name exists already
|
||||
@@ -283,8 +282,5 @@ namespace mbts.ui.shell
|
||||
[ProtoMember(10, IsRequired = false)]
|
||||
public bool is_shared { get; set; }
|
||||
|
||||
[ProtoMember(11, IsRequired = false)]
|
||||
public ulong last_save_lldatetime { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -91,10 +91,6 @@ namespace mbts.spark {
|
||||
public static double sod_pos(this PositionUnitImmutable position_unit) => position_unit.SodLongPos - position_unit.SodShortPos;
|
||||
public static double day_pos(this PositionUnitImmutable position_unit) => position_unit.DayLongPos - position_unit.DayShortPos;
|
||||
public static double total_pos(this PositionUnitImmutable position_unit) => position_unit.sod_pos() + position_unit.day_pos();
|
||||
// raw day
|
||||
public static double raw_day_long_pos(this PositionUnitImmutable position_unit) => position_unit.HasRawDay ? position_unit.RawDay.LongPos : double.NaN;
|
||||
public static double raw_day_short_pos(this PositionUnitImmutable position_unit) => position_unit.HasRawDay ? position_unit.RawDay.ShortPos : double.NaN;
|
||||
public static double raw_day_pos(this PositionUnitImmutable position_unit) => position_unit.HasRawDay ? position_unit.RawDay.LongPos - position_unit.RawDay.ShortPos : double.NaN;
|
||||
|
||||
public static void lazy_assert(bool test, Func<string> get_msg) {
|
||||
if (!test)
|
||||
|
||||
@@ -197,7 +197,6 @@ namespace mbts.spark {
|
||||
static class TickStore {
|
||||
public static readonly RegionalTickFactors default_rtf = new RegionalTickFactors(new TickFactors(1000, 1), new TickFactors(1000, 1), double.MaxValue);
|
||||
static readonly ConcurrentDictionary<SpecKey, RegionalTickFactors> _exchange_ticks = new ConcurrentDictionary<SpecKey, RegionalTickFactors>();
|
||||
static readonly bool _som_ticks_fix_enabled = config.Configuration.get<bts.config.toggles.SOMTicksFix>().enabled;
|
||||
|
||||
public static RegionalTickFactors get(ContractSpecification spec) =>
|
||||
_exchange_ticks.GetOrAdd(new SpecKey(spec), _create_rtf);
|
||||
@@ -250,23 +249,8 @@ namespace mbts.spark {
|
||||
var tf = _try_create_tf(tick_size, tick_base);
|
||||
if (tf != null && display_digits_override > 0) {
|
||||
int display_digit_diff = (int)display_digits_override - tf.display_digits();
|
||||
if (_som_ticks_fix_enabled) {
|
||||
// we can always add display_digits (i.e. dislpay_digit_diff > 0), of course the ticks can be expressed precisely with extra 0s.
|
||||
// but if we are going to remove display_digits (i.e display_digit_diff < 0), we must make sure the tick_unit is large enough to be expressed precisely with that many fewer digits
|
||||
// - "-display_digit_diff <= Math.Ceiling(Math.Log10(tf.tick_unit))" will tell us if the digits can be removed without compromising precision
|
||||
// - unfortunately, we cannot rely on the exchange's ContractSpec for this, there is 1 known case where the data they give us is incorrect
|
||||
// SOM spreads ContractSpec (from the exchange):
|
||||
// LowTick: 0.0025 (aka 1/400, or 2/800)
|
||||
// MainFraction: 8
|
||||
// SubFraction: 0
|
||||
// PriceDisplayFormat: 1
|
||||
// - as you can see, this cannot possibly be formatted with 1 display digit, it ticks in increments of 1/400
|
||||
// - in practice, to maintain the '8' base, we format this in increments of 2/800 (e.g. 1-000, 1-002, 1-004, and so on up to 1-778, 2-000)
|
||||
if (display_digit_diff > 0 || (display_digit_diff < 0 && -display_digit_diff <= Math.Ceiling(Math.Log10(tf.tick_unit))))
|
||||
tf = _inflate_to_base(tf, tf.tick_base * Math.Pow(10, display_digit_diff));
|
||||
} else if (display_digit_diff != 0) {
|
||||
if (display_digit_diff != 0)
|
||||
tf = _inflate_to_base(tf, tf.tick_base * Math.Pow(10, display_digit_diff));
|
||||
}
|
||||
}
|
||||
return tf;
|
||||
}
|
||||
|
||||
@@ -65,8 +65,6 @@ namespace mbts.spark {
|
||||
grouping.Where(y => y.fill.FillKind != Fill.Types.FillKind.Sod).Sum(y => (uint)y.sell_quantity),
|
||||
grouping.Sum(y => (int)(y.buy_quantity - y.sell_quantity)),
|
||||
grouping.Where(y => y.fill.FillKind == Fill.Types.FillKind.Sod).Sum(y => (int)(y.buy_quantity - y.sell_quantity)),
|
||||
double.NaN,
|
||||
double.NaN,
|
||||
grouping.weighted_average(y => y.fill.LastPrice, y => y.buy_quantity),
|
||||
grouping.weighted_average(y => y.fill.LastPrice, y => y.sell_quantity),
|
||||
grouping.Select(i => i.fill),
|
||||
@@ -97,8 +95,6 @@ namespace mbts.spark {
|
||||
(uint)pos.DayShortPos,
|
||||
(int)pos.total_pos(),
|
||||
(int)pos.sod_pos(),
|
||||
pos.raw_day_long_pos(),
|
||||
pos.raw_day_short_pos(),
|
||||
pos.DayLongAvgPrice,
|
||||
pos.DayShortAvgPrice,
|
||||
null,
|
||||
@@ -108,7 +104,7 @@ namespace mbts.spark {
|
||||
|
||||
// remove rows for which there is no net pos or no day pos
|
||||
summary_position_rows = summary_position_rows.Where(r =>
|
||||
!(r.start_net_position == 0 && r.day_long_quantity == 0 && r.day_short_quantity == 0 && r.raw_day_long_position == 0 && r.raw_day_short_position == 0)
|
||||
!(r.start_net_position == 0 && r.day_long_quantity == 0 && r.day_short_quantity == 0)
|
||||
);
|
||||
_pnl_data.sync_with(summary_position_rows);
|
||||
_last_pnl_update_time = DateTime.Now;
|
||||
@@ -128,7 +124,6 @@ namespace mbts.spark {
|
||||
start_pnl_filter.load_state(fs.start_pnl);
|
||||
day_long_quantity_filter.load_state(fs.day_long_pos);
|
||||
day_short_quantity_filter.load_state(fs.day_short_pos);
|
||||
raw_day_net_position_filter.load_state(fs.raw_day_net_pos);
|
||||
day_pnl_filter.load_state(fs.day_pnl);
|
||||
total_net_position_filter.load_state(fs.total_net_pos);
|
||||
total_pnl_filter.load_state(fs.total_pnl);
|
||||
@@ -151,8 +146,6 @@ namespace mbts.spark {
|
||||
add_col(Columns.StartPNL, DefaultColumnHeaders.StartPnL, r => r.start_pnl_text, nameof(PnLSummaryRow.start_pnl), DefaultColumnWidths.PnL_Generic, start_pnl_filter, DataGridFilterKnownTemplates.DoubleMinMax, HorizontalAlignment.Right, r => r.start_pnl_background);
|
||||
add_col(Columns.DayLong, DefaultColumnHeaders.DayLong, r => r.day_long_quantity.ToString(), nameof(PnLSummaryRow.day_long_quantity), DefaultColumnWidths.PnL_Generic, day_long_quantity_filter, DataGridFilterKnownTemplates.UIntMinMax, HorizontalAlignment.Right);
|
||||
add_col(Columns.DayShort, DefaultColumnHeaders.DayShort, r => r.day_short_quantity.ToString(), nameof(PnLSummaryRow.day_short_quantity), DefaultColumnWidths.PnL_Generic, day_short_quantity_filter, DataGridFilterKnownTemplates.UIntMinMax, HorizontalAlignment.Right);
|
||||
// convert nan to 0 so text is consistent with other position columns
|
||||
add_col(Columns.RawDayPos, DefaultColumnHeaders.RawDayPos, r => SparkUtils.fmt_position(r.raw_day_net_position), nameof(PnLSummaryRow.raw_day_net_position), DefaultColumnWidths.PnL_Generic, raw_day_net_position_filter, DataGridFilterKnownTemplates.DoubleMinMax, HorizontalAlignment.Right);
|
||||
add_col(Columns.DayPNL, DefaultColumnHeaders.DayPnL, r => r.day_pnl_text, nameof(PnLSummaryRow.day_pnl), 90, day_pnl_filter, DataGridFilterKnownTemplates.DoubleMinMax, HorizontalAlignment.Right, r => r.day_pnl_background);
|
||||
add_col(Columns.TradedToday, "Traded Today", r => r.has_day_trades_text, nameof(PnLSummaryRow.has_day_trades), 100, has_day_trades_filter, DataGridFilterKnownTemplates.YesNo, HorizontalAlignment.Center);
|
||||
add_col(Columns.TotalNetPos, DefaultColumnHeaders.TotalNetPos, r => r.total_net_position.ToString(), nameof(PnLSummaryRow.total_net_position), DefaultColumnWidths.Total_Net_Pos, total_net_position_filter, DataGridFilterKnownTemplates.DoubleMinMax, HorizontalAlignment.Right);
|
||||
@@ -195,7 +188,6 @@ namespace mbts.spark {
|
||||
start_pnl_filter,
|
||||
day_long_quantity_filter,
|
||||
day_short_quantity_filter,
|
||||
raw_day_net_position_filter,
|
||||
day_pnl_filter,
|
||||
total_net_position_filter,
|
||||
total_pnl_filter,
|
||||
@@ -319,7 +311,6 @@ namespace mbts.spark {
|
||||
public IDataGridColumnPersistentFilter<PnLSummaryRow, DoubleMinMaxState> start_pnl_filter { get; } = new DoubleMinMax<PnLSummaryRow>(r => r.start_pnl, nameof(PnLSummaryRow.start_pnl));
|
||||
public IDataGridColumnPersistentFilter<PnLSummaryRow, UIntMinMaxState> day_long_quantity_filter { get; } = new UIntMinMax<PnLSummaryRow>(r => r.day_long_quantity, nameof(PnLSummaryRow.day_long_quantity));
|
||||
public IDataGridColumnPersistentFilter<PnLSummaryRow, UIntMinMaxState> day_short_quantity_filter { get; } = new UIntMinMax<PnLSummaryRow>(r => r.day_short_quantity, nameof(PnLSummaryRow.day_short_quantity));
|
||||
public IDataGridColumnPersistentFilter<PnLSummaryRow, DoubleMinMaxState> raw_day_net_position_filter { get; } = new DoubleMinMax<PnLSummaryRow>(r => r.raw_day_net_position, nameof(PnLSummaryRow.raw_day_net_position));
|
||||
public IDataGridColumnPersistentFilter<PnLSummaryRow, DoubleMinMaxState> day_pnl_filter { get; } = new DoubleMinMax<PnLSummaryRow>(r => r.day_pnl, nameof(PnLSummaryRow.day_pnl));
|
||||
public IDataGridColumnPersistentFilter<PnLSummaryRow, IntMinMaxState> total_net_position_filter { get; } = new IntMinMax<PnLSummaryRow>(r => r.total_net_position, nameof(PnLSummaryRow.total_net_position));
|
||||
public IDataGridColumnPersistentFilter<PnLSummaryRow, DoubleMinMaxState> total_pnl_filter { get; } = new DoubleMinMax<PnLSummaryRow>(r => r.total_pnl, nameof(PnLSummaryRow.total_pnl));
|
||||
@@ -338,7 +329,6 @@ namespace mbts.spark {
|
||||
start_pnl = start_pnl_filter.get_state(),
|
||||
day_long_pos = day_long_quantity_filter.get_state(),
|
||||
day_short_pos = day_short_quantity_filter.get_state(),
|
||||
raw_day_net_pos = raw_day_net_position_filter.get_state(),
|
||||
day_pnl = day_pnl_filter.get_state(),
|
||||
total_net_pos = total_net_position_filter.get_state(),
|
||||
total_pnl = total_pnl_filter.get_state(),
|
||||
|
||||
@@ -3,7 +3,7 @@ using System.Windows.Controls;
|
||||
|
||||
namespace mbts.spark {
|
||||
internal partial class PAndLView : UserControl {
|
||||
public enum Columns { Account, Exchange, Symbol, Security, SecurityType, StartNetPos, StartPNL, DayLong, DayShort, DayPNL, TotalNetPos, TotalPNL, PNLPrice, TradedToday, RawDayPos }
|
||||
public enum Columns { Account, Exchange, Symbol, Security, SecurityType, StartNetPos, StartPNL, DayLong, DayShort, DayPNL, TotalNetPos, TotalPNL, PNLPrice, TradedToday }
|
||||
|
||||
public PAndLView(PAndLVM vm) {
|
||||
InitializeComponent();
|
||||
|
||||
@@ -24,8 +24,7 @@ namespace mbts.spark {
|
||||
PAndLWindowFactory _factory;
|
||||
IReadOnlyList<SparkCommand> _commands;
|
||||
|
||||
public PAndLWindow(OrdersModel orders_model, PAndLWindowFactory factory, IBus bus, PAndLWindowFactory.PAndLStartupData startup_data, ImageSource Icon, IWindowSettingsManager<GridSettings> settings_manager)
|
||||
: base("P&L", bus, factory, SparkCommands.ShowPAndL.icon) {
|
||||
public PAndLWindow(OrdersModel orders_model, PAndLWindowFactory factory, IBus bus, PAndLWindowFactory.PAndLStartupData startup_data, ImageSource Icon, WindowSettingsManager<GridSettings> settings_manager) : base("P&L", bus, factory, SparkCommands.ShowPAndL.icon) {
|
||||
_orders_model = orders_model;
|
||||
_factory = factory;
|
||||
_startup_data = startup_data;
|
||||
|
||||
Loaded 100 of 325 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user