1
0
mirror of https://github.com/chylex/TweetDuck.git synced 2025-09-14 01:32:10 +02:00

Compare commits

..

55 Commits

Author SHA1 Message Date
9238410756 WIP? 2019-05-09 11:54:38 +02:00
7210c29cd8 Update readme (VS 2019, CefSharp version, remove MyGet reference) 2019-05-08 13:13:09 +02:00
26d90c0c9b Work around missing culture codes on Wine 2019-05-08 12:44:16 +02:00
a03b222a95 Fix emoji keyboard button not working after re-enabling w/ compose drawer open
Closes #256
2019-04-04 20:05:52 +02:00
7944a24d3c Release 1.17.4 2019-03-08 19:29:26 +01:00
cc8459c759 Fix clear-columns plugin nav button to match new TweetDeck style 2019-03-08 19:25:39 +01:00
10074ff92c Fix various alignment issues with the verified badge 2019-03-08 18:57:00 +01:00
173f25bebc Add option to disable automatic DM input focus
Closes #253
2019-03-08 18:16:37 +01:00
31680fc4ae Fix colors in retweet dialog w/ black theme 2019-03-07 19:12:35 +01:00
e937d43614 Fix broken compose drawer hooks after a recent TweetDeck update 2019-03-07 19:03:30 +01:00
20e29a7975 Release 1.17.3 2019-01-28 23:59:32 +01:00
ef815dabce Add verbose logging controlled by command line flag & update existing calls 2019-01-28 23:43:52 +01:00
1fb133e6b8 Make TweetDeck resource freezing a command line argument 2019-01-28 23:17:33 +01:00
50b58cd6a6 Add keyboard shortcut to open dev tools (Ctrl+Shift+I) 2019-01-28 18:43:55 +01:00
01485d7ef9 Add a base class for browser keyboard handling 2019-01-28 18:42:27 +01:00
b17c6a5ac7 Safeguard video player to avoid showing overlay if the URL is null 2019-01-28 17:43:53 +01:00
d2ed2b4a00 Force video player UI layout update to work around an edge case 2019-01-28 17:18:05 +01:00
710a7524a1 Kill subprocess if it doesn't exit after the app is closed 2019-01-23 15:28:05 +01:00
2be46464d6 Release 1.17.2 2018-11-23 06:19:43 +01:00
8d536a6734 Fix video player seek bar resizing when clipped & adjust min window size 2018-11-23 04:13:39 +01:00
250d502238 Add a compact layout for video player controls if the window is small
Closes #245
2018-11-23 03:03:58 +01:00
e8de7266d0 Fix cursor staying on 'resize' when moved over minimum size video player 2018-11-23 01:31:01 +01:00
9414f372d7 Fix video player size with a small window on high DPI 2018-11-23 00:45:02 +01:00
b0f9de67cf Release 1.7.1 2018-11-21 04:41:18 +01:00
9b082e114e Redirect plain twitter.com requests to TD to fix 2FA bug 2018-11-20 20:58:04 +01:00
816a5334ac Make the fix to visible scrollbar when loading TweetDeck more reliable 2018-11-20 20:56:52 +01:00
15a4e10da9 Hide the Manage Options dialog when cancelling profile import from login page 2018-11-20 20:44:44 +01:00
01b9302b0c Fix colors in introduction dialog 2018-11-20 20:13:18 +01:00
442126a11a Rewrite login/logout page CSS handling to fix broken 2FA styles
Closes #218
2018-11-20 20:07:42 +01:00
a9c140c0fc Fix video player seek bar tooltip not disappearing when cursor moves outside 2018-11-20 18:30:06 +01:00
97ad7a3e68 Fix video player bug where playback freezes for ~3s on non-primary screen 2018-11-20 18:04:34 +01:00
7d737eefb6 Fix video player's minimum size 2018-11-20 17:33:09 +01:00
4ac05b38d3 Fix column loading spinner color when using black theme 2018-11-20 15:25:26 +01:00
651bbbb672 Fix crash when showing a browser error message
Closes #244
2018-11-20 14:55:40 +01:00
52da4d8687 Release 1.17 2018-11-16 22:06:47 +01:00
36063ae76a Fix Visual Studio being stupid 2018-11-15 10:16:27 +01:00
2fcec2d2cd Update CefSharp to 67 (release) 2018-11-14 22:53:55 +01:00
762a7fdfb7 Disable compression on vendor.js as the x-ton-expected-size header isn't sometimes sent
Closes #241
2018-11-14 19:22:49 +01:00
cd7aeaeed2 Create and use a custom resource handler factory 2018-11-14 18:47:19 +01:00
6f414d312c Clear cache after each update 2018-11-10 06:20:22 +01:00
1b5304efb7 Release 1.16.3 2018-11-07 11:55:37 +01:00
d59375308f Work around clear-columns plugin reappearing after being disabled
Closes #240
2018-11-07 11:16:44 +01:00
8c9509a906 Fix broken colors of plugin elements with dark theme 2018-11-07 10:27:08 +01:00
fb86d8f3a8 Fix broken black theme colors 2018-11-07 10:18:24 +01:00
50e909cb3d Move debug TweetDeck resource freezing and update it to support CSS 2018-11-07 05:45:20 +01:00
2f54edf7e7 Fix missing <body> margin reset 2018-11-07 00:26:43 +01:00
c251603e1e Fix broken Arial font override 2018-11-07 00:26:22 +01:00
4476edb6c3 Release 1.16.2 2018-10-18 22:31:28 +02:00
28fc67660f Fix border color of large timeline cards 2018-10-18 22:30:11 +02:00
6e8b5a5ce5 Bypass t.co in new timeline cards 2018-10-18 21:26:03 +02:00
e53681416f Fix TweetDeck update breaking theme setting in edit-design plugin 2018-10-18 20:44:09 +02:00
acb5e184e8 Update styles for new timeline cards (rounded borders, black theme colors) 2018-10-18 20:36:42 +02:00
bdbafb3e5c Fix rounded borders in media badges and sensitive media overlay 2018-10-02 01:18:43 +02:00
ac70cf87c6 Fix slight misalignments in tweet composer elements when using old icons 2018-09-27 22:03:00 +02:00
93de835ab4 Fix rounded borders in composer (media elements & media description dialog) 2018-09-27 22:01:34 +02:00
66 changed files with 1042 additions and 380 deletions

View File

