Skip to content

Commit cfb4c1e

Browse files
committed
IP Widget: Avalonia cross-platform IP checker with multi-source, geo, VPN detection and tray
0 parents  commit cfb4c1e

12 files changed

Lines changed: 983 additions & 0 deletions

.gitignore

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
## .NET build output
2+
bin/
3+
obj/
4+
out/
5+
publish/
6+
7+
## IDE / editor
8+
.vs/
9+
.vscode/
10+
.idea/
11+
*.user
12+
*.suo
13+
14+
## OS
15+
.DS_Store
16+
Thumbs.db

App.axaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<Application xmlns="https://github.com/avaloniaui"
2+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
3+
x:Class="IpWidget.App"
4+
RequestedThemeVariant="Dark">
5+
<Application.Styles>
6+
<FluentTheme />
7+
</Application.Styles>
8+
</Application>

App.axaml.cs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
using Avalonia;
2+
using Avalonia.Controls;
3+
using Avalonia.Controls.ApplicationLifetimes;
4+
using Avalonia.Markup.Xaml;
5+
using IpWidget.Services;
6+
7+
namespace IpWidget;
8+
9+
public partial class App : Application
10+
{
11+
private TrayIcon? _tray;
12+
13+
public override void Initialize() => AvaloniaXamlLoader.Load(this);
14+
15+
public override void OnFrameworkInitializationCompleted()
16+
{
17+
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
18+
{
19+
// window can hide to tray without the app quitting
20+
desktop.ShutdownMode = ShutdownMode.OnExplicitShutdown;
21+
22+
var icon = IconFactory.Create();
23+
var window = new MainWindow { Icon = icon };
24+
window.RequestQuit = () => desktop.Shutdown();
25+
desktop.MainWindow = window;
26+
27+
_tray = new TrayIcon
28+
{
29+
Icon = icon,
30+
ToolTipText = "IP Widget",
31+
IsVisible = true,
32+
};
33+
34+
var menu = new NativeMenu();
35+
var show = new NativeMenuItem("Показать");
36+
show.Click += (_, _) => window.ShowFromTray();
37+
var refresh = new NativeMenuItem("Обновить");
38+
refresh.Click += (_, _) => { window.ShowFromTray(); window.TriggerCheck(); };
39+
var quit = new NativeMenuItem("Выход");
40+
quit.Click += (_, _) => desktop.Shutdown();
41+
42+
menu.Items.Add(show);
43+
menu.Items.Add(refresh);
44+
menu.Items.Add(new NativeMenuItemSeparator());
45+
menu.Items.Add(quit);
46+
_tray.Menu = menu;
47+
_tray.Clicked += (_, _) => window.ShowFromTray();
48+
49+
TrayIcon.SetIcons(this, new TrayIcons { _tray });
50+
}
51+
52+
base.OnFrameworkInitializationCompleted();
53+
}
54+
}

IpWidget.csproj

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>WinExe</OutputType>
5+
<TargetFramework>net9.0</TargetFramework>
6+
<Nullable>enable</Nullable>
7+
<LangVersion>latest</LangVersion>
8+
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
9+
<ApplicationManifest>app.manifest</ApplicationManifest>
10+
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
11+
<!-- lean output -->
12+
<PublishTrimmed>false</PublishTrimmed>
13+
<InvariantGlobalization>true</InvariantGlobalization>
14+
<DebuggerSupport>false</DebuggerSupport>
15+
<EventSourceSupport>false</EventSourceSupport>
16+
<HttpActivityPropagationSupport>false</HttpActivityPropagationSupport>
17+
</PropertyGroup>
18+
19+
<ItemGroup>
20+
<PackageReference Include="Avalonia" Version="11.2.3" />
21+
<PackageReference Include="Avalonia.Desktop" Version="11.2.3" />
22+
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.2.3" />
23+
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.2.3" />
24+
</ItemGroup>
25+
26+
</Project>

