1
0
Fork 0
Univerxel/include/Remotery/vis/Code/TimelineWindow.js

285 lines
7.1 KiB
JavaScript

//
// TODO: Use WebGL and instancing for quicker renders
//
TimelineWindow = (function()
{
var BORDER = 10;
var ROW_START_SIZE = 210;
var ROW_END_SIZE = 20; // make room for scrollbar
var box_template = "<div class='TimelineBox'></div>";
function TimelineWindow(wm, settings, server, check_handler)
{
this.Settings = settings;
// Ordered list of thread rows on the timeline
this.ThreadRows = [ ];
// Create window and containers
this.Window = wm.AddWindow("Timeline", 10, 20, 100, 100);
this.Window.ShowNoAnim();
this.TimelineContainer = this.Window.AddControlNew(new WM.Container(10, 10, 800, 160));
DOM.Node.AddClass(this.TimelineContainer.Node, "TimelineContainer");
var mouse_wheel_event = (/Firefox/i.test(navigator.userAgent)) ? "DOMMouseScroll" : "mousewheel";
DOM.Event.AddHandler(this.TimelineContainer.Node, mouse_wheel_event, Bind(OnMouseScroll, this));
// Setup timeline manipulation
this.MouseDown = false;
this.LastMouseState = null;
this.TimelineMoved = false;
this.OnHoverHandler = null;
this.OnSelectedHandler = null;
DOM.Event.AddHandler(this.TimelineContainer.Node, "mousedown", Bind(OnMouseDown, this));
DOM.Event.AddHandler(this.TimelineContainer.Node, "mouseup", Bind(OnMouseUp, this));
DOM.Event.AddHandler(this.TimelineContainer.Node, "mousemove", Bind(OnMouseMove, this));
// Set time range AFTER the window has been created, as it uses the window to determine pixel coverage
this.TimeRange = new PixelTimeRange(0, 200 * 1000, RowWidth(this));
this.CheckHandler = check_handler;
this.Window.SetOnResize(Bind(OnUserResize, this));
}
TimelineWindow.prototype.SetOnHover = function(handler)
{
this.OnHoverHandler = handler;
}
TimelineWindow.prototype.SetOnSelected = function(handler)
{
this.OnSelectedHandler = handler;
}
TimelineWindow.prototype.WindowResized = function(width, height, top_window)
{
// Resize window
var top = top_window.Position[1] + top_window.Size[1] + 10;
this.Window.SetPosition(10, top);
this.Window.SetSize(width - 2 * 10, 260);
ResizeInternals(this);
}
TimelineWindow.prototype.ResetTimeRange = function()
{
this.TimeRange.SetStart(0);
}
TimelineWindow.prototype.OnSamples = function(thread_name, frame_history)
{
// Shift the timeline to the last entry on this thread
// As multiple threads come through here with different end frames, only do this for the latest
var last_frame = frame_history[frame_history.length - 1];
if (last_frame.EndTime_us > this.TimeRange.End_us)
this.TimeRange.SetEnd(last_frame.EndTime_us);
// Search for the index of this thread
var thread_index = -1;
for (var i in this.ThreadRows)
{
if (this.ThreadRows[i].Name == thread_name)
{
thread_index = i;
break;
}
}
// If this thread has not been seen before, add a new row to the list and re-sort
if (thread_index == -1)
{
var row = new TimelineRow(thread_name, RowWidth(this), this.TimelineContainer.Node, frame_history, this.CheckHandler);
this.ThreadRows.push(row);
this.ThreadRows.sort(function(a, b) { return b.Name.localeCompare(a.Name); });
}
}
TimelineWindow.prototype.DrawAllRows = function()
{
var time_range = this.TimeRange;
var draw_text = this.Settings.IsPaused;
for (var i in this.ThreadRows)
{
var thread_row = this.ThreadRows[i];
thread_row.SetVisibleFrames(time_range);
thread_row.Draw(draw_text);
}
}
function RowXOffset(self)
{
// Add sizing of the label
// TODO: Use computed size
return DOM.Node.GetPosition(self.TimelineContainer.Node)[0] + ROW_START_SIZE;
}
function RowWidth(self)
{
// Subtract sizing of the label
// TODO: Use computed size
return self.TimelineContainer.Size[0] - (ROW_START_SIZE + ROW_END_SIZE);
}
function OnUserResize(self, evt)
{
ResizeInternals(self);
}
function ResizeInternals(self)
{
// Resize controls
var parent_size = self.Window.Size;
self.TimelineContainer.SetPosition(BORDER, 10);
self.TimelineContainer.SetSize(parent_size[0] - 2 * BORDER, parent_size[1] - 40);
// Resize rows
var row_width = RowWidth(self);
for (var i in self.ThreadRows)
{
var row = self.ThreadRows[i];
row.SetSize(row_width);
}
// Adjust time range to new width
self.TimeRange.SetPixelSpan(row_width);
self.DrawAllRows();
}
function OnMouseScroll(self, evt)
{
var mouse_state = new Mouse.State(evt);
var scale = 1.11;
if (mouse_state.WheelDelta > 0)
scale = 1 / scale;
// What time is the mouse hovering over?
var x = mouse_state.Position[0] - RowXOffset(self);
var time_us = self.TimeRange.Start_us + x / self.TimeRange.usPerPixel;
// Calculate start time relative to the mouse hover position
var time_start_us = self.TimeRange.Start_us - time_us;
// Scale and offset back to the hover time
self.TimeRange.Set(time_start_us * scale + time_us, self.TimeRange.Span_us * scale);
self.DrawAllRows();
// Prevent vertical scrolling on mouse-wheel
DOM.Event.StopDefaultAction(evt);
}
function OnMouseDown(self, evt)
{
// Only manipulate the timelime when paused
if (!self.Settings.IsPaused)
return;
self.MouseDown = true;
self.LastMouseState = new Mouse.State(evt);
self.TimelineMoved = false;
DOM.Event.StopDefaultAction(evt);
}
function OnMouseUp(self, evt)
{
// Only manipulate the timelime when paused
if (!self.Settings.IsPaused)
return;
var mouse_state = new Mouse.State(evt);
self.MouseDown = false;
if (!self.TimelineMoved)
{
// Search for the row being clicked and update its selection
var row_node = DOM.Event.GetNode(evt);
for (var i in self.ThreadRows)
{
var thread_row = self.ThreadRows[i];
if (thread_row.CanvasNode == row_node)
{
var select = thread_row.UpdateSelectedSample(mouse_state, RowXOffset(self));
// Call any selection handlers
if (self.OnSelectedHandler)
self.OnSelectedHandler(thread_row.Name, select);
break;
}
}
}
}
function OnMouseMove(self, evt)
{
// Only manipulate the timelime when paused
if (!self.Settings.IsPaused)
return;
var mouse_state = new Mouse.State(evt);
if (self.MouseDown)
{
// Get the time the mouse is over
var x = mouse_state.Position[0] - RowXOffset(self);
var time_us = self.TimeRange.Start_us + x / self.TimeRange.usPerPixel;
// Shift the visible time range with mouse movement
var time_offset_us = (mouse_state.Position[0] - self.LastMouseState.Position[0]) / self.TimeRange.usPerPixel;
if (time_offset_us)
{
self.TimeRange.SetStart(self.TimeRange.Start_us - time_offset_us);
self.DrawAllRows();
self.TimelineMoved = true;
}
}
else
{
// Highlight any samples the mouse moves over
var row_node = DOM.Event.GetNode(evt);
for (var i in self.ThreadRows)
{
var thread_row = self.ThreadRows[i];
if (thread_row.CanvasNode == row_node)
{
var hover = thread_row.UpdateHoverSample(mouse_state, RowXOffset(self));
if (self.OnHoverHandler)
self.OnHoverHandler(thread_row.Name, hover);
}
else
{
thread_row.SetHoverSample(null, 0);
if (self.OnHoverHandler)
self.OnHoverHandler(thread_row.Name, null);
}
}
}
self.LastMouseState = mouse_state;
}
return TimelineWindow;
})();