@@ -7,6 +7,7 @@ namespace TweetDuck.Configuration{
public const string ArgDataFolder = "-datafolder";
public const string ArgLogging = "-log";
public const string ArgIgnoreGDPR = "-nogdpr";
public const string ArgFreeze = "-freeze";
// internal args
public const string ArgRestart = "-restart";

View File

@@ -34,7 +34,7 @@ namespace TweetDuck.Configuration.Instance{
LoadInternal(attempt > 0);
if (firstException != null){ // silently log exception that caused a backup restore
Program.Reporter.Log(firstException.ToString());
Program.Reporter.LogImportant(firstException.ToString());
}
return;

View File

@@ -124,7 +124,7 @@ namespace TweetDuck.Configuration{
try{
File.Delete(file);
}catch(Exception e){
Program.Reporter.Log(e.ToString());
Program.Reporter.LogImportant(e.ToString());
return false;
}
}

View File

@@ -18,6 +18,7 @@ namespace TweetDuck.Configuration{
public Size PluginsWindowSize { get; set; } = Size.Empty;
public bool ExpandLinksOnHover { get; set; } = true;
public bool FocusDmInput { get; set; } = true;
public bool OpenSearchInFirstColumn { get; set; } = true;
public bool KeepLikeFollowDialogsOpen { get; set; } = true;
public bool BestImageQuality { get; set; } = true;

View File

@@ -17,6 +17,7 @@ namespace TweetDuck.Core.Bridge{
build.Append("x.expandLinksOnHover=").Append(Bool(config.ExpandLinksOnHover));
if (environment == Environment.Browser){
build.Append("x.focusDmInput=").Append(Bool(config.FocusDmInput));
build.Append("x.openSearchInFirstColumn=").Append(Bool(config.OpenSearchInFirstColumn));
build.Append("x.keepLikeFollowDialogsOpen=").Append(Bool(config.KeepLikeFollowDialogsOpen));
build.Append("x.muteNotifications=").Append(Bool(config.MuteNotifications));

View File

@@ -1,6 +1,7 @@
using System;
using System.Drawing;
using System.Windows.Forms;
using Microsoft.WindowsAPICodePack.Taskbar;
using TweetDuck.Configuration;
using TweetDuck.Core.Bridge;
using TweetDuck.Core.Controls;
@@ -50,6 +51,7 @@ namespace TweetDuck.Core{
private readonly FormNotificationTweet notification;
private readonly ContextMenu contextMenu;
private readonly UpdateBridge updateBridge;
private readonly TaskbarIcon taskbarIcon;
private bool isLoaded;
private FormWindowState prevState;
@@ -84,6 +86,9 @@ namespace TweetDuck.Core{
Controls.Add(new MenuStrip{ Visible = false }); // fixes Alt freezing the program in Win 10 Anniversary Update
this.taskbarIcon = new TaskbarIcon();
Shown += (sender, args) => taskbarIcon.UpdateIcon();
Disposed += (sender, args) => {
Config.MuteToggled -= Config_MuteToggled;
Config.TrayBehaviorChanged -= Config_TrayBehaviorChanged;
@@ -91,6 +96,7 @@ namespace TweetDuck.Core{
browser.Dispose();
updates.Dispose();
contextMenu.Dispose();
taskbarIcon.Dispose();
notificationScreenshotManager?.Dispose();
videoPlayer?.Dispose();
@@ -105,10 +111,6 @@ namespace TweetDuck.Core{
UpdateTray();
if (Config.MuteNotifications){
UpdateFormIcon();
}
if (Config.AllowDataCollection){
analytics = new AnalyticsManager(this, plugins, Program.AnalyticsFilePath);
}
@@ -134,10 +136,6 @@ namespace TweetDuck.Core{
isLoaded = true;
}
private void UpdateFormIcon(){ // TODO fix to show icon in taskbar too
Icon = Config.MuteNotifications ? Properties.Resources.icon_muted : Properties.Resources.icon;
}
private void UpdateTray(){
trayIcon.Visible = Config.TrayBehavior.ShouldDisplayIcon();
}
@@ -152,6 +150,7 @@ namespace TweetDuck.Core{
if (!isLoaded)return;
trayIcon.HasNotifications = false;
taskbarIcon.HasNotifications = false;
if (!browser.Enabled){ // when taking a screenshot, the window is unfocused and
browser.Enabled = true; // the browser is disabled; if the user clicks back into
@@ -213,7 +212,6 @@ namespace TweetDuck.Core{
}
private void Config_MuteToggled(object sender, EventArgs e){
UpdateFormIcon();
AnalyticsFile.NotificationMutes.Trigger();
}
@@ -494,7 +492,7 @@ namespace TweetDuck.Core{
FormManager.TryFind<FormSettings>()?.Close();
using(DialogSettingsManage dialog = new DialogSettingsManage(plugins, true)){
if (dialog.ShowDialog() == DialogResult.OK && !dialog.IsRestarting){
if (!dialog.IsDisposed && dialog.ShowDialog() == DialogResult.OK && !dialog.IsRestarting){ // needs disposal check because the dialog may be closed in constructor
BrowserProcessHandler.UpdatePrefs();
FormManager.TryFind<FormPlugins>()?.Close();
plugins.Reload(); // also reloads the browser
@@ -503,9 +501,13 @@ namespace TweetDuck.Core{
}
public void OnTweetNotification(){ // may be called multiple times, once for each type of notification
if (Config.EnableTrayHighlight && !ContainsFocus){
if (!ContainsFocus){
if (Config.EnableTrayHighlight){
trayIcon.HasNotifications = true;
}
taskbarIcon.HasNotifications = false;
}
}
public void OnTweetSound(){

View File

@@ -0,0 +1,47 @@
using System.Windows.Forms;
using CefSharp;
using TweetDuck.Core.Controls;
using TweetDuck.Core.Other;
using TweetDuck.Core.Utils;
namespace TweetDuck.Core.Handling{
class KeyboardHandlerBase : IKeyboardHandler{
protected virtual bool HandleRawKey(IWebBrowser browserControl, IBrowser browser, Keys key, CefEventFlags modifiers){
if (modifiers == (CefEventFlags.ControlDown | CefEventFlags.ShiftDown) && key == Keys.I){
if (BrowserUtils.HasDevTools){
browser.ShowDevTools();
}
else{
browserControl.AsControl().InvokeSafe(() => {
string extraMessage;
if (Program.IsPortable){
extraMessage = "Please download the portable installer, select the folder with your current installation of TweetDuck Portable, and tick 'Install dev tools' during the installation process.";
}
else{
extraMessage = "Please download the installer, and tick 'Install dev tools' during the installation process. The installer will automatically find and update your current installation of TweetDuck.";
}
FormMessage.Information("Dev Tools", "You do not have dev tools installed. "+extraMessage, FormMessage.OK);
});
}
return true;
}
return false;
}
bool IKeyboardHandler.OnPreKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey, ref bool isKeyboardShortcut){
if (type == KeyType.RawKeyDown && !browser.FocusedFrame.Url.StartsWith("chrome-devtools://")){
return HandleRawKey(browserControl, browser, (Keys)windowsKeyCode, modifiers);
}
return false;
}
bool IKeyboardHandler.OnKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey){
return false;
}
}
}

View File

@@ -2,19 +2,19 @@
using CefSharp;
namespace TweetDuck.Core.Handling{
sealed class KeyboardHandlerBrowser : IKeyboardHandler{
sealed class KeyboardHandlerBrowser : KeyboardHandlerBase{
private readonly FormBrowser form;
public KeyboardHandlerBrowser(FormBrowser form){
this.form = form;
}
bool IKeyboardHandler.OnPreKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey, ref bool isKeyboardShortcut){
return type == KeyType.RawKeyDown && form.ProcessBrowserKey((Keys)windowsKeyCode);
protected override bool HandleRawKey(IWebBrowser browserControl, IBrowser browser, Keys key, CefEventFlags modifiers){
if (base.HandleRawKey(browserControl, browser, key, modifiers)){
return true;
}
bool IKeyboardHandler.OnKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey){
return false;
return form.ProcessBrowserKey(key);
}
}
}

View File

@@ -4,7 +4,7 @@ using TweetDuck.Core.Controls;
using TweetDuck.Core.Notification;
namespace TweetDuck.Core.Handling {
sealed class KeyboardHandlerNotification : IKeyboardHandler{
sealed class KeyboardHandlerNotification : KeyboardHandlerBase{
private readonly FormNotificationBase notification;
public KeyboardHandlerNotification(FormNotificationBase notification){
@@ -15,9 +15,12 @@ namespace TweetDuck.Core.Handling {
notification.InvokeAsyncSafe(notification.AnalyticsFile.NotificationKeyboardShortcuts.Trigger);
}
bool IKeyboardHandler.OnPreKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey, ref bool isKeyboardShortcut){
if (type == KeyType.RawKeyDown && !browser.FocusedFrame.Url.StartsWith("chrome-devtools://")){
switch((Keys)windowsKeyCode){
protected override bool HandleRawKey(IWebBrowser browserControl, IBrowser browser, Keys key, CefEventFlags modifiers){
if (base.HandleRawKey(browserControl, browser, key, modifiers)){
return true;
}
switch(key){
case Keys.Enter:
notification.InvokeAsyncSafe(notification.FinishCurrentNotification);
TriggerKeyboardShortcutAnalytics();
@@ -32,14 +35,10 @@ namespace TweetDuck.Core.Handling {
notification.InvokeAsyncSafe(() => notification.FreezeTimer = !notification.FreezeTimer);
TriggerKeyboardShortcutAnalytics();
return true;
}
}
default:
return false;
}
bool IKeyboardHandler.OnKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey){
return false;
}
}
}

View File

@@ -1,4 +1,8 @@
using System.Collections.Specialized;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text.RegularExpressions;
using CefSharp;
using CefSharp.Handler;
using TweetDuck.Core.Handling.General;
@@ -6,6 +10,36 @@ using TweetDuck.Core.Utils;
namespace TweetDuck.Core.Handling{
class RequestHandlerBase : DefaultRequestHandler{
private static readonly Regex TweetDeckResourceUrl = new Regex(@"/dist/(.*?)\.(.*?)\.(css|js)$", RegexOptions.Compiled);
private static readonly SortedList<string, string> TweetDeckHashes = new SortedList<string, string>(4);
public static void LoadResourceRewriteRules(string rules){
if (string.IsNullOrEmpty(rules)){
return;
}
TweetDeckHashes.Clear();
foreach(string rule in rules.Replace(" ", "").ToLower().Split(',')){
string[] split = rule.Split('=');
if (split.Length == 2){
string key = split[0];
string hash = split[1];
if (hash.All(chr => char.IsDigit(chr) || (chr >= 'a' && chr <= 'f'))){
TweetDeckHashes.Add(key, hash);
}
else{
throw new ArgumentException("Invalid hash characters: "+rule);
}
}
else{
throw new ArgumentException("A rule must have exactly one '=' character: "+rule);
}
}
}
private readonly bool autoReload;
public RequestHandlerBase(bool autoReload){
@@ -26,6 +60,26 @@ namespace TweetDuck.Core.Handling{
return base.OnBeforeResourceLoad(browserControl, browser, frame, request, callback);
}
public override bool OnResourceResponse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response){
if ((request.ResourceType == ResourceType.Script || request.ResourceType == ResourceType.Stylesheet) && TweetDeckHashes.Count > 0){
string url = request.Url;
Match match = TweetDeckResourceUrl.Match(url);
if (match.Success && TweetDeckHashes.TryGetValue($"{match.Groups[1]}.{match.Groups[3]}", out string hash)){
if (match.Groups[2].Value == hash){
Program.Reporter.LogVerbose("[RequestHandlerBase] Accepting " + url);
}
else{
Program.Reporter.LogVerbose("[RequestHandlerBase] Replacing " + url + " hash with " + hash);
request.Url = TweetDeckResourceUrl.Replace(url, $"/dist/$1.{hash}.$3");
return true;
}
}
}
return base.OnResourceResponse(browserControl, browser, frame, request, response);
}
public override void OnRenderProcessTerminated(IWebBrowser browserControl, IBrowser browser, CefTerminationStatus status){
if (autoReload){
browser.Reload();

View File

@@ -1,27 +1,36 @@
// Uncomment to force TweetDeck to load a predefined version of the vendor/bundle scripts
// #define FREEZE_TWEETDECK_SCRIPTS
using System.Collections.Specialized;
using System.Collections.Specialized;
using CefSharp;
using TweetDuck.Core.Handling.Filters;
using TweetDuck.Core.Utils;
#if FREEZE_TWEETDECK_SCRIPTS
using System.Collections.Generic;
using System.Text.RegularExpressions;
#endif
namespace TweetDuck.Core.Handling{
sealed class RequestHandlerBrowser : RequestHandlerBase{
private const string UrlVendorResource = "/dist/vendor";
private const string UrlLoadingSpinner = "/backgrounds/spinner_blue";
public string BlockNextUserNavUrl { get; set; }
public RequestHandlerBrowser() : base(true){}
public override CefReturnValue OnBeforeResourceLoad(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IRequestCallback callback){
if (request.ResourceType == ResourceType.Script && request.Url.Contains("analytics.")){
if (request.ResourceType == ResourceType.MainFrame){
if (request.Url.EndsWith("//twitter.com/")){
request.Url = TwitterUtils.TweetDeckURL; // redirect plain twitter.com requests, fixes bugs with login 2FA
}
}
else if (request.ResourceType == ResourceType.Script){
string url = request.Url;
if (url.Contains("analytics.")){
callback.Dispose();
return CefReturnValue.Cancel;
}
else if (url.Contains(UrlVendorResource)){
NameValueCollection headers = request.Headers;
headers["Accept-Encoding"] = "identity";
request.Headers = headers;
}
}
return base.OnBeforeResourceLoad(browserControl, browser, frame, request, callback);
}
@@ -36,59 +45,19 @@ namespace TweetDuck.Core.Handling{
return base.OnBeforeBrowse(browserControl, browser, frame, request, userGesture, isRedirect);
}
#if FREEZE_TWEETDECK_SCRIPTS
private static readonly Regex TweetDeckScriptUrl = new Regex(@"/dist/(.*?)\.(.*?)\.js$", RegexOptions.Compiled);
private static readonly SortedList<string, string> TweetDeckHashes = new SortedList<string, string>(2){
{ "vendor", "942c0a20e8" },
{ "bundle", "1bd75b5854" }
};
#endif
public override bool OnResourceResponse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response){
if (request.ResourceType == ResourceType.Image && request.Url.Contains("/backgrounds/spinner_blue")){
if (request.ResourceType == ResourceType.Image && request.Url.Contains(UrlLoadingSpinner)){
request.Url = TwitterUtils.LoadingSpinner.Url;
return true;
}
#if FREEZE_TWEETDECK_SCRIPTS
else if (request.ResourceType == ResourceType.Script){
Match match = TweetDeckScriptUrl.Match(request.Url);
if (match.Success && TweetDeckHashes.TryGetValue(match.Groups[1].Value, out string hash)){
if (match.Groups[2].Value == hash){
System.Diagnostics.Debug.WriteLine($"accepting {request.Url}");
}
else{
System.Diagnostics.Debug.WriteLine($"rewriting {request.Url} to {hash}");
request.Url = TweetDeckScriptUrl.Replace(request.Url, "/dist/$1."+hash+".js");
return true;
}
}
}
#endif
return base.OnResourceResponse(browserControl, browser, frame, request, response);
}
public override IResponseFilter GetResourceResponseFilter(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response){
if (request.ResourceType == ResourceType.Script && request.Url.Contains("/dist/vendor")){
NameValueCollection headers = response.ResponseHeaders;
if (int.TryParse(headers["x-ton-expected-size"], out int totalBytes)){
if (request.ResourceType == ResourceType.Script && request.Url.Contains(UrlVendorResource) && int.TryParse(response.ResponseHeaders["Content-Length"], out int totalBytes)){
return new ResponseFilterVendor(totalBytes);
}
#if DEBUG
else{
System.Diagnostics.Debug.WriteLine($"Missing uncompressed size header in {request.Url}");
foreach(string key in headers){
System.Diagnostics.Debug.WriteLine($" {key}: {headers[key]}");
}
System.Diagnostics.Debugger.Break();
}
#endif
}
return base.GetResourceResponseFilter(browserControl, browser, frame, request, response);
}

View File

@@ -0,0 +1,43 @@
using System;
using System.Collections.Concurrent;
using CefSharp;
using TweetDuck.Data;
namespace TweetDuck.Core.Handling{
sealed class ResourceHandlerFactory : IResourceHandlerFactory{
public bool HasHandlers => !handlers.IsEmpty;
private readonly ConcurrentDictionary<string, IResourceHandler> handlers = new ConcurrentDictionary<string, IResourceHandler>(StringComparer.OrdinalIgnoreCase);
public IResourceHandler GetResourceHandler(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request){
try{
return handlers.TryGetValue(request.Url, out IResourceHandler handler) ? handler : null;
}finally{
request.Dispose();
}
}
// registration
public bool RegisterHandler(string url, IResourceHandler handler){
if (Uri.TryCreate(url, UriKind.Absolute, out Uri uri)){
handlers.AddOrUpdate(uri.AbsoluteUri, handler, (key, prev) => handler);
return true;
}
return false;
}
public bool RegisterHandler(ResourceLink link){
return RegisterHandler(link.Url, link.Handler);
}
public bool UnregisterHandler(string url){
return handlers.TryRemove(url, out IResourceHandler _);
}
public bool UnregisterHandler(ResourceLink link){
return UnregisterHandler(link.Url);
}
}
}

View File

@@ -54,6 +54,14 @@ namespace TweetDuck.Core.Management{
RefreshTimer();
}
public static void TryClearNow(){
try{
Directory.Delete(CacheFolder, true);
}catch{
// welp, too bad
}
}
public static void Exit(){
if (AutoClearTimer != null){
AutoClearTimer.Dispose();
@@ -61,11 +69,7 @@ namespace TweetDuck.Core.Management{
}
if (ClearOnExit){
try{
Directory.Delete(CacheFolder, true);
}catch{
// welp, too bad
}
TryClearNow();
}
}
}

View File

@@ -40,7 +40,7 @@ namespace TweetDuck.Core.Management{
if ((process = Process.Start(new ProcessStartInfo{
FileName = Path.Combine(Program.ProgramPath, "TweetDuck.Video.exe"),
Arguments = $"{owner.Handle} {Config.VideoPlayerVolume} \"{url}\" \"{pipe.GenerateToken()}\"",
Arguments = $"{owner.Handle} {(int)Math.Floor(100F*owner.GetDPIScale())} {Config.VideoPlayerVolume} \"{url}\" \"{pipe.GenerateToken()}\"",
UseShellExecute = false,
RedirectStandardOutput = true
})) != null){
@@ -135,7 +135,7 @@ namespace TweetDuck.Core.Management{
private void process_OutputDataReceived(object sender, DataReceivedEventArgs e){
if (!string.IsNullOrEmpty(e.Data)){
Program.Reporter.Log("[VideoPlayer] "+e.Data);
Program.Reporter.LogVerbose("[VideoPlayer] "+e.Data);
}
}

View File

@@ -121,18 +121,20 @@ namespace TweetDuck.Core.Notification{
this.owner = owner;
this.owner.FormClosed += owner_FormClosed;
ResourceHandlerFactory resourceHandlerFactory = new ResourceHandlerFactory();
resourceHandlerFactory.RegisterHandler(TwitterUtils.TweetDeckURL, this.resourceHandler);
resourceHandlerFactory.RegisterHandler(TweetNotification.AppLogo);
this.browser = new ChromiumWebBrowser("about:blank"){
MenuHandler = new ContextMenuNotification(this, enableContextMenu),
JsDialogHandler = new JavaScriptDialogHandler(),
LifeSpanHandler = new LifeSpanHandler(),
RequestHandler = new RequestHandlerBase(false)
RequestHandler = new RequestHandlerBase(false),
ResourceHandlerFactory = resourceHandlerFactory
};
this.browser.Dock = DockStyle.None;
this.browser.ClientSize = ClientSize;
this.browser.SetupResourceHandler(TwitterUtils.TweetDeckURL, this.resourceHandler);
this.browser.SetupResourceHandler(TweetNotification.AppLogo);
this.browser.SetupZoomEvents();
Controls.Add(browser);

View File

@@ -59,7 +59,7 @@ namespace TweetDuck.Core.Notification{
build.Append("<style type='text/css'>").Append(customCSS).Append("</style>");
}
build.Append("</head><body class='scroll-styled-v system-font-stack");
build.Append("</head><body class='scroll-styled-v");
if (!string.IsNullOrEmpty(bodyClasses)){
build.Append(' ').Append(bodyClasses);

View File

@@ -64,11 +64,16 @@ namespace TweetDuck.Core.Other{
Size = new Size(owner.Size.Width*3/4, owner.Size.Height*3/4);
VisibleChanged += (sender, args) => this.MoveToCenter(owner);
ResourceHandlerFactory resourceHandlerFactory = new ResourceHandlerFactory();
resourceHandlerFactory.RegisterHandler(DummyPage);
this.browser = new ChromiumWebBrowser(url){
MenuHandler = new ContextMenuGuide(owner),
JsDialogHandler = new JavaScriptDialogHandler(),
KeyboardHandler = new KeyboardHandlerBase(),
LifeSpanHandler = new LifeSpanHandler(),
RequestHandler = new RequestHandlerBase(true)
RequestHandler = new RequestHandlerBase(true),
ResourceHandlerFactory = resourceHandlerFactory
};
browser.LoadingStateChanged += browser_LoadingStateChanged;
@@ -77,8 +82,6 @@ namespace TweetDuck.Core.Other{
browser.BrowserSettings.BackgroundColor = (uint)BackColor.ToArgb();
browser.Dock = DockStyle.None;
browser.Location = ControlExtensions.InvisibleLocation;
browser.SetupResourceHandler(DummyPage);
browser.SetupZoomEvents();
Controls.Add(browser);

View File

@@ -33,6 +33,7 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
private readonly PluginManager plugins;
private readonly Dictionary<CheckBox, ProfileManager.Items> checkBoxMap = new Dictionary<CheckBox, ProfileManager.Items>(4);
private readonly bool openImportImmediately;
private State currentState;
private ProfileManager importManager;
@@ -51,6 +52,8 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
this.checkBoxMap[cbSession] = ProfileManager.Items.Session;
this.checkBoxMap[cbPluginData] = ProfileManager.Items.PluginData;
this.openImportImmediately = openImportImmediately;
if (openImportImmediately){
radioImport.Checked = true;
btnContinue_Click(null, EventArgs.Empty);
@@ -88,6 +91,10 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
Filter = "TweetDuck Profile (*.tdsettings)|*.tdsettings"
}){
if (dialog.ShowDialog() != DialogResult.OK){
if (openImportImmediately){
Close();
}
return;
}

View File

@@ -39,6 +39,7 @@
this.checkAnimatedAvatars = new System.Windows.Forms.CheckBox();
this.labelUpdates = new System.Windows.Forms.Label();
this.flowPanelLeft = new System.Windows.Forms.FlowLayoutPanel();
this.checkFocusDmInput = new System.Windows.Forms.CheckBox();
this.checkKeepLikeFollowDialogsOpen = new System.Windows.Forms.CheckBox();
this.labelTray = new System.Windows.Forms.Label();
this.comboBoxTrayType = new System.Windows.Forms.ComboBox();
@@ -82,11 +83,11 @@
//
this.checkUpdateNotifications.AutoSize = true;
this.checkUpdateNotifications.Font = new System.Drawing.Font("Segoe UI", 9F);
this.checkUpdateNotifications.Location = new System.Drawing.Point(6, 393);
this.checkUpdateNotifications.Location = new System.Drawing.Point(6, 409);
this.checkUpdateNotifications.Margin = new System.Windows.Forms.Padding(6, 6, 3, 2);
this.checkUpdateNotifications.Name = "checkUpdateNotifications";
this.checkUpdateNotifications.Size = new System.Drawing.Size(182, 19);
this.checkUpdateNotifications.TabIndex = 13;
this.checkUpdateNotifications.TabIndex = 14;
this.checkUpdateNotifications.Text = "Check Updates Automatically";
this.checkUpdateNotifications.UseVisualStyleBackColor = true;
//
@@ -94,12 +95,12 @@
//
this.btnCheckUpdates.AutoSize = true;
this.btnCheckUpdates.Font = new System.Drawing.Font("Segoe UI", 9F);
this.btnCheckUpdates.Location = new System.Drawing.Point(5, 417);
this.btnCheckUpdates.Location = new System.Drawing.Point(5, 433);
this.btnCheckUpdates.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
this.btnCheckUpdates.Name = "btnCheckUpdates";
this.btnCheckUpdates.Padding = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.btnCheckUpdates.Size = new System.Drawing.Size(128, 25);
this.btnCheckUpdates.TabIndex = 14;
this.btnCheckUpdates.TabIndex = 15;
this.btnCheckUpdates.Text = "Check Updates Now";
this.btnCheckUpdates.UseVisualStyleBackColor = true;
//
@@ -119,11 +120,11 @@
//
this.checkBestImageQuality.AutoSize = true;
this.checkBestImageQuality.Font = new System.Drawing.Font("Segoe UI", 9F);
this.checkBestImageQuality.Location = new System.Drawing.Point(6, 98);
this.checkBestImageQuality.Location = new System.Drawing.Point(6, 122);
this.checkBestImageQuality.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2);
this.checkBestImageQuality.Name = "checkBestImageQuality";
this.checkBestImageQuality.Size = new System.Drawing.Size(125, 19);
this.checkBestImageQuality.TabIndex = 4;
this.checkBestImageQuality.TabIndex = 5;
this.checkBestImageQuality.Text = "Best Image Quality";
this.checkBestImageQuality.UseVisualStyleBackColor = true;
//
@@ -131,11 +132,11 @@
//
this.checkOpenSearchInFirstColumn.AutoSize = true;
this.checkOpenSearchInFirstColumn.Font = new System.Drawing.Font("Segoe UI", 9F);
this.checkOpenSearchInFirstColumn.Location = new System.Drawing.Point(6, 50);
this.checkOpenSearchInFirstColumn.Location = new System.Drawing.Point(6, 74);
this.checkOpenSearchInFirstColumn.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2);
this.checkOpenSearchInFirstColumn.Name = "checkOpenSearchInFirstColumn";
this.checkOpenSearchInFirstColumn.Size = new System.Drawing.Size(245, 19);
this.checkOpenSearchInFirstColumn.TabIndex = 2;
this.checkOpenSearchInFirstColumn.TabIndex = 3;
this.checkOpenSearchInFirstColumn.Text = "Add Search Columns Before First Column";
this.checkOpenSearchInFirstColumn.UseVisualStyleBackColor = true;
//
@@ -158,11 +159,11 @@
//
this.labelZoom.AutoSize = true;
this.labelZoom.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold);
this.labelZoom.Location = new System.Drawing.Point(3, 155);
this.labelZoom.Location = new System.Drawing.Point(3, 179);
this.labelZoom.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
this.labelZoom.Name = "labelZoom";
this.labelZoom.Size = new System.Drawing.Size(39, 15);
this.labelZoom.TabIndex = 6;
this.labelZoom.TabIndex = 7;
this.labelZoom.Text = "Zoom";
//
// zoomUpdateTimer
@@ -185,21 +186,21 @@
//
this.panelZoom.Controls.Add(this.trackBarZoom);
this.panelZoom.Controls.Add(this.labelZoomValue);
this.panelZoom.Location = new System.Drawing.Point(0, 171);
this.panelZoom.Location = new System.Drawing.Point(0, 195);
this.panelZoom.Margin = new System.Windows.Forms.Padding(0, 1, 0, 0);
this.panelZoom.Name = "panelZoom";
this.panelZoom.Size = new System.Drawing.Size(300, 35);
this.panelZoom.TabIndex = 7;
this.panelZoom.TabIndex = 8;
//
// checkAnimatedAvatars
//
this.checkAnimatedAvatars.AutoSize = true;
this.checkAnimatedAvatars.Font = new System.Drawing.Font("Segoe UI", 9F);
this.checkAnimatedAvatars.Location = new System.Drawing.Point(6, 122);
this.checkAnimatedAvatars.Location = new System.Drawing.Point(6, 146);
this.checkAnimatedAvatars.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2);
this.checkAnimatedAvatars.Name = "checkAnimatedAvatars";
this.checkAnimatedAvatars.Size = new System.Drawing.Size(158, 19);
this.checkAnimatedAvatars.TabIndex = 5;
this.checkAnimatedAvatars.TabIndex = 6;
this.checkAnimatedAvatars.Text = "Enable Animated Avatars";
this.checkAnimatedAvatars.UseVisualStyleBackColor = true;
//
@@ -207,11 +208,11 @@
//
this.labelUpdates.AutoSize = true;
this.labelUpdates.Font = new System.Drawing.Font("Segoe UI Semibold", 10.5F, System.Drawing.FontStyle.Bold);
this.labelUpdates.Location = new System.Drawing.Point(0, 367);
this.labelUpdates.Margin = new System.Windows.Forms.Padding(0, 30, 0, 1);
this.labelUpdates.Location = new System.Drawing.Point(0, 383);
this.labelUpdates.Margin = new System.Windows.Forms.Padding(0, 27, 0, 1);
this.labelUpdates.Name = "labelUpdates";
this.labelUpdates.Size = new System.Drawing.Size(69, 19);
this.labelUpdates.TabIndex = 12;
this.labelUpdates.TabIndex = 13;
this.labelUpdates.Text = "UPDATES";
//
// flowPanelLeft
@@ -220,6 +221,7 @@
| System.Windows.Forms.AnchorStyles.Left)));
this.flowPanelLeft.Controls.Add(this.labelUI);
this.flowPanelLeft.Controls.Add(this.checkExpandLinks);
this.flowPanelLeft.Controls.Add(this.checkFocusDmInput);
this.flowPanelLeft.Controls.Add(this.checkOpenSearchInFirstColumn);
this.flowPanelLeft.Controls.Add(this.checkKeepLikeFollowDialogsOpen);
this.flowPanelLeft.Controls.Add(this.checkBestImageQuality);
@@ -240,15 +242,27 @@
this.flowPanelLeft.TabIndex = 0;
this.flowPanelLeft.WrapContents = false;
//
// checkFocusDmInput
//
this.checkFocusDmInput.AutoSize = true;
this.checkFocusDmInput.Font = new System.Drawing.Font("Segoe UI", 9F);
this.checkFocusDmInput.Location = new System.Drawing.Point(6, 50);
this.checkFocusDmInput.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2);
this.checkFocusDmInput.Name = "checkFocusDmInput";
this.checkFocusDmInput.Size = new System.Drawing.Size(282, 19);
this.checkFocusDmInput.TabIndex = 2;
this.checkFocusDmInput.Text = "Focus Input Field When Opening Direct Message";
this.checkFocusDmInput.UseVisualStyleBackColor = true;
//
// checkKeepLikeFollowDialogsOpen
//
this.checkKeepLikeFollowDialogsOpen.AutoSize = true;
this.checkKeepLikeFollowDialogsOpen.Font = new System.Drawing.Font("Segoe UI", 9F);
this.checkKeepLikeFollowDialogsOpen.Location = new System.Drawing.Point(6, 74);
this.checkKeepLikeFollowDialogsOpen.Location = new System.Drawing.Point(6, 98);
this.checkKeepLikeFollowDialogsOpen.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2);
this.checkKeepLikeFollowDialogsOpen.Name = "checkKeepLikeFollowDialogsOpen";
this.checkKeepLikeFollowDialogsOpen.Size = new System.Drawing.Size(190, 19);
this.checkKeepLikeFollowDialogsOpen.TabIndex = 3;
this.checkKeepLikeFollowDialogsOpen.TabIndex = 4;
this.checkKeepLikeFollowDialogsOpen.Text = "Keep Like/Follow Dialogs Open";
this.checkKeepLikeFollowDialogsOpen.UseVisualStyleBackColor = true;
//
@@ -256,11 +270,11 @@
//
this.labelTray.AutoSize = true;
this.labelTray.Font = new System.Drawing.Font("Segoe UI Semibold", 10.5F, System.Drawing.FontStyle.Bold);
this.labelTray.Location = new System.Drawing.Point(0, 236);
this.labelTray.Margin = new System.Windows.Forms.Padding(0, 30, 0, 1);
this.labelTray.Location = new System.Drawing.Point(0, 255);
this.labelTray.Margin = new System.Windows.Forms.Padding(0, 25, 0, 1);
this.labelTray.Name = "labelTray";
this.labelTray.Size = new System.Drawing.Size(99, 19);
this.labelTray.TabIndex = 8;
this.labelTray.TabIndex = 9;
this.labelTray.Text = "SYSTEM TRAY";
//
// comboBoxTrayType
@@ -268,32 +282,32 @@
this.comboBoxTrayType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.comboBoxTrayType.Font = new System.Drawing.Font("Segoe UI", 9F);
this.comboBoxTrayType.FormattingEnabled = true;
this.comboBoxTrayType.Location = new System.Drawing.Point(5, 260);
this.comboBoxTrayType.Location = new System.Drawing.Point(5, 279);
this.comboBoxTrayType.Margin = new System.Windows.Forms.Padding(5, 4, 3, 3);
this.comboBoxTrayType.Name = "comboBoxTrayType";
this.comboBoxTrayType.Size = new System.Drawing.Size(144, 23);
this.comboBoxTrayType.TabIndex = 9;
this.comboBoxTrayType.TabIndex = 10;
//
// labelTrayIcon
//
this.labelTrayIcon.AutoSize = true;
this.labelTrayIcon.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold);
this.labelTrayIcon.Location = new System.Drawing.Point(3, 295);
this.labelTrayIcon.Location = new System.Drawing.Point(3, 314);
this.labelTrayIcon.Margin = new System.Windows.Forms.Padding(3, 9, 3, 0);
this.labelTrayIcon.Name = "labelTrayIcon";
this.labelTrayIcon.Size = new System.Drawing.Size(56, 15);
this.labelTrayIcon.TabIndex = 10;
this.labelTrayIcon.TabIndex = 11;
this.labelTrayIcon.Text = "Tray Icon";
//
// checkTrayHighlight
//
this.checkTrayHighlight.AutoSize = true;
this.checkTrayHighlight.Font = new System.Drawing.Font("Segoe UI", 9F);
this.checkTrayHighlight.Location = new System.Drawing.Point(6, 316);
this.checkTrayHighlight.Location = new System.Drawing.Point(6, 335);
this.checkTrayHighlight.Margin = new System.Windows.Forms.Padding(6, 6, 3, 2);
this.checkTrayHighlight.Name = "checkTrayHighlight";
this.checkTrayHighlight.Size = new System.Drawing.Size(114, 19);
this.checkTrayHighlight.TabIndex = 11;
this.checkTrayHighlight.TabIndex = 12;
this.checkTrayHighlight.Text = "Enable Highlight";
this.checkTrayHighlight.UseVisualStyleBackColor = true;
//
@@ -548,5 +562,6 @@
private System.Windows.Forms.Label labelTranslationTarget;
private System.Windows.Forms.ComboBox comboBoxTranslationTarget;
private System.Windows.Forms.CheckBox checkHardwareAcceleration;
private System.Windows.Forms.CheckBox checkFocusDmInput;
}
}

View File

@@ -34,6 +34,7 @@ namespace TweetDuck.Core.Other.Settings{
// user interface
toolTip.SetToolTip(checkExpandLinks, "Expands links inside the tweets. If disabled,\r\nthe full links show up in a tooltip instead.");
toolTip.SetToolTip(checkFocusDmInput, "Places cursor into Direct Message input\r\nfield when opening a conversation.");
toolTip.SetToolTip(checkOpenSearchInFirstColumn, "By default, TweetDeck adds Search columns at the end.\r\nThis option makes them appear before the first column instead.");
toolTip.SetToolTip(checkKeepLikeFollowDialogsOpen, "Allows liking and following from multiple accounts at once,\r\ninstead of automatically closing the dialog after taking an action.");
toolTip.SetToolTip(checkBestImageQuality, "When right-clicking a tweet image, the context menu options\r\nwill use links to the original image size (:orig in the URL).");
@@ -42,6 +43,7 @@ namespace TweetDuck.Core.Other.Settings{
toolTip.SetToolTip(trackBarZoom, toolTip.GetToolTip(labelZoomValue));
checkExpandLinks.Checked = Config.ExpandLinksOnHover;
checkFocusDmInput.Checked = Config.FocusDmInput;
checkOpenSearchInFirstColumn.Checked = Config.OpenSearchInFirstColumn;
checkKeepLikeFollowDialogsOpen.Checked = Config.KeepLikeFollowDialogsOpen;
checkBestImageQuality.Checked = Config.BestImageQuality;
@@ -127,6 +129,7 @@ namespace TweetDuck.Core.Other.Settings{
public override void OnReady(){
checkExpandLinks.CheckedChanged += checkExpandLinks_CheckedChanged;
checkFocusDmInput.CheckedChanged += checkFocusDmInput_CheckedChanged;
checkOpenSearchInFirstColumn.CheckedChanged += checkOpenSearchInFirstColumn_CheckedChanged;
checkKeepLikeFollowDialogsOpen.CheckedChanged += checkKeepLikeFollowDialogsOpen_CheckedChanged;
checkBestImageQuality.CheckedChanged += checkBestImageQuality_CheckedChanged;
@@ -160,6 +163,10 @@ namespace TweetDuck.Core.Other.Settings{
Config.ExpandLinksOnHover = checkExpandLinks.Checked;
}
private void checkFocusDmInput_CheckedChanged(object sender, EventArgs e){
Config.FocusDmInput = checkFocusDmInput.Checked;
}
private void checkOpenSearchInFirstColumn_CheckedChanged(object sender, EventArgs e){
Config.OpenSearchInFirstColumn = checkOpenSearchInFirstColumn.Checked;
}

49
Core/Other/TaskbarIcon.cs Normal file
View File

@@ -0,0 +1,49 @@
using System;
using Microsoft.WindowsAPICodePack.Taskbar;
using TweetDuck.Configuration;
using Res = TweetDuck.Properties.Resources;
namespace TweetDuck.Core.Other{
sealed class TaskbarIcon : IDisposable{
private static UserConfig Config => Program.Config.User;
public bool HasNotifications{
get{
return hasNotifications;
}
set{
if (hasNotifications != value){
hasNotifications = value;
UpdateIcon();
}
}
}
private bool hasNotifications;
public TaskbarIcon(){
Config.MuteToggled += Config_MuteToggled;
}
public void Dispose(){
Config.MuteToggled -= Config_MuteToggled;
}
private void Config_MuteToggled(object sender, EventArgs e){
UpdateIcon();
}
public void UpdateIcon(){
if (hasNotifications){
TaskbarManager.Instance.SetOverlayIcon(Res.overlay_notification, "Unread Notifications");
}
else if (Config.MuteNotifications){
TaskbarManager.Instance.SetOverlayIcon(Res.overlay_muted, "Notifications Muted");
}
else{
TaskbarManager.Instance.SetOverlayIcon(null, string.Empty);
}
}
}
}

View File

@@ -19,6 +19,9 @@ namespace TweetDuck.Core{
sealed class TweetDeckBrowser : IDisposable{
private static UserConfig Config => Program.Config.User;
private const string ErrorUrl = "http://td/error";
private const string TwitterStyleUrl = "https://abs.twimg.com/tduck/css";
public bool Ready { get; private set; }
public bool Enabled{
@@ -39,10 +42,14 @@ namespace TweetDuck.Core{
}
private readonly ChromiumWebBrowser browser;
private readonly ResourceHandlerFactory resourceHandlerFactory = new ResourceHandlerFactory();
private string prevSoundNotificationPath = null;
public TweetDeckBrowser(FormBrowser owner, PluginManager plugins, TweetDeckBridge tdBridge, UpdateBridge updateBridge){
resourceHandlerFactory.RegisterHandler(TweetNotification.AppLogo);
resourceHandlerFactory.RegisterHandler(TwitterUtils.LoadingSpinner);
RequestHandlerBrowser requestHandler = new RequestHandlerBrowser();
this.browser = new ChromiumWebBrowser(TwitterUtils.TweetDeckURL){
@@ -52,7 +59,8 @@ namespace TweetDuck.Core{
JsDialogHandler = new JavaScriptDialogHandler(),
KeyboardHandler = new KeyboardHandlerBrowser(owner),
LifeSpanHandler = new LifeSpanHandler(),
RequestHandler = requestHandler
RequestHandler = requestHandler,
ResourceHandlerFactory = resourceHandlerFactory
};
this.browser.LoadingStateChanged += browser_LoadingStateChanged;
@@ -66,9 +74,6 @@ namespace TweetDuck.Core{
this.browser.BrowserSettings.BackgroundColor = (uint)TwitterUtils.BackgroundColor.ToArgb();
this.browser.Dock = DockStyle.None;
this.browser.Location = ControlExtensions.InvisibleLocation;
this.browser.SetupResourceHandler(TweetNotification.AppLogo);
this.browser.SetupResourceHandler(TwitterUtils.LoadingSpinner);
this.browser.SetupZoomEvents();
owner.Controls.Add(browser);
@@ -117,12 +122,17 @@ namespace TweetDuck.Core{
if (frame.IsMain){
if (TwitterUtils.IsTwitterWebsite(frame)){
string css = ScriptLoader.LoadResource("styles/twitter.css", browser);
resourceHandlerFactory.RegisterHandler(TwitterStyleUrl, ResourceHandler.FromString(css, mimeType: "text/css"));
ScriptLoader.ExecuteFile(frame, "twitter.js", browser);
}
if (!TwitterUtils.IsTwitterLogin2FactorWebsite(frame)){
frame.ExecuteJavaScriptAsync(TwitterUtils.BackgroundColorOverride);
}
}
}
private void browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
IFrame frame = e.Frame;
@@ -149,6 +159,10 @@ namespace TweetDuck.Core{
ScriptLoader.ExecuteFile(frame, "update.js", browser);
}
if (frame.Url == ErrorUrl){
resourceHandlerFactory.UnregisterHandler(ErrorUrl);
}
}
private void browser_LoadError(object sender, LoadErrorEventArgs e){
@@ -160,7 +174,8 @@ namespace TweetDuck.Core{
string errorPage = ScriptLoader.LoadResourceSilent("pages/error.html");
if (errorPage != null){
browser.LoadHtml(errorPage.Replace("{err}", BrowserUtils.GetErrorName(e.ErrorCode)), "http://td/error");
resourceHandlerFactory.RegisterHandler(ErrorUrl, ResourceHandler.FromString(errorPage.Replace("{err}", BrowserUtils.GetErrorName(e.ErrorCode))));
browser.Load(ErrorUrl);
}
}
}
@@ -176,8 +191,14 @@ namespace TweetDuck.Core{
string newNotificationPath = Config.NotificationSoundPath;
if (prevSoundNotificationPath != newNotificationPath){
browser.SetupResourceHandler(soundUrl, hasCustomSound ? SoundNotification.CreateFileHandler(newNotificationPath) : null);
prevSoundNotificationPath = newNotificationPath;
if (hasCustomSound){
resourceHandlerFactory.RegisterHandler(soundUrl, SoundNotification.CreateFileHandler(newNotificationPath));
}
else{
resourceHandlerFactory.UnregisterHandler(soundUrl);
}
}
browser.ExecuteScriptAsync("TDGF_setSoundNotificationData", hasCustomSound, Config.NotificationSoundVolume);

View File

@@ -8,7 +8,6 @@ using System.Windows.Forms;
using CefSharp.WinForms;
using TweetDuck.Configuration;
using TweetDuck.Core.Other;
using TweetDuck.Data;
namespace TweetDuck.Core.Utils{
static class BrowserUtils{
@@ -60,21 +59,6 @@ namespace TweetDuck.Core.Utils{
return (ChromiumWebBrowser)browserControl;
}
public static void SetupResourceHandler(this ChromiumWebBrowser browser, string url, IResourceHandler handler){
DefaultResourceHandlerFactory factory = (DefaultResourceHandlerFactory)browser.ResourceHandlerFactory;
if (handler == null){
factory.UnregisterHandler(url);
}
else{
factory.RegisterHandler(url, handler);
}
}
public static void SetupResourceHandler(this ChromiumWebBrowser browser, ResourceLink resource){
browser.SetupResourceHandler(resource.Url, resource.Handler);
}
public static void SetupZoomEvents(this ChromiumWebBrowser browser){
void UpdateZoomLevel(object sender, EventArgs args){
SetZoomLevel(browser.GetBrowser(), Config.ZoomLevel);

View File

@@ -30,11 +30,19 @@ namespace TweetDuck.Core.Utils{
public sealed class Item : IComparable<Item>{
public string Code { get; }
public CultureInfo Info { get; }
private string Name => info?.NativeName ?? Code;
private readonly CultureInfo info;
public Item(string code, string alt = null){
this.Code = code;
this.Info = CultureInfo.GetCultureInfo(alt ?? code);
try{
this.info = CultureInfo.GetCultureInfo(alt ?? code);
}catch(CultureNotFoundException){
// ignore
}
}
public override bool Equals(object obj){
@@ -46,12 +54,16 @@ namespace TweetDuck.Core.Utils{
}
public override string ToString(){
string capitalizedName = Info.TextInfo.ToTitleCase(Info.NativeName);
return Info.DisplayName == Info.NativeName ? capitalizedName : $"{capitalizedName}, {Info.DisplayName}";
if (info == null){
return Code;
}
string capitalizedName = info.TextInfo.ToTitleCase(info.NativeName);
return info.DisplayName == info.NativeName ? capitalizedName : $"{capitalizedName}, {info.DisplayName}";
}
public int CompareTo(Item other){
return string.Compare(Info.NativeName, other.Info.NativeName, false, CultureInfo.InvariantCulture);
return string.Compare(Name, other.Name, false, CultureInfo.InvariantCulture);
}
}
}

View File

@@ -16,7 +16,7 @@ namespace TweetDuck.Core.Utils{
public const string TweetDeckURL = "https://tweetdeck.twitter.com";
public static readonly Color BackgroundColor = Color.FromArgb(28, 99, 153);
public const string BackgroundColorOverride = "setTimeout(function f(){let h=document.head;if(!h){setTimeout(f,5);return;}let e=document.createElement('style');e.innerHTML='body,body::before{background:#1c6399!important}';h.appendChild(e);},1)";
public const string BackgroundColorOverride = "setTimeout(function f(){let h=document.head;if(!h){setTimeout(f,5);return;}let e=document.createElement('style');e.innerHTML='body,body::before{background:#1c6399!important;margin:0}';h.appendChild(e);},1)";
public static readonly ResourceLink LoadingSpinner = new ResourceLink("https://ton.twimg.com/tduck/spinner", ResourceHandler.FromByteArray(Properties.Resources.spinner, "image/apng"));
@@ -43,6 +43,10 @@ namespace TweetDuck.Core.Utils{
return frame.Url.Contains("//twitter.com/");
}
public static bool IsTwitterLogin2FactorWebsite(IFrame frame){
return frame.Url.Contains("//twitter.com/account/login_verification");
}
private static string ExtractMediaBaseLink(string url){
int slash = url.LastIndexOf('/');
return slash == -1 ? url : StringUtils.ExtractBefore(url, ':', slash);

View File

@@ -9,6 +9,7 @@ using System.Threading;
using System.Windows.Forms;
using TweetDuck.Configuration;
using TweetDuck.Core;
using TweetDuck.Core.Handling;
using TweetDuck.Core.Handling.General;
using TweetDuck.Core.Other;
using TweetDuck.Core.Management;
@@ -20,7 +21,7 @@ namespace TweetDuck{
public const string BrandName = "TweetDuck";
public const string Website = "https://tweetduck.chylex.com";
public const string VersionTag = "1.16.1";
public const string VersionTag = "1.17.4";
public static readonly string ProgramPath = AppDomain.CurrentDomain.BaseDirectory;
public static readonly bool IsPortable = File.Exists(Path.Combine(ProgramPath, "makeportable"));
@@ -126,6 +127,14 @@ namespace TweetDuck{
if (Arguments.HasFlag(Arguments.ArgUpdated)){
WindowsUtils.TryDeleteFolderWhenAble(InstallerPath, 8000);
BrowserCache.TryClearNow();
}
try{
RequestHandlerBase.LoadResourceRewriteRules(Arguments.GetValue(Arguments.ArgFreeze, null));
}catch(Exception e){
FormMessage.Error("Resource Freeze", "Error parsing resource rewrite rules: "+e.Message, FormMessage.OK);
return;
}
BrowserCache.RefreshTimer();

View File

@@ -19,7 +19,7 @@ namespace TweetDuck.Properties {
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
@@ -120,6 +120,26 @@ namespace TweetDuck.Properties {
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
/// </summary>
internal static System.Drawing.Icon overlay_muted {
get {
object obj = ResourceManager.GetObject("overlay_muted", resourceCulture);
return ((System.Drawing.Icon)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
/// </summary>
internal static System.Drawing.Icon overlay_notification {
get {
object obj = ResourceManager.GetObject("overlay_notification", resourceCulture);
return ((System.Drawing.Icon)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>

View File

@@ -136,6 +136,12 @@
<data name="icon_tray_new" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Images\icon-tray-new.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="overlay_muted" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Images\overlay-muted.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="overlay_notification" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Images\overlay-notification.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="spinner" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Images\spinner.apng;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>

View File

@@ -6,20 +6,20 @@
### Setup
The program was built using Visual Studio 2017. Before opening the solution, please make sure you have the following workloads and components installed (optional components that are not listed can be deselected to save space):
The program can be built using Visual Studio 2017 or newer. Before opening the solution, please make sure you have the following workloads and components installed (optional components that are not listed can be deselected to save space):
* **.NET desktop development**
* .NET Framework 4 4.6 development tools
* F# desktop language support
* **Desktop development with C++**
* VC++ 2017 latest v141 tools
* *(VS 2017)* VC++ 2017 version 15.9 v14.16 latest v141 tools
* *(VS 2019)* MSVC v142 - VS 2019 C++ x64/x86 build tools (v14.20)
After opening the solution, right-click the solution and select **Restore NuGet Packages**, or manually run this command in the **Package Manager Console**:
```
PM> Install-Package CefSharp.WinForms -Version 67.0.0-pre01
PM> Install-Package CefSharp.WinForms -Version 67.0.0
PM> Install-Package WindowsAPICodePack-Shell -Version 1.1.1
```
Note that some pre-release builds of CefSharp are not available on NuGet. To correctly restore packages in that case, open **Package Manager Settings**, and add `https://www.myget.org/F/cefsharp/api/v3/index.json` to the list of package sources.
### Debug
The `Debug` configuration uses a separate data folder by default (`%LOCALAPPDATA%\TweetDuckDebug`) to avoid affecting an existing installation of TweetDuck. You can modify this by opening **TweetDuck Properties** in Visual Studio, clicking the **Debug** tab, and changing the **Command line arguments** field.
@@ -45,7 +45,7 @@ If you decide to publicly release a custom version, please make it clear that it
- `Resources/Plugins/emoji-keyboard/emoji-ordering.txt` line endings must be LF (line feed); any CR (carriage return) in the file will cause a failed build, and you will need to ensure correct line endings in your text editor
#### Error: The "EmbedAllSources" parameter is not supported by the "Csc" task
1. Open `C:\Program Files (x86)\Visual Studio\2017\<edition>\MSBuild\15.0\Bin\Microsoft.CSharp.CurrentVersion.targets` in a text editor
1. Open `C:\Program Files (x86)\Visual Studio\2017\<edition>\MSBuild\15.0\Bin\Roslyn\Microsoft.CSharp.Core.targets` in a text editor
2. Remove line that says `EmbedAllSources="$(EmbedAllSources)"`
3. Hope the next Visual Studio update fixes it...

View File

@@ -4,6 +4,7 @@ using System.Drawing;
using System.IO;
using System.Text;
using System.Windows.Forms;
using TweetDuck.Configuration;
using TweetDuck.Core.Other;
namespace TweetDuck{
@@ -22,7 +23,11 @@ namespace TweetDuck{
};
}
public bool Log(string data){
public bool LogVerbose(string data){
return Arguments.HasFlag(Arguments.ArgLogging) && LogImportant(data);
}
public bool LogImportant(string data){
#if DEBUG
Debug.WriteLine(data);
#endif
@@ -45,7 +50,7 @@ namespace TweetDuck{
}
public void HandleException(string caption, string message, bool canIgnore, Exception e){
bool loggedSuccessfully = Log(e.ToString());
bool loggedSuccessfully = LogImportant(e.ToString());
string exceptionText = e is ExpandedLogException ? e.Message+"\n\nDetails with potentially sensitive information are in the Error Log." : e.Message;
FormMessage form = new FormMessage(caption, message+"\nError: "+exceptionText, canIgnore ? MessageBoxIcon.Warning : MessageBoxIcon.Error);

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

@@ -87,9 +87,9 @@ enabled(){
// update UI
this.btnClearAllHTML = `
<a class="clear-columns-btn-all-parent js-header-action link-clean cf app-nav-link padding-h--10" data-title="Clear columns (hold Shift to restore)" data-action="td-clearcolumns-doall">
<a class="clear-columns-btn-all-parent js-header-action link-clean cf app-nav-link padding-h--16 padding-v--2" data-title="Clear columns (hold Shift to restore)" data-action="td-clearcolumns-doall">
<div class="obj-left margin-l--2"><i class="icon icon-medium icon-clear-timeline"></i></div>
<div class="clear-columns-btn-all nbfc padding-ts hide-condensed txt-size--16 app-nav-link-text">Clear columns</div>
<div class="clear-columns-btn-all nbfc padding-ts hide-condensed txt-size--14 app-nav-link-text">Clear columns</div>
</a>`;
this.btnClearOneHTML = `
@@ -110,18 +110,28 @@ enabled(){
// styles
if (!document.getElementById("td-clearcolumns-workaround")){
// TD started caching mustaches so disabling the plugin doesn't update the column headers properly...
let workaround = document.createElement("style");
workaround.id = "td-clearcolumns-workaround";
workaround.innerText = "#tduck a[data-action='td-clearcolumns-dosingle'] { display: none }";
document.head.appendChild(workaround);
}
this.css = window.TDPF_createCustomStyle(this);
this.css.insert(".js-app-add-column.is-hidden + .clear-columns-btn-all-parent { display: none; }");
this.css.insert(".column-navigator-overflow .clear-columns-btn-all-parent { display: none !important; }");
this.css.insert(".column-navigator-overflow { bottom: 224px !important; }");
this.css.insert(".app-navigator .clear-columns-btn-all-parent { font-weight: 700; }");
this.css.insert(".column-header-links { min-width: 51px !important; }");
this.css.insert(".column[data-td-icon='icon-message'] .column-header-links { min-width: 110px !important; }");
this.css.insert(".btn-options-tray[data-action='clear'] { display: none !important; }");
this.css.insert(".column[data-td-icon='icon-schedule'] a[data-action='td-clearcolumns-dosingle'] { display: none; }");
this.css.insert(".column[data-td-icon='icon-custom-timeline'] a[data-action='td-clearcolumns-dosingle'] { display: none; }");
this.css.insert("#tduck a[data-action='td-clearcolumns-dosingle'] { display: inline-block; }");
this.css.insert("#tduck .column[data-td-icon='icon-schedule'] a[data-action='td-clearcolumns-dosingle'] { display: none; }");
this.css.insert("#tduck .column[data-td-icon='icon-custom-timeline'] a[data-action='td-clearcolumns-dosingle'] { display: none; }");
}
ready(){

View File

@@ -255,7 +255,7 @@ enabled(){
setTimeout(function(){
if (theme != TD.settings.getTheme()){
$(document).trigger("uiToggleTheme");
TD.settings.setTheme(theme);
}
me.saveConfig();
@@ -432,7 +432,8 @@ enabled(){
}
if (this.config.forceArialFont){
this.css.insert("#tduck .system-font-stack { font-family: Arial, sans-serif; font-weight: 400 }");
this.css.insert("#tduck { font-family: Arial, sans-serif; font-weight: 400 }");
this.css.insert("#tduck input, #tduck label, #tduck select, #tduck textarea { font-family: Arial }")
}
if (this.config.increaseQuoteTextSize){
@@ -541,11 +542,13 @@ ${iconData.map(entry => `#tduck .icon-${entry[0]}:before{content:\"\\f0${entry[1
.drawer .btn .icon, .app-header .btn .icon { line-height: 1em !important }
.app-search-fake .icon { margin-top: -3px !important }
#tduck .js-docked-compose .js-drawer-close { margin: 20px 0 0 !important }
#tduck .search-input-control .icon { font-size: 20px !important; top: -4px !important }
#tduck .js-docked-compose .js-drawer-close { margin: 20px 0 0 !important }
#tduck .compose-media-bar-remove .icon-close, #tduck .compose-media-grid-remove .icon-close { padding: 3px 2px 1px !important }
.js-column-header .column-type-icon { margin-top: 0 !important }
.inline-reply .pull-left .Button--link { margin-top: 3px !important }
.js-inline-compose-pop .icon-popout { font-size: 23px !important }
.tweet-action-item .icon-favorite-toggle { font-size: 16px !important; }
.tweet-action-item .heartsprite { top: -260% !important; left: -260% !important; transform: scale(0.4, 0.39) translateY(0.5px) !important; }
@@ -601,7 +604,7 @@ html[data-td-font] { font-size: ${this.config.fontSize} !important }
.avatar { border-radius: ${this.config.avatarRadius}% !important }
${this.config.forceArialFont ? `
#tduck .system-font-stack { font-family: Arial, sans-serif; font-weight: 400 }
#tduck { font-family: Arial, sans-serif; font-weight: 400 }
` : ``}
${this.config.increaseQuoteTextSize ? `

View File

@@ -173,12 +173,23 @@
#edit-design-panel {
width: 693px;
height: 380px;
background-color: #FFF;
box-shadow: 0 0 10px rgba(17, 17, 17, 0.5);
}
#edit-design-panel .mdl-header {
color: #8899A6;
}
#edit-design-panel .mdl-inner {
padding-top: 0;
}
#edit-design-panel .mdl-content {
border: 1px solid #CCD6DD;
background: #EAEAEA;
}
#edit-design-panel-inner-cols {
padding: 0 6px;
}

View File

@@ -117,7 +117,7 @@ html.dark .account-settings-bt{border-top:1px solid #e1e8ed}
html.dark .account-settings-bb{border-bottom:1px solid #e1e8ed}
html.dark .account-stats a{color:#66757f}
html.dark .account-stats a:hover{color:#2b7bb9}
html.dark .column{background-color:#222426}
html.dark .column-panel{background-color:#222426}
html.dark .column.is-focused{box-shadow:0 0 0 6px #7aa2c0}
html.dark .column-background-fill{background-color:#F5F8FA}
html.dark .more-tweets-glow{background-color:#55acee;background:radial-gradient(ellipse farthest-corner at 50% 100%,#55acee 0%,#55acee 25%,rgba(255,255,255,0) 75%)}
@@ -135,7 +135,6 @@ html.dark .column-header{background-color:#292F33}
html.dark .is-inverted-dark .column-header{border-bottom:1px solid #ddd}
html.dark .is-inverted-dark .column-title-edit-box{color:#111;background-color:#fff;border-color:#e1e8ed}
html.dark .column-header{border-bottom:1px solid #222426}
html.dark .column-header-temp{border-bottom:1px solid #ddd}
html.dark .column-title-edit-box{color:#e1e8ed;background-color:#14171A;border-color:#111}
html.dark .column-number{color:#66757f}
html.dark .is-new .column-type-icon{color:#55acee}
@@ -428,8 +427,7 @@ html.dark .list-account .username{color:#8899a6}
html.dark .list-listmember .username{color:#8899a6}
html.dark .list-listmember .bio{color:#657786}
html.dark .divider-bar{background-color:#ddd}
html.dark select{background-color:#fff}
html.dark input,html.dark textarea,html.dark select{color:#111;border:1px solid #e1e8ed}
html.dark input,html.dark textarea,html.dark select{color:#111;border:1px solid #e1e8ed;background-color:#fff}
html.dark input:disabled{background-color:#eaeaea;border-color:#e1e8ed}
html.dark select:disabled{background-color:#f5f8fa}
html.dark input:focus,html.dark select:focus,html.dark textarea:focus,html.dark .focus{border-color:rgba(80,165,230,0.8);box-shadow:inset 0 1px 3px rgba(17,17,17,0.1),0 0 8px rgba(80,165,230,0.6)}
@@ -802,5 +800,12 @@ html.dark .NotificationList .Notification-body{color:#14171A}
html.dark .DrawerModal{color:#14171A}
/* fixes */
html.dark .app-search-fake{border-color:transparent}
html.dark .spinner-small,html.dark .spinner-large{filter:grayscale(80%)brightness(93%)}
html.dark .spinner-small,html.dark .spinner-large{filter:grayscale(85%)brightness(117%)}
html.dark .tweet>.color-twitter-blue{color:#8bd!important}
html.dark .hw-card-container>div{border-color:#292F33;background:transparent}
html.dark .hw-card-container>div>div{border-color:#292F33}
html.dark .modal-content,html.dark .lst-group,html.dark #actions-modal{color:#292F33}
html.dark #actions-modal .account-link{color:#38444d}
html.dark .mdl-accent{background-color:transparent}
html.dark .lst-launcher a span{color:#657786!important}
html.dark .social-proof-names a{color:#3b94d9}

View File

@@ -56,11 +56,13 @@ enabled(){
this.prevComposeMustache = TD.mustaches["compose/docked_compose.mustache"];
window.TDPF_injectMustache("compose/docked_compose.mustache", "append", '<div class="cf margin-t--12 margin-b--30">', buttonHTML);
let maybeDockedComposePanel = $(".js-docked-compose");
this.getDrawerInput = () => {
return $(".js-compose-text", me.composeDrawer);
};
if (maybeDockedComposePanel.length){
maybeDockedComposePanel.find(".cf.margin-t--12.margin-b--30").first().append(buttonHTML);
}
this.getDrawerScroller = () => {
return $(".js-compose-scroller > .scroll-v", me.composeDrawer);
};
// keyboard generation
@@ -79,12 +81,12 @@ enabled(){
this.currentKeywords = [];
this.composePanelScroller.trigger("scroll");
this.getDrawerScroller().trigger("scroll");
$(".emoji-keyboard-popup-btn").removeClass("is-selected");
if (refocus){
this.composeInput.focus();
this.getDrawerInput().focus();
if (lastEmojiKeyword && lastEmojiPosition === 0){
document.execCommand("insertText", false, lastEmojiKeyword);
@@ -229,16 +231,16 @@ enabled(){
this.currentSpanner.style.height = ($(this.currentKeyboard).height()-10)+"px";
$(".emoji-keyboard-popup-btn").parent().after(this.currentSpanner);
this.composePanelScroller.trigger("scroll");
this.getDrawerScroller().trigger("scroll");
};
const getKeyboardTop = () => {
let button = $(".emoji-keyboard-popup-btn");
return button.offset().top+button.outerHeight()+me.composePanelScroller.scrollTop()+8;
return button.offset().top + button.outerHeight() + me.getDrawerScroller().scrollTop() + 8;
};
const insertEmoji = (src, alt) => {
let input = this.composeInput;
let input = this.getDrawerInput();
let val = input.val();
let posStart = input[0].selectionStart;
@@ -378,6 +380,15 @@ enabled(){
hideKeyboard();
};
this.drawerToggleEvent = function(e, data){
if (data.activeDrawer === "compose"){
setTimeout(function(){
$(".emoji-keyboard-popup-btn", me.composeDrawer).on("click", me.emojiKeyboardButtonClickEvent);
$(".js-docked-compose .js-compose-scroller > .scroll-v", me.composeDrawer).on("scroll", me.composerScrollEvent);
}, 0);
}
};
this.documentClickEvent = function(e){
if (me.currentKeyboard && $(e.target).closest(".compose-text-container").length === 0){
hideKeyboard();
@@ -396,20 +407,26 @@ enabled(){
me.currentKeyboard.style.top = getKeyboardTop()+"px";
}
};
// re-enabling
let maybeDockedComposePanel = $(".js-docked-compose");
if (maybeDockedComposePanel.length){
maybeDockedComposePanel.find(".cf.margin-t--12.margin-b--30").first().append(buttonHTML);
this.drawerToggleEvent({}, { activeDrawer: "compose" });
}
}
ready(){
this.composeDrawer = $("[data-drawer='compose']");
this.composeInput = $(".js-compose-text", ".js-docked-compose");
this.composeDrawer = $(".js-drawer[data-drawer='compose']");
this.composeSelector = ".js-compose-text,.js-reply-tweetbox";
this.composePanelScroller = $(".js-compose-scroller", ".js-docked-compose").first().children().first();
this.composePanelScroller.on("scroll", this.composerScrollEvent);
$(".emoji-keyboard-popup-btn").on("click", this.emojiKeyboardButtonClickEvent);
$(document).on("click", this.documentClickEvent);
$(document).on("keydown", this.documentKeyEvent);
$(document).on("uiDrawerActive", this.drawerToggleEvent);
$(document).on("uiComposeImageAdded", this.uploadFilesEvent);
this.composeDrawer.on("uiComposeTweetSending", this.composerSendingEvent);
$(document).on("keydown", this.composeSelector, this.composeInputKeyDownEvent);
@@ -529,14 +546,13 @@ disabled(){
$(this.currentSpanner).remove();
}
this.composePanelScroller.off("scroll", this.composerScrollEvent);
$(".emoji-keyboard-popup-btn").off("click", this.emojiKeyboardButtonClickEvent);
$(".emoji-keyboard-popup-btn").remove();
$(document).off("click", this.documentClickEvent);
$(document).off("keydown", this.documentKeyEvent);
$(document).off("uiDrawerActive", this.drawerToggleEvent);
$(document).off("uiComposeImageAdded", this.uploadFilesEvent);
this.composeDrawer.off("uiComposeTweetSending", this.composerSendingEvent);
$(document).off("keydown", this.composeSelector, this.composeInputKeyDownEvent);

View File

@@ -383,7 +383,7 @@ enabled(){
}
ready(){
$(".manage-templates-btn").on("click", this.manageTemplatesButtonClickEvent);
$(".js-drawer[data-drawer='compose']").on("click", ".manage-templates-btn", this.manageTemplatesButtonClickEvent);
$(document).on("uiDrawerActive", this.drawerToggleEvent);
}
@@ -391,6 +391,7 @@ disabled(){
$(".manage-templates-btn").remove();
$("#templates-modal-wrap").remove();
$(".js-drawer[data-drawer='compose']").off("click", ".manage-templates-btn", this.manageTemplatesButtonClickEvent);
$(document).off("uiDrawerActive", this.drawerToggleEvent);
TD.mustaches["compose/docked_compose.mustache"] = this.prevComposeMustache;

View File

@@ -171,14 +171,14 @@
}
.template-editor-form input, .template-editor-form textarea {
color: #111;
background-color: #fff;
color: #111 !important;
background-color: #fff !important;
border: none;
border-radius: 0;
}
.template-editor-form input:focus, .template-editor-form textarea:focus {
box-shadow: inset 0 1px 3px rgba(17, 17, 17, 0.1), 0 0 8px rgba(80, 165, 230, 0.6);
box-shadow: inset 0 1px 3px rgba(17, 17, 17, 0.1), 0 0 8px rgba(80, 165, 230, 0.6) !important;
}
.template-editor-form textarea {

View File

@@ -245,6 +245,7 @@
if (column.model.getHasNotification()){
let sensitive = isSensitive(tweet);
let previews = $TDX.notificationMediaPreviews && (!sensitive || TD.settings.getDisplaySensitiveMedia());
// TODO new cards don't have either previews or links
let html = $(tweet.render({
withFooter: false,
@@ -413,7 +414,7 @@
let fontSizeName = TD.settings.getFontSize();
let themeName = TD.settings.getTheme();
let columnBackground = getClassStyleProperty("column", "background-color");
let columnBackground = getClassStyleProperty("column-panel", "background-color");
let tags = [
"<html "+Array.prototype.map.call(document.documentElement.attributes, ele => `${ele.name}="${ele.value}"`).join(" ")+"><head>"
@@ -574,6 +575,31 @@
data.setData("text/html", `<a href="${url}">${url}</a>`);
});
if (ensurePropertyExists(TD, "services", "TwitterStatus", "prototype", "_generateHTMLText")){
TD.services.TwitterStatus.prototype._generateHTMLText = prependToFunction(TD.services.TwitterStatus.prototype._generateHTMLText, function(){
let card = this.card;
let entities = this.entities;
return if !(card && entities);
let urls = entities.urls;
return if !(urls && urls.length);
let shortUrl = card.url;
let urlObj = entities.urls.find(obj => obj.url === shortUrl && obj.expanded_url);
if (urlObj){
let expandedUrl = urlObj.expanded_url;
card.url = expandedUrl;
let values = card.binding_values;
if (values && values.card_url){
values.card_url.string_value = expandedUrl;
}
}
});
}
if (ensurePropertyExists(TD, "services", "TwitterMedia", "prototype", "fromMediaEntity")){
const prevFunc = TD.services.TwitterMedia.prototype.fromMediaEntity;
@@ -1013,14 +1039,20 @@
// Block: Refocus the textbox after switching accounts.
//
onAppReady.push(function setupAccountSwitchRefocus(){
const composeInput = $$(".js-compose-text", ".js-docked-compose");
const refocusInput = function(){
composeInput.focus();
$$(".js-compose-text", ".js-docked-compose").focus();
};
$$(".js-account-list", ".js-docked-compose").delegate(".js-account-item", "click", function(e){
const accountItemClickEvent = function(e){
setTimeout(refocusInput, 0);
};
$(document).on("uiDrawerActive", function(e, data){
if (data.activeDrawer === "compose"){
setTimeout(function(){
$$(".js-account-list", ".js-docked-compose").delegate(".js-account-item", "click", accountItemClickEvent);
}, 0);
}
});
});
@@ -1172,6 +1204,8 @@
//
execSafe(function setupVideoPlayer(){
window.TDGF_playVideo = function(url, username){
return if !url;
$('<div id="td-video-player-overlay" class="ovl" style="display:block"></div>').on("click contextmenu", function(){
$TD.playVideo(null, null);
}).appendTo(app);
@@ -1477,6 +1511,8 @@
//
if (ensurePropertyExists(TD, "components", "ConversationDetailView", "prototype", "showChirp")){
TD.components.ConversationDetailView.prototype.showChirp = appendToFunction(TD.components.ConversationDetailView.prototype.showChirp, function(){
return if !$TDX.focusDmInput;
setTimeout(function(){
$(".js-reply-tweetbox").first().focus();
}, 100);

View File

@@ -7,19 +7,30 @@
min-width: 515px;
max-width: 835px;
height: 328px;
background-color: #fff;
}
#td-introduction-modal .mdl-inner {
padding-top: 0;
#td-introduction-modal .mdl-header {
color: #8899a6;
}
#td-introduction-modal .mdl-header-title {
cursor: default;
}
#td-introduction-modal .mdl-dismiss {
color: #292f33;
}
#td-introduction-modal .mdl-inner {
padding-top: 0;
}
#td-introduction-modal .mdl-content {
padding: 4px 16px 0;
overflow-y: auto;
border-color: #ccd6dd;
background: #eaeaea;
}
#td-introduction-modal p {

View File

@@ -1,38 +0,0 @@
/*****************************/
/* Fix min width and margins */
/*****************************/
.page-canvas {
width: auto !important;
max-width: 888px;
}
.signout-wrapper {
width: auto !important;
margin: 0 auto !important;
}
.signout {
margin: 60px 0 54px !important;
}
.buttons {
padding-bottom: 0 !important;
}
/*******************/
/* General styling */
/*******************/
.aside {
/* hide elements around dialog */
display: none;
}
.buttons button, .buttons a {
/* style buttons */
display: inline-block;
margin: 0 4px !important;
border: 1px solid rgba(0, 0, 0, 0.3) !important;
border-radius: 0 !important;
}

View File

@@ -38,19 +38,23 @@
/* Square-ify stuff */
/********************/
button, .btn, .mdl, .mdl-content, .popover, .lst-modal, .tooltip-inner {
#tduck .compose-media-bar-remove .icon-close, #tduck .compose-media-grid-remove .icon-close {
border-radius: 2px !important;
}
button, .btn, .mdl, .mdl-content, .modal-content, .popover, .lst-modal, .tooltip-inner {
border-radius: 1px !important;
}
.media-item, .media-preview, .media-image, .js-media-added .br--4, #tduck .compose-message-recipient img {
.media-item, .media-preview, .media-image, .media-badge, .js-media-sensitive-overlay, .js-media-added .br--4, #tduck .compose-message-recipient img {
border-radius: 1px !important;
}
.tweet-button, .app-search-fake, .app-search-input, .compose-text-container, .compose-reply-tweet, .compose-message-recipient-input-container, .compose-message-recipient, .compose-media-bar-holder, .media-grid-container, .js-quote-tweet-holder, .detail-view-inline-text {
.tweet-button, .app-search-fake, .app-search-input, .compose-text-container, .compose-reply-tweet, .compose-message-recipient-input-container, .compose-message-recipient, .compose-media-bar-holder, .compose-media-bar-thumb, .media-grid-container, .js-add-image-description, .js-quote-tweet-holder, .detail-view-inline-text, .mdl-column-rhs {
border-radius: 0 !important;
}
.dropdown-menu, .list-item-last, .quoted-tweet, input, textarea, select, .prf-header {
.dropdown-menu, .list-item-last, .quoted-tweet, .hw-card-container > div, input, textarea, select, .prf-header {
border-radius: 0 !important;
}
@@ -365,14 +369,25 @@ html[data-td-font='smallest'] .badge-verified:before {
html[data-td-font='smallest'] .tweet-detail-wrapper .badge-verified:before {
/* fix cut off badge in detail view */
width: 13px !important;
width: 14px !important;
height: 14px !important;
background-position: -223px -97px !important;
}
html[data-td-font='smallest'] .fullname-badged:before, html[data-td-font='small'] .fullname-badged:before {
/* fix cut off badge in follow chirps */
margin-top: -7px !important;
/* fix cut off badge in follow chirps and detail view */
margin-top: -1px !important;
min-width: 14px !important;
}
html[data-td-font='smallest'] .tweet-detail-wrapper .fullname-badged:before {
/* fix misaligned badge in detail view */
margin-top: -2px !important;
}
html[data-td-font='small'] .tweet-detail-wrapper .fullname-badged:before {
/* fix misaligned badge in detail view */
margin-top: 0px !important;
}
.keyboard-shortcut-list {

View File

@@ -6,6 +6,7 @@ html, body {
height: auto !important;
overflow-x: hidden !important;
overflow-y: auto !important;
margin: 0;
}
body::before {
@@ -36,7 +37,7 @@ body::before {
/* Square-ify stuff */
/********************/
.media-item, .media-preview {
.media-item, .media-preview, .media-badge {
border-radius: 1px !important;
}

View File

@@ -25,6 +25,17 @@
margin: 0 auto !important;
}
.ResponsiveLayout {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
}
.Section {
padding: 36px !important;
}
/*******************/
/* General styling */
/*******************/
@@ -39,7 +50,7 @@ body {
box-shadow: 0 0 150px rgba(255, 255, 255, 0.3) !important;
}
.topbar {
.topbar, .TopNav {
/* hide top bar */
display: none !important;
}
@@ -65,3 +76,42 @@ button[type='submit'] {
margin-top: 15px !important;
font-weight: bold !important;
}
/********************************************/
/* Fix min width and margins on logout page */
/********************************************/
html[logout] .page-canvas {
width: auto !important;
max-width: 888px;
}
html[logout] .signout-wrapper {
width: auto !important;
margin: 0 auto !important;
}
html[logout] .signout {
margin: 60px 0 54px !important;
}
html[logout] .buttons {
padding-bottom: 0 !important;
}
/*******************************/
/* General logout page styling */
/*******************************/
html[logout] .aside {
/* hide elements around dialog */
display: none;
}
html[logout] .buttons button, html[logout] .buttons a {
/* style buttons */
display: inline-block;
margin: 0 4px !important;
border: 1px solid rgba(0, 0, 0, 0.3) !important;
border-radius: 0 !important;
}

View File

@@ -8,15 +8,15 @@
return;
}
let style = document.createElement("style");
let link = document.createElement("link");
link.rel = "stylesheet";
link.href = "https://abs.twimg.com/tduck/css";
style.innerText = `#import "styles/twitter.base.css"`;
document.head.appendChild(link);
if (location.pathname === "/logout"){
style.innerText += `#import "styles/twitter.logout.css"`;
document.documentElement.setAttribute("logout", "");
}
document.head.appendChild(style);
};
setTimeout(injectCSS, 1);

View File

@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="packages\CefSharp.WinForms.67.0.0-pre01\build\CefSharp.WinForms.props" Condition="Exists('packages\CefSharp.WinForms.67.0.0-pre01\build\CefSharp.WinForms.props')" />
<Import Project="packages\CefSharp.Common.67.0.0-pre01\build\CefSharp.Common.props" Condition="Exists('packages\CefSharp.Common.67.0.0-pre01\build\CefSharp.Common.props')" />
<Import Project="packages\Microsoft.Net.Compilers.2.9.0\build\Microsoft.Net.Compilers.props" Condition="Exists('packages\Microsoft.Net.Compilers.2.9.0\build\Microsoft.Net.Compilers.props')" />
<Import Project="packages\CefSharp.WinForms.67.0.0\build\CefSharp.WinForms.props" Condition="Exists('packages\CefSharp.WinForms.67.0.0\build\CefSharp.WinForms.props')" />
<Import Project="packages\CefSharp.Common.67.0.0\build\CefSharp.Common.props" Condition="Exists('packages\CefSharp.Common.67.0.0\build\CefSharp.Common.props')" />
<Import Project="packages\cef.redist.x86.3.3396.1786\build\cef.redist.x86.props" Condition="Exists('packages\cef.redist.x86.3.3396.1786\build\cef.redist.x86.props')" />
<Import Project="packages\cef.redist.x64.3.3396.1786\build\cef.redist.x64.props" Condition="Exists('packages\cef.redist.x64.3.3396.1786\build\cef.redist.x64.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
@@ -45,6 +46,12 @@
<LangVersion>7</LangVersion>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.WindowsAPICodePack, Version=1.1.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>packages\WindowsAPICodePack-Core.1.1.1\lib\Microsoft.WindowsAPICodePack.dll</HintPath>
</Reference>
<Reference Include="Microsoft.WindowsAPICodePack.Shell, Version=1.1.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>packages\WindowsAPICodePack-Shell.1.1.1\lib\Microsoft.WindowsAPICodePack.Shell.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
@@ -91,10 +98,12 @@
<DependentUpon>FormBrowser.cs</DependentUpon>
</Compile>
<Compile Include="Core\Handling\General\FileDialogHandler.cs" />
<Compile Include="Core\Handling\KeyboardHandlerBase.cs" />
<Compile Include="Core\Handling\KeyboardHandlerBrowser.cs" />
<Compile Include="Core\Handling\KeyboardHandlerNotification.cs" />
<Compile Include="Core\Handling\RequestHandlerBase.cs" />
<Compile Include="Core\Handling\RequestHandlerBrowser.cs" />
<Compile Include="Core\Handling\ResourceHandlerFactory.cs" />
<Compile Include="Core\Handling\ResourceHandlerNotification.cs" />
<Compile Include="Core\Management\ContextInfo.cs" />
<Compile Include="Core\Notification\Example\FormNotificationExample.cs">
@@ -390,6 +399,15 @@
<Name>TweetLib.Communication</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="Resources\Images\overlay-muted.png" />
</ItemGroup>
<ItemGroup>
<None Include="Resources\Images\overlay-muted.ico" />
</ItemGroup>
<ItemGroup>
<None Include="Resources\Images\overlay-notification.ico" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>rmdir "$(ProjectDir)bin\Debug"
@@ -424,11 +442,12 @@ IF EXIST "$(ProjectDir)bld\post_build.exe" (
</PropertyGroup>
<Error Condition="!Exists('packages\cef.redist.x64.3.3396.1786\build\cef.redist.x64.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\cef.redist.x64.3.3396.1786\build\cef.redist.x64.props'))" />
<Error Condition="!Exists('packages\cef.redist.x86.3.3396.1786\build\cef.redist.x86.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\cef.redist.x86.3.3396.1786\build\cef.redist.x86.props'))" />
<Error Condition="!Exists('packages\CefSharp.Common.67.0.0-pre01\build\CefSharp.Common.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.Common.67.0.0-pre01\build\CefSharp.Common.props'))" />
<Error Condition="!Exists('packages\CefSharp.Common.67.0.0-pre01\build\CefSharp.Common.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.Common.67.0.0-pre01\build\CefSharp.Common.targets'))" />
<Error Condition="!Exists('packages\CefSharp.WinForms.67.0.0-pre01\build\CefSharp.WinForms.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.WinForms.67.0.0-pre01\build\CefSharp.WinForms.props'))" />
<Error Condition="!Exists('packages\CefSharp.WinForms.67.0.0-pre01\build\CefSharp.WinForms.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.WinForms.67.0.0-pre01\build\CefSharp.WinForms.targets'))" />
<Error Condition="!Exists('packages\CefSharp.Common.67.0.0\build\CefSharp.Common.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.Common.67.0.0\build\CefSharp.Common.props'))" />
<Error Condition="!Exists('packages\CefSharp.Common.67.0.0\build\CefSharp.Common.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.Common.67.0.0\build\CefSharp.Common.targets'))" />
<Error Condition="!Exists('packages\CefSharp.WinForms.67.0.0\build\CefSharp.WinForms.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.WinForms.67.0.0\build\CefSharp.WinForms.props'))" />
<Error Condition="!Exists('packages\CefSharp.WinForms.67.0.0\build\CefSharp.WinForms.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.WinForms.67.0.0\build\CefSharp.WinForms.targets'))" />
<Error Condition="!Exists('packages\Microsoft.Net.Compilers.2.9.0\build\Microsoft.Net.Compilers.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\Microsoft.Net.Compilers.2.9.0\build\Microsoft.Net.Compilers.props'))" />
</Target>
<Import Project="packages\CefSharp.Common.67.0.0-pre01\build\CefSharp.Common.targets" Condition="Exists('packages\CefSharp.Common.67.0.0-pre01\build\CefSharp.Common.targets')" />
<Import Project="packages\CefSharp.WinForms.67.0.0-pre01\build\CefSharp.WinForms.targets" Condition="Exists('packages\CefSharp.WinForms.67.0.0-pre01\build\CefSharp.WinForms.targets')" />
<Import Project="packages\CefSharp.Common.67.0.0\build\CefSharp.Common.targets" Condition="Exists('packages\CefSharp.Common.67.0.0\build\CefSharp.Common.targets')" />
<Import Project="packages\CefSharp.WinForms.67.0.0\build\CefSharp.WinForms.targets" Condition="Exists('packages\CefSharp.WinForms.67.0.0\build\CefSharp.WinForms.targets')" />
</Project>

View File

@@ -220,14 +220,14 @@ begin
end;
{ Return whether the version of the installed libcef.dll library matches internal one. }
{ TODO: Remove workaround that forces full installation for 1.15 and older eventually. }
{ TODO: Remove workaround that forces full installation for 1.16 and older eventually. }
function TDIsMatchingCEFVersion: Boolean;
var CEFVersion: String;
var TDVersionMS: Cardinal;
var TDVersionLS: Cardinal;
begin
if (GetVersionNumbers(UpdatePath+'TweetDuck.exe', TDVersionMS, TDVersionLS)) and ((TDVersionMS and $FFFF) < 16) then
if (GetVersionNumbers(UpdatePath+'TweetDuck.exe', TDVersionMS, TDVersionLS)) and ((TDVersionMS and $FFFF) < 17) then
begin
Result := False
Exit

View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\packages\Microsoft.Net.Compilers.2.9.0\build\Microsoft.Net.Compilers.props" Condition="Exists('..\..\packages\Microsoft.Net.Compilers.2.9.0\build\Microsoft.Net.Compilers.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -11,6 +12,8 @@
<AssemblyName>TweetLib.Communication</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
@@ -40,5 +43,14 @@
<Compile Include="DuplexPipe.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\packages\Microsoft.Net.Compilers.2.9.0\build\Microsoft.Net.Compilers.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.Net.Compilers.2.9.0\build\Microsoft.Net.Compilers.props'))" />
</Target>
</Project>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Net.Compilers" version="2.9.0" targetFramework="net452" developmentDependency="true" />
</packages>

View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\packages\Microsoft.Net.Compilers.2.9.0\build\Microsoft.Net.Compilers.props" Condition="Exists('..\..\packages\Microsoft.Net.Compilers.2.9.0\build\Microsoft.Net.Compilers.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
@@ -17,6 +18,8 @@
<IsCodedUITest>False</IsCodedUITest>
<TestProjectType>UnitTest</TestProjectType>
<TargetFrameworkProfile />
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<PlatformTarget>x86</PlatformTarget>
@@ -55,6 +58,9 @@
<Name>TweetDuck</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Choose>
<When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'">
<ItemGroup>
@@ -75,6 +81,12 @@
</Choose>
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\packages\Microsoft.Net.Compilers.2.9.0\build\Microsoft.Net.Compilers.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.Net.Compilers.2.9.0\build\Microsoft.Net.Compilers.props'))" />
</Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Net.Compilers" version="2.9.0" targetFramework="net452" developmentDependency="true" />
</packages>

View File

@@ -2,6 +2,9 @@
<packages>
<package id="cef.redist.x64" version="3.3396.1786" targetFramework="net452" />
<package id="cef.redist.x86" version="3.3396.1786" targetFramework="net452" />
<package id="CefSharp.Common" version="67.0.0-pre01" targetFramework="net452" />
<package id="CefSharp.WinForms" version="67.0.0-pre01" targetFramework="net452" />
<package id="CefSharp.Common" version="67.0.0" targetFramework="net452" />
<package id="CefSharp.WinForms" version="67.0.0" targetFramework="net452" />
<package id="Microsoft.Net.Compilers" version="2.9.0" targetFramework="net452" developmentDependency="true" />
<package id="WindowsAPICodePack-Core" version="1.1.1" targetFramework="net452" />
<package id="WindowsAPICodePack-Shell" version="1.1.1" targetFramework="net452" />
</packages>

View File

@@ -1,17 +1,29 @@
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using CefSharp.BrowserSubprocess;
namespace TweetDuck.Browser{
static class Program{
internal const string Version = "1.4.1.0";
internal const string Version = "1.4.2";
private static int Main(string[] args){
SubProcess.EnableHighDPISupport();
const string typePrefix = "--type=";
string type = Array.Find(args, arg => arg.StartsWith(typePrefix, StringComparison.OrdinalIgnoreCase)).Substring(typePrefix.Length);
string FindArg(string key){
return Array.Find(args, arg => arg.StartsWith(key, StringComparison.OrdinalIgnoreCase)).Substring(key.Length);
}
if (type == "renderer"){
const string typePrefix = "--type=";
const string parentIdPrefix = "--host-process-id=";
if (!int.TryParse(FindArg(parentIdPrefix), out int parentId)){
return 0;
}
Task.Factory.StartNew(() => KillWhenHung(parentId), TaskCreationOptions.LongRunning);
if (FindArg(typePrefix) == "renderer"){
using(SubProcess subProcess = new SubProcess(args)){
return subProcess.Run();
}
@@ -20,5 +32,18 @@ namespace TweetDuck.Browser{
return SubProcess.ExecuteProcess();
}
}
private static async void KillWhenHung(int parentId){
try{
using(Process process = Process.GetProcessById(parentId)){
process.WaitForExit();
}
}catch{
// ded
}
await Task.Delay(10000);
Environment.Exit(0);
}
}
}

View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\Microsoft.Net.Compilers.2.9.0\build\Microsoft.Net.Compilers.props" Condition="Exists('..\packages\Microsoft.Net.Compilers.2.9.0\build\Microsoft.Net.Compilers.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -11,6 +12,8 @@
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<PlatformTarget>x86</PlatformTarget>
@@ -28,7 +31,7 @@
<ItemGroup>
<Reference Include="CefSharp.BrowserSubprocess.Core, Version=67.0.0.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=x86">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\CefSharp.Common.67.0.0-pre01\CefSharp\x86\CefSharp.BrowserSubprocess.Core.dll</HintPath>
<HintPath>..\packages\CefSharp.Common.67.0.0\CefSharp\x86\CefSharp.BrowserSubprocess.Core.dll</HintPath>
</Reference>
<Reference Include="System" />
</ItemGroup>
@@ -36,9 +39,18 @@
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>call "$(DevEnvDir)..\..\VC\Auxiliary\Build\vcvars32.bat"
editbin /largeaddressaware /TSAWARE "$(TargetPath)"</PostBuildEvent>
</PropertyGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\Microsoft.Net.Compilers.2.9.0\build\Microsoft.Net.Compilers.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Net.Compilers.2.9.0\build\Microsoft.Net.Compilers.props'))" />
</Target>
</Project>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Net.Compilers" version="2.9.0" targetFramework="net452" developmentDependency="true" />
</packages>

View File

@@ -19,7 +19,14 @@ namespace TweetDuck.Video.Controls{
Form form = control.FindForm();
System.Diagnostics.Debug.Assert(form != null);
Text = tooltipFunc(args);
string text = tooltipFunc(args);
if (text == null){
Visible = false;
return;
}
Text = text;
Point loc = form.PointToClient(control.Parent.PointToScreen(new Point(control.Location.X+(followCursor ? args.X : control.Width/2), 0)));
loc.X = Math.Max(0, Math.Min(form.Width-Width, loc.X-Width/2));

View File

@@ -30,7 +30,7 @@ namespace TweetDuck.Video.Controls{
brushBack.Color = Parent.BackColor;
}
Rectangle rect = e.ClipRectangle;
Rectangle rect = new Rectangle(0, 0, Width, Height);
Point cursor = PointToClient(Cursor.Position);
int width = rect.Width-1;
int progress = (int)(width*((double)Value/Maximum));

View File

@@ -26,16 +26,18 @@
this.components = new System.ComponentModel.Container();
this.timerSync = new System.Windows.Forms.Timer(this.components);
this.trackBarVolume = new System.Windows.Forms.TrackBar();
this.tablePanel = new System.Windows.Forms.TableLayoutPanel();
this.tablePanelFull = new System.Windows.Forms.TableLayoutPanel();
this.progressSeek = new TweetDuck.Video.Controls.SeekBar();
this.labelTime = new System.Windows.Forms.Label();
this.timerData = new System.Windows.Forms.Timer(this.components);
this.labelTooltip = new TweetDuck.Video.Controls.LabelTooltip();
this.imageResize = new System.Windows.Forms.PictureBox();
this.imageDownload = new System.Windows.Forms.PictureBox();
this.imageClose = new System.Windows.Forms.PictureBox();
this.timerData = new System.Windows.Forms.Timer(this.components);
this.tablePanelCompactBottom = new System.Windows.Forms.TableLayoutPanel();
this.tablePanelCompactTop = new System.Windows.Forms.TableLayoutPanel();
this.labelTooltip = new TweetDuck.Video.Controls.LabelTooltip();
((System.ComponentModel.ISupportInitialize)(this.trackBarVolume)).BeginInit();
this.tablePanel.SuspendLayout();
this.tablePanelFull.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.imageResize)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.imageDownload)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.imageClose)).BeginInit();
@@ -64,30 +66,32 @@
this.trackBarVolume.MouseDown += new System.Windows.Forms.MouseEventHandler(this.trackBarVolume_MouseDown);
this.trackBarVolume.MouseUp += new System.Windows.Forms.MouseEventHandler(this.trackBarVolume_MouseUp);
//
// tablePanel
// tablePanelFull
//
this.tablePanel.BackColor = System.Drawing.SystemColors.Control;
this.tablePanel.ColumnCount = 6;
this.tablePanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 28F));
this.tablePanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tablePanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 74F));
this.tablePanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 130F));
this.tablePanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 28F));
this.tablePanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 28F));
this.tablePanel.Controls.Add(this.trackBarVolume, 3, 0);
this.tablePanel.Controls.Add(this.progressSeek, 1, 0);
this.tablePanel.Controls.Add(this.labelTime, 2, 0);
this.tablePanel.Controls.Add(this.imageResize, 5, 0);
this.tablePanel.Controls.Add(this.imageDownload, 4, 0);
this.tablePanel.Controls.Add(this.imageClose, 0, 0);
this.tablePanel.Dock = System.Windows.Forms.DockStyle.Bottom;
this.tablePanel.Location = new System.Drawing.Point(0, 86);
this.tablePanel.Name = "tablePanel";
this.tablePanel.Padding = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.tablePanel.RowCount = 1;
this.tablePanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tablePanel.Size = new System.Drawing.Size(400, 34);
this.tablePanel.TabIndex = 1;
this.tablePanelFull.BackColor = System.Drawing.SystemColors.Control;
this.tablePanelFull.ColumnCount = 6;
this.tablePanelFull.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 28F));
this.tablePanelFull.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tablePanelFull.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 80F));
this.tablePanelFull.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 130F));
this.tablePanelFull.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 28F));
this.tablePanelFull.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 28F));
this.tablePanelFull.Controls.Add(this.trackBarVolume, 3, 0);
this.tablePanelFull.Controls.Add(this.progressSeek, 1, 0);
this.tablePanelFull.Controls.Add(this.labelTime, 2, 0);
this.tablePanelFull.Controls.Add(this.imageResize, 5, 0);
this.tablePanelFull.Controls.Add(this.imageDownload, 4, 0);
this.tablePanelFull.Controls.Add(this.imageClose, 0, 0);
this.tablePanelFull.Dock = System.Windows.Forms.DockStyle.Bottom;
this.tablePanelFull.Enabled = false;
this.tablePanelFull.Location = new System.Drawing.Point(0, 86);
this.tablePanelFull.Name = "tablePanelFull";
this.tablePanelFull.Padding = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.tablePanelFull.RowCount = 1;
this.tablePanelFull.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tablePanelFull.Size = new System.Drawing.Size(400, 34);
this.tablePanelFull.TabIndex = 0;
this.tablePanelFull.Visible = false;
//
// progressSeek
//
@@ -98,7 +102,7 @@
this.progressSeek.Margin = new System.Windows.Forms.Padding(9, 10, 8, 10);
this.progressSeek.Maximum = 5000;
this.progressSeek.Name = "progressSeek";
this.progressSeek.Size = new System.Drawing.Size(91, 14);
this.progressSeek.Size = new System.Drawing.Size(85, 14);
this.progressSeek.Style = System.Windows.Forms.ProgressBarStyle.Continuous;
this.progressSeek.TabIndex = 0;
this.progressSeek.MouseDown += new System.Windows.Forms.MouseEventHandler(this.progressSeek_MouseDown);
@@ -106,32 +110,14 @@
// labelTime
//
this.labelTime.Dock = System.Windows.Forms.DockStyle.Fill;
this.labelTime.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular);
this.labelTime.Location = new System.Drawing.Point(138, 3);
this.labelTime.Font = new System.Drawing.Font("Segoe UI", 9F);
this.labelTime.Location = new System.Drawing.Point(132, 3);
this.labelTime.Margin = new System.Windows.Forms.Padding(0, 3, 0, 5);
this.labelTime.Name = "labelTime";
this.labelTime.Size = new System.Drawing.Size(74, 26);
this.labelTime.Size = new System.Drawing.Size(80, 26);
this.labelTime.TabIndex = 1;
this.labelTime.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
//
// timerData
//
this.timerData.Interval = 500;
this.timerData.Tick += new System.EventHandler(this.timerData_Tick);
//
// labelTooltip
//
this.labelTooltip.AutoSize = true;
this.labelTooltip.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold);
this.labelTooltip.ForeColor = System.Drawing.Color.White;
this.labelTooltip.Location = new System.Drawing.Point(0, 0);
this.labelTooltip.Margin = new System.Windows.Forms.Padding(0, 2, 0, 0);
this.labelTooltip.Name = "labelTooltip";
this.labelTooltip.Padding = new System.Windows.Forms.Padding(4, 2, 2, 2);
this.labelTooltip.Size = new System.Drawing.Size(6, 19);
this.labelTooltip.TabIndex = 2;
this.labelTooltip.Visible = false;
//
// imageResize
//
this.imageResize.Cursor = System.Windows.Forms.Cursors.Hand;
@@ -178,6 +164,66 @@
this.imageClose.WaitOnLoad = true;
this.imageClose.Click += new System.EventHandler(this.imageClose_Click);
//
// timerData
//
this.timerData.Interval = 500;
this.timerData.Tick += new System.EventHandler(this.timerData_Tick);
//
// tablePanelCompactBottom
//
this.tablePanelCompactBottom.BackColor = System.Drawing.SystemColors.Control;
this.tablePanelCompactBottom.ColumnCount = 5;
this.tablePanelCompactBottom.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 28F));
this.tablePanelCompactBottom.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tablePanelCompactBottom.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 130F));
this.tablePanelCompactBottom.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 28F));
this.tablePanelCompactBottom.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 28F));
this.tablePanelCompactBottom.Dock = System.Windows.Forms.DockStyle.Bottom;
this.tablePanelCompactBottom.Enabled = false;
this.tablePanelCompactBottom.Location = new System.Drawing.Point(0, 52);
this.tablePanelCompactBottom.Margin = new System.Windows.Forms.Padding(3, 0, 3, 3);
this.tablePanelCompactBottom.Name = "tablePanelCompactBottom";
this.tablePanelCompactBottom.Padding = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.tablePanelCompactBottom.RowCount = 1;
this.tablePanelCompactBottom.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tablePanelCompactBottom.Size = new System.Drawing.Size(400, 34);
this.tablePanelCompactBottom.TabIndex = 1;
//
// tablePanelCompactTop
//
this.tablePanelCompactTop.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.tablePanelCompactTop.BackColor = System.Drawing.SystemColors.Control;
this.tablePanelCompactTop.ColumnCount = 2;
this.tablePanelCompactTop.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tablePanelCompactTop.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 80F));
this.tablePanelCompactTop.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F));
this.tablePanelCompactTop.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F));
this.tablePanelCompactTop.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F));
this.tablePanelCompactTop.Enabled = false;
this.tablePanelCompactTop.Location = new System.Drawing.Point(0, 60);
this.tablePanelCompactTop.Margin = new System.Windows.Forms.Padding(0);
this.tablePanelCompactTop.Name = "tablePanelCompactTop";
this.tablePanelCompactTop.Padding = new System.Windows.Forms.Padding(2, 0, 4, 0);
this.tablePanelCompactTop.RowCount = 1;
this.tablePanelCompactTop.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tablePanelCompactTop.Size = new System.Drawing.Size(400, 34);
this.tablePanelCompactTop.TabIndex = 2;
this.tablePanelCompactTop.Visible = false;
//
// labelTooltip
//
this.labelTooltip.AutoSize = true;
this.labelTooltip.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold);
this.labelTooltip.ForeColor = System.Drawing.Color.White;
this.labelTooltip.Location = new System.Drawing.Point(0, 0);
this.labelTooltip.Margin = new System.Windows.Forms.Padding(0, 2, 0, 0);
this.labelTooltip.Name = "labelTooltip";
this.labelTooltip.Padding = new System.Windows.Forms.Padding(4, 2, 2, 2);
this.labelTooltip.Size = new System.Drawing.Size(6, 19);
this.labelTooltip.TabIndex = 3;
this.labelTooltip.Visible = false;
//
// FormPlayer
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
@@ -186,12 +232,13 @@
this.ClientSize = new System.Drawing.Size(400, 120);
this.ControlBox = false;
this.Controls.Add(this.labelTooltip);
this.Controls.Add(this.tablePanel);
this.Controls.Add(this.tablePanelCompactBottom);
this.Controls.Add(this.tablePanelFull);
this.Controls.Add(this.tablePanelCompactTop);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
this.Location = new System.Drawing.Point(-32000, -32000);
this.MaximizeBox = false;
this.MinimizeBox = false;
this.MinimumSize = new System.Drawing.Size(400, 120);
this.Name = "FormPlayer";
this.ShowIcon = false;
this.ShowInTaskbar = false;
@@ -199,7 +246,7 @@
this.Text = "TweetDuck Video";
this.Load += new System.EventHandler(this.FormPlayer_Load);
((System.ComponentModel.ISupportInitialize)(this.trackBarVolume)).EndInit();
this.tablePanel.ResumeLayout(false);
this.tablePanelFull.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.imageResize)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.imageDownload)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.imageClose)).EndInit();
@@ -212,7 +259,7 @@
private System.Windows.Forms.Timer timerSync;
private System.Windows.Forms.TrackBar trackBarVolume;
private System.Windows.Forms.TableLayoutPanel tablePanel;
private System.Windows.Forms.TableLayoutPanel tablePanelFull;
private Controls.SeekBar progressSeek;
private System.Windows.Forms.Label labelTime;
private System.Windows.Forms.Timer timerData;
@@ -220,6 +267,8 @@
private System.Windows.Forms.PictureBox imageResize;
private System.Windows.Forms.PictureBox imageDownload;
private System.Windows.Forms.PictureBox imageClose;
private System.Windows.Forms.TableLayoutPanel tablePanelCompactBottom;
private System.Windows.Forms.TableLayoutPanel tablePanelCompactTop;
}
}

View File

@@ -10,9 +10,17 @@ using WMPLib;
namespace TweetDuck.Video{
sealed partial class FormPlayer : Form{
private bool IsCursorOverVideo{
get{
Point cursor = PointToClient(Cursor.Position);
return cursor.Y < (tablePanelFull.Enabled ? tablePanelFull.Location.Y : tablePanelCompactTop.Location.Y);
}
}
protected override bool ShowWithoutActivation => true;
private readonly IntPtr ownerHandle;
private readonly float ownerDpi;
private readonly string videoUrl;
private readonly DuplexPipe pipe;
@@ -23,14 +31,21 @@ namespace TweetDuck.Video{
private WindowsMediaPlayer Player => player.Ocx;
public FormPlayer(IntPtr handle, int volume, string url, string token){
public FormPlayer(IntPtr handle, int dpi, int volume, string url, string token){
InitializeComponent();
this.ownerHandle = handle;
this.ownerDpi = dpi / 100F;
this.videoUrl = url;
this.pipe = DuplexPipe.CreateClient(token);
this.pipe.DataIn += pipe_DataIn;
if (NativeMethods.GetWindowRect(ownerHandle, out NativeMethods.RECT rect)){
ClientSize = new Size(0, 0);
Location = new Point((rect.Left+rect.Right)/2, (rect.Top+rect.Bottom)/2);
Opacity = 0;
}
player = new ControlWMP{
Dock = DockStyle.Fill
};
@@ -51,6 +66,10 @@ namespace TweetDuck.Video{
trackBarVolume.Value = volume; // changes player volume too if non-default
labelTooltip.AttachTooltip(progressSeek, true, args => {
if (args.X < 0 || args.Y < 0 || args.X >= progressSeek.Width || args.Y >= progressSeek.Height){
return null;
}
IWMPMedia media = Player.currentMedia;
int progress = (int)(media.duration*progressSeek.GetProgress(args.X));
@@ -68,6 +87,59 @@ namespace TweetDuck.Video{
Application.AddMessageFilter(new MessageFilter(this));
}
// Layout
private int DpiScaled(int value){
return (int)Math.Round(value*ownerDpi);
}
private void RefreshControlPanel(){
bool useCompactLayout = ClientSize.Width < DpiScaled(480);
bool needsUpdate = !timerSync.Enabled || (useCompactLayout ? tablePanelFull.Enabled : tablePanelCompactBottom.Enabled);
if (needsUpdate){
void Disable(TableLayoutPanel panel){
panel.Controls.Clear();
panel.Visible = false;
panel.Enabled = false;
}
tablePanelFull.SuspendLayout();
tablePanelCompactBottom.SuspendLayout();
tablePanelCompactTop.SuspendLayout();
if (useCompactLayout){
Disable(tablePanelFull);
tablePanelCompactBottom.Enabled = true;
tablePanelCompactBottom.Controls.Add(imageClose, 0, 0);
tablePanelCompactBottom.Controls.Add(trackBarVolume, 2, 0);
tablePanelCompactBottom.Controls.Add(imageDownload, 3, 0);
tablePanelCompactBottom.Controls.Add(imageResize, 4, 0);
tablePanelCompactTop.Enabled = true;
tablePanelCompactTop.Controls.Add(progressSeek, 0, 0);
tablePanelCompactTop.Controls.Add(labelTime, 1, 0);
}
else{
Disable(tablePanelCompactBottom);
Disable(tablePanelCompactTop);
tablePanelFull.Enabled = true;
tablePanelFull.Controls.Add(imageClose, 0, 0);
tablePanelFull.Controls.Add(progressSeek, 1, 0);
tablePanelFull.Controls.Add(labelTime, 2, 0);
tablePanelFull.Controls.Add(trackBarVolume, 3, 0);
tablePanelFull.Controls.Add(imageDownload, 4, 0);
tablePanelFull.Controls.Add(imageResize, 5, 0);
}
tablePanelFull.ResumeLayout();
tablePanelCompactBottom.ResumeLayout();
tablePanelCompactTop.ResumeLayout();
}
}
// Events
private void FormPlayer_Load(object sender, EventArgs e){
@@ -97,9 +169,14 @@ namespace TweetDuck.Video{
else if (state == WMPPlayState.wmppsPlaying){
Player.PlayStateChange -= player_PlayStateChange;
timerSync.Start();
NativeMethods.SetWindowOwner(Handle, ownerHandle);
Cursor.Current = Cursors.Default;
SuspendLayout();
timerSync_Tick(timerSync, EventArgs.Empty);
timerSync.Start();
Opacity = 1;
ResumeLayout(true);
}
}
@@ -115,20 +192,38 @@ namespace TweetDuck.Video{
[HandleProcessCorruptedStateExceptions]
private void timerSync_Tick(object sender, EventArgs e){
if (NativeMethods.GetWindowRect(ownerHandle, out NativeMethods.RECT rect)){
int width = rect.Right-rect.Left+1;
int height = rect.Bottom-rect.Top+1;
IWMPMedia media = Player.currentMedia;
IWMPControls controls = Player.controls;
int ownerLeft = rect.Left;
int ownerTop = rect.Top;
int ownerWidth = rect.Right-rect.Left+1;
int ownerHeight = rect.Bottom-rect.Top+1;
// roughly matches MinimumSize for client bounds, adjusted a bit for weirdness with higher DPI
int minWidth = DpiScaled(356);
int minHeight = DpiScaled(386);
if (NativeMethods.GetClientRect(ownerHandle, out NativeMethods.RECT clientSize)){
minWidth = Math.Min(minWidth, clientSize.Right);
minHeight = Math.Min(minHeight, clientSize.Bottom);
}
int maxWidth = Math.Min(DpiScaled(media.imageSourceWidth), ownerWidth*3/4);
int maxHeight = Math.Min(DpiScaled(media.imageSourceHeight), ownerHeight*3/4);
bool isCursorInside = ClientRectangle.Contains(PointToClient(Cursor.Position));
ClientSize = new Size(Math.Max(MinimumSize.Width, Math.Min(media.imageSourceWidth, width*3/4)), Math.Max(MinimumSize.Height, Math.Min(media.imageSourceHeight, height*3/4)));
Location = new Point(rect.Left+(width-ClientSize.Width)/2, rect.Top+(height-ClientSize.Height+SystemInformation.CaptionHeight)/2);
Size newSize = new Size(Math.Max(minWidth+2, maxWidth), Math.Max(minHeight+2, maxHeight));
Point newLocation = new Point(ownerLeft+(ownerWidth-newSize.Width)/2, ownerTop+(ownerHeight-newSize.Height+SystemInformation.CaptionHeight)/2);
tablePanel.Visible = isCursorInside || isDragging;
if (ClientSize != newSize || Location != newLocation){
ClientSize = newSize;
Location = newLocation;
RefreshControlPanel();
}
if (tablePanel.Visible){
if (isCursorInside || isDragging){
labelTime.Text = $"{controls.currentPositionString} / {media.durationString}";
int value = (int)Math.Round(progressSeek.Maximum*controls.currentPosition/media.duration);
@@ -142,6 +237,19 @@ namespace TweetDuck.Video{
progressSeek.Value = value+1;
progressSeek.Value = value;
}
if (tablePanelFull.Enabled){
tablePanelFull.Visible = true;
}
else{
tablePanelCompactBottom.Visible = true;
tablePanelCompactTop.Visible = true;
}
}
else{
tablePanelFull.Visible = false;
tablePanelCompactBottom.Visible = false;
tablePanelCompactTop.Visible = false;
}
if (controls.currentPosition > media.duration){ // pausing near the end of the video causes WMP to play beyond the end of the video wtf
@@ -157,6 +265,10 @@ namespace TweetDuck.Video{
if (isCursorInside && !wasCursorInside){
wasCursorInside = true;
if (IsCursorOverVideo){
Cursor.Current = Cursors.Default;
}
}
else if (!isCursorInside && wasCursorInside){
wasCursorInside = false;
@@ -273,26 +385,19 @@ namespace TweetDuck.Video{
internal sealed class MessageFilter : IMessageFilter{
private readonly FormPlayer form;
private bool IsCursorOverVideo{
get{
Point cursor = form.PointToClient(Cursor.Position);
return cursor.Y < form.tablePanel.Location.Y;
}
}
public MessageFilter(FormPlayer form){
this.form = form;
}
bool IMessageFilter.PreFilterMessage(ref Message m){
if (m.Msg == 0x0201){ // WM_LBUTTONDOWN
if (IsCursorOverVideo){
if (form.IsCursorOverVideo){
form.TogglePause();
return true;
}
}
else if (m.Msg == 0x0203){ // WM_LBUTTONDBLCLK
if (IsCursorOverVideo){
if (form.IsCursorOverVideo){
form.TogglePause();
form.Player.fullScreen = !form.Player.fullScreen;
return true;

View File

@@ -9,6 +9,10 @@ namespace TweetDuck.Video{
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect);
[DllImport("user32.dll")]
private static extern int SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong);

View File

@@ -5,7 +5,7 @@ using System.Windows.Forms;
namespace TweetDuck.Video{
static class Program{
internal const string Version = "1.3";
internal const string Version = "1.4.2";
// referenced in VideoPlayer
// set by task manager -- public const int CODE_PROCESS_KILLED = 1;
@@ -25,21 +25,23 @@ namespace TweetDuck.Video{
CultureInfo.DefaultThreadCurrentCulture = CultureInfo.InvariantCulture;
IntPtr ownerHandle;
int ownerDpi;
int defaultVolume;
string videoUrl;
string pipeToken;
try{
ownerHandle = new IntPtr(int.Parse(args[0], NumberStyles.Integer));
defaultVolume = int.Parse(args[1], NumberStyles.Integer);
videoUrl = new Uri(args[2], UriKind.Absolute).AbsoluteUri;
pipeToken = args[3];
ownerDpi = int.Parse(args[1], NumberStyles.Integer);
defaultVolume = int.Parse(args[2], NumberStyles.Integer);
videoUrl = new Uri(args[3], UriKind.Absolute).AbsoluteUri;
pipeToken = args[4];
}catch{
return CODE_INVALID_ARGS;
}
try{
Application.Run(new FormPlayer(ownerHandle, defaultVolume, videoUrl, pipeToken));
Application.Run(new FormPlayer(ownerHandle, ownerDpi, defaultVolume, videoUrl, pipeToken));
}catch(Exception e){
Console.Out.WriteLine(e);
return CODE_LAUNCH_FAIL;

View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\Microsoft.Net.Compilers.2.9.0\build\Microsoft.Net.Compilers.props" Condition="Exists('..\packages\Microsoft.Net.Compilers.2.9.0\build\Microsoft.Net.Compilers.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -12,6 +13,8 @@
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<ResolveComReferenceSilent>True</ResolveComReferenceSilent>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
@@ -82,6 +85,7 @@
</COMReference>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
<None Include="Resources\btnResize.png" />
<None Include="Resources\btnDownload.png" />
<None Include="Resources\btnClose.png" />
@@ -100,4 +104,10 @@
</EmbeddedResource>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\Microsoft.Net.Compilers.2.9.0\build\Microsoft.Net.Compilers.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Net.Compilers.2.9.0\build\Microsoft.Net.Compilers.props'))" />
</Target>
</Project>

4
video/packages.config Normal file
View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Net.Compilers" version="2.9.0" targetFramework="net452" developmentDependency="true" />
</packages>