MainWindow.axaml

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
<Window xmlns="https://github.com/avaloniaui"
2+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
3+
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
4+
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
5+
mc:Ignorable="d"
6+
x:Class="IpWidget.MainWindow"
7+
Title="IP Widget"
8+
Width="340" Height="520"
9+
MinWidth="300" MinHeight="420"
10+
CanResize="False"
11+
ShowInTaskbar="True"
12+
SystemDecorations="None"
13+
Background="Transparent"
14+
TransparencyLevelHint="AcrylicBlur, Blur, Transparent"
15+
ExtendClientAreaToDecorationsHint="True"
16+
ExtendClientAreaChromeHints="NoChrome"
17+
ExtendClientAreaTitleBarHeightHint="0">
18+
19+
<Window.Resources>
20+
<LinearGradientBrush x:Key="BgBrush" StartPoint="0%,0%" EndPoint="100%,100%">
21+
<GradientStop Color="#EE1B1035" Offset="0"/>
22+
<GradientStop Color="#EE2A1B57" Offset="0.45"/>
23+
<GradientStop Color="#EE0E2A4A" Offset="1"/>
24+
</LinearGradientBrush>
25+
<LinearGradientBrush x:Key="AccentBrush" StartPoint="0%,0%" EndPoint="100%,0%">
26+
<GradientStop Color="#7C6CFF" Offset="0"/>
27+
<GradientStop Color="#4FD1FF" Offset="1"/>
28+
</LinearGradientBrush>
29+
30+
<!-- icon geometries (mdi) -->
31+
<StreamGeometry x:Key="IconClose">M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z</StreamGeometry>
32+
<StreamGeometry x:Key="IconMinimize">M19,13H5V11H19V13Z</StreamGeometry>
33+
<StreamGeometry x:Key="IconPin">M16,12V4H17V2H7V4H8V12L6,14V16H11.2V22H12.8V16H18V14L16,12Z</StreamGeometry>
34+
<StreamGeometry x:Key="IconCopy">M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z</StreamGeometry>
35+
<StreamGeometry x:Key="IconRefresh">M17.65,6.35C16.2,4.9 14.21,4 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20C15.73,20 18.84,17.45 19.73,14H17.65C16.83,16.33 14.61,18 12,18A6,6 0 0,1 6,12A6,6 0 0,1 12,6C13.66,6 15.14,6.69 16.22,7.78L13,11H20V4L17.65,6.35Z</StreamGeometry>
36+
<StreamGeometry x:Key="IconFlash">M7,2V13H10V22L17,10H13L17,2H7Z</StreamGeometry>
37+
<StreamGeometry x:Key="IconLocation">M12,11.5A2.5,2.5 0 0,1 9.5,9A2.5,2.5 0 0,1 12,6.5A2.5,2.5 0 0,1 14.5,9A2.5,2.5 0 0,1 12,11.5M12,2A7,7 0 0,0 5,9C5,14.25 12,22 12,22C12,22 19,14.25 19,9A7,7 0 0,0 12,2Z</StreamGeometry>
38+
<StreamGeometry x:Key="IconShieldCheck">M12,1L3,5V11C3,16.55 6.84,21.74 12,23C17.16,21.74 21,16.55 21,11V5L12,1M10,17L6,13L7.41,11.59L10,14.17L16.59,7.58L18,9L10,17Z</StreamGeometry>
39+
<StreamGeometry x:Key="IconShieldAlert">M12,2L4,5V11.09C4,16.14 7.41,20.85 12,22C16.59,20.85 20,16.14 20,11.09V5L12,2M11,7H13V13H11V7M11,15H13V17H11V15Z</StreamGeometry>
40+
</Window.Resources>
41+
42+
<Border CornerRadius="18" Background="{StaticResource BgBrush}"
43+
BorderThickness="1" ClipToBounds="True">
44+
<Border.BorderBrush>
45+
<LinearGradientBrush StartPoint="0%,0%" EndPoint="100%,100%">
46+
<GradientStop Color="#40FFFFFF" Offset="0"/>
47+
<GradientStop Color="#10FFFFFF" Offset="1"/>
48+
</LinearGradientBrush>
49+
</Border.BorderBrush>
50+
51+
<Grid RowDefinitions="Auto,Auto,*,Auto">
52+
53+
<!-- ===== Title bar (drag) ===== -->
54+
<Grid x:Name="TitleBar" Grid.Row="0" Height="42" Margin="16,10,10,0"
55+
ColumnDefinitions="Auto,*,Auto,Auto,Auto">
56+
<StackPanel Orientation="Horizontal" Spacing="8" VerticalAlignment="Center">
57+
<Border Width="10" Height="10" CornerRadius="5"
58+
Background="{StaticResource AccentBrush}"/>
59+
<TextBlock Text="IP WIDGET" FontSize="12" FontWeight="Bold"
60+
Foreground="#CFE3FF" LetterSpacing="2" VerticalAlignment="Center"/>
61+
</StackPanel>
62+
63+
<Button x:Name="MinBtn" Grid.Column="2" Classes="chrome" ToolTip.Tip="Свернуть в трей">
64+
<PathIcon Data="{StaticResource IconMinimize}" Width="14" Height="14"/>
65+
</Button>
66+
<Button x:Name="PinBtn" Grid.Column="3" Classes="chrome" ToolTip.Tip="Поверх всех окон">
67+
<PathIcon x:Name="PinIcon" Data="{StaticResource IconPin}" Width="14" Height="14"/>
68+
</Button>
69+
<Button x:Name="CloseBtn" Grid.Column="4" Classes="chrome" ToolTip.Tip="Выход">
70+
<PathIcon Data="{StaticResource IconClose}" Width="14" Height="14"/>
71+
</Button>
72+
</Grid>
73+
74+
<!-- ===== Hero ===== -->
75+
<StackPanel Grid.Row="1" Margin="20,14,20,6" Spacing="6">
76+
<StackPanel Spacing="2">
77+
<TextBlock Text="ВАШ ВНЕШНИЙ IP" FontSize="10" Foreground="#7E8AB5"
78+
LetterSpacing="2" FontWeight="SemiBold"/>
79+
<TextBlock x:Name="IpText" Text="" FontSize="30" FontWeight="Bold"
80+
Foreground="White" TextTrimming="CharacterEllipsis">
81+
<TextBlock.Effect>
82+
<DropShadowEffect Color="#7C6CFF" BlurRadius="24" OffsetX="0" OffsetY="0" Opacity="0.6"/>
83+
</TextBlock.Effect>
84+
</TextBlock>
85+
<TextBlock x:Name="StatusText" Text="нажми «Проверить»" FontSize="11"
86+
Foreground="#8FA0C8"/>
87+
</StackPanel>
88+
89+
<!-- geo line -->
90+
<StackPanel x:Name="GeoPanel" Orientation="Horizontal" Spacing="7" IsVisible="False">
91+
<PathIcon Data="{StaticResource IconLocation}" Width="13" Height="13"
92+
Foreground="#8FA0C8" VerticalAlignment="Center"/>
93+
<TextBlock x:Name="GeoText" FontSize="12" Foreground="#C2D2F0"
94+
VerticalAlignment="Center" TextTrimming="CharacterEllipsis"/>
95+
</StackPanel>
96+
97+
<!-- VPN / connection-type chip -->
98+
<Border x:Name="VpnChip" CornerRadius="8" Padding="10,5"
99+
HorizontalAlignment="Left" IsVisible="False">
100+
<StackPanel Orientation="Horizontal" Spacing="6">
101+
<PathIcon x:Name="VpnIcon" Width="14" Height="14" VerticalAlignment="Center"/>
102+
<TextBlock x:Name="VpnText" FontSize="11" FontWeight="SemiBold"
103+
VerticalAlignment="Center"/>
104+
</StackPanel>
105+
</Border>
106+
</StackPanel>
107+
108+
<!-- ===== Sources ===== -->
109+
<ScrollViewer Grid.Row="2" Margin="14,6,14,0" VerticalScrollBarVisibility="Auto">
110+
<ItemsControl x:Name="SourcesList">
111+
<ItemsControl.ItemsPanel>
112+
<ItemsPanelTemplate>
113+
<StackPanel Spacing="6"/>
114+
</ItemsPanelTemplate>
115+
</ItemsControl.ItemsPanel>
116+
</ItemsControl>
117+
</ScrollViewer>
118+
119+
<!-- ===== Actions ===== -->
120+
<Grid Grid.Row="3" Margin="14,10,14,14" ColumnDefinitions="*,Auto,Auto">
121+
<Button x:Name="CheckBtn" Grid.Column="0" Classes="primary" HorizontalAlignment="Stretch">
122+
<StackPanel Orientation="Horizontal" Spacing="8" HorizontalAlignment="Center">
123+
<PathIcon Data="{StaticResource IconRefresh}" Width="16" Height="16"/>
124+
<TextBlock x:Name="CheckLabel" Text="Проверить" VerticalAlignment="Center"/>
125+
</StackPanel>
126+
</Button>
127+
<Button x:Name="CopyBtn" Grid.Column="1" Classes="ghost" Margin="8,0,0,0"
128+
ToolTip.Tip="Скопировать IP">
129+
<PathIcon Data="{StaticResource IconCopy}" Width="15" Height="15"/>
130+
</Button>
131+
<ToggleButton x:Name="AutoBtn" Grid.Column="2" Classes="ghost" Margin="8,0,0,0"
132+
ToolTip.Tip="Автообновление каждые 30с">
133+
<PathIcon Data="{StaticResource IconFlash}" Width="15" Height="15"/>
134+
</ToggleButton>
135+
</Grid>
136+
</Grid>
137+
</Border>
138+
139+
<Window.Styles>
140+
<Style Selector="Button.chrome">
141+
<Setter Property="Width" Value="30"/>
142+
<Setter Property="Height" Value="30"/>
143+
<Setter Property="Padding" Value="0"/>
144+
<Setter Property="Foreground" Value="#B9C7EC"/>
145+
<Setter Property="Background" Value="Transparent"/>
146+
<Setter Property="CornerRadius" Value="8"/>
147+
<Setter Property="HorizontalContentAlignment" Value="Center"/>
148+
<Setter Property="VerticalContentAlignment" Value="Center"/>
149+
</Style>
150+
<Style Selector="Button.chrome:pointerover /template/ ContentPresenter">
151+
<Setter Property="Background" Value="#22FFFFFF"/>
152+
</Style>
153+
154+
<Style Selector="Button.primary">
155+
<Setter Property="Height" Value="42"/>
156+
<Setter Property="FontSize" Value="14"/>
157+
<Setter Property="FontWeight" Value="SemiBold"/>
158+
<Setter Property="Foreground" Value="White"/>
159+
<Setter Property="CornerRadius" Value="12"/>
160+
<Setter Property="HorizontalContentAlignment" Value="Center"/>
161+
<Setter Property="Background">
162+
<LinearGradientBrush StartPoint="0%,0%" EndPoint="100%,0%">
163+
<GradientStop Color="#7C6CFF" Offset="0"/>
164+
<GradientStop Color="#4FD1FF" Offset="1"/>
165+
</LinearGradientBrush>
166+
</Setter>
167+
</Style>
168+
<Style Selector="Button.primary:pointerover /template/ ContentPresenter">
169+
<Setter Property="Background">
170+
<LinearGradientBrush StartPoint="0%,0%" EndPoint="100%,0%">
171+
<GradientStop Color="#8E80FF" Offset="0"/>
172+
<GradientStop Color="#66D8FF" Offset="1"/>
173+
</LinearGradientBrush>
174+
</Setter>
175+
</Style>
176+
177+
<Style Selector="Button.ghost, ToggleButton.ghost">
178+
<Setter Property="Height" Value="42"/>
179+
<Setter Property="Width" Value="46"/>
180+
<Setter Property="Padding" Value="0"/>
181+
<Setter Property="Foreground" Value="#CFE3FF"/>
182+
<Setter Property="CornerRadius" Value="12"/>
183+
<Setter Property="Background" Value="#1AFFFFFF"/>
184+
<Setter Property="BorderThickness" Value="1"/>
185+
<Setter Property="BorderBrush" Value="#22FFFFFF"/>
186+
<Setter Property="HorizontalContentAlignment" Value="Center"/>
187+
<Setter Property="VerticalContentAlignment" Value="Center"/>
188+
</Style>
189+
<Style Selector="Button.ghost:pointerover /template/ ContentPresenter, ToggleButton.ghost:pointerover /template/ ContentPresenter">
190+
<Setter Property="Background" Value="#2AFFFFFF"/>
191+
</Style>
192+
<Style Selector="ToggleButton.ghost:checked">
193+
<Setter Property="Foreground" Value="#4FD1FF"/>
194+
</Style>
195+
<Style Selector="ToggleButton.ghost:checked /template/ ContentPresenter">
196+
<Setter Property="Background" Value="#4C7C6CFF"/>
197+
</Style>
198+
</Window.Styles>
199+
</Window>

0 commit comments

Comments
 (0)