Closed heinrich-ulbricht closed 5 months ago
I agree that this would be a great core feature for the library. But in the mean time here is what you can do with the current release and overriding the Button redraw method.
using Terminal.Gui;
Application.Init();
var win = new Window("Example App (Ctrl+Q to quit)");
win.Add(new ShadowButton("Button 1"){
X = 2,
Y = 1
});
win.Add(new ShadowButton("Button 2")
{
X = 20,
Y = 1
});
Application.Run(win);
Application.Shutdown();
class ShadowButton : Button
{
public ShadowButton(string text):base(text + " ") /*put a space on to leave 'shadow' room*/
{
Height = 2;
}
public override void Redraw(Rect bounds)
{
// draw regular button
base.Redraw(bounds);
// draw the 'end' button symbol one in
AddRune(bounds.Width - 2,0, ']');
// shadow color
Driver.SetAttribute(new Terminal.Gui.Attribute(Color.Black,
Colors.Base.Normal.Background));
// end shadow (right)
AddRune(bounds.Width - 1, 0, '▄');
// leave whitespace in lower left in parent/default background color
Driver.SetAttribute(new Terminal.Gui.Attribute(Color.Black,
Colors.Base.Normal.Background));
AddRune(0, 1, ' ');
// The color for rendering shadow is 'black' + parent/default background color
Driver.SetAttribute(new Terminal.Gui.Attribute(Colors.Base.Normal.Background,
Color.Black));
// underline shadow
for (int x = 1;x<bounds.Width;x++)
{
AddRune(x,1, '▄');
}
}
}
UPDATE: From a little quick experimenting the 'three quarter block' ▆
renders as a question mark in PowerShell (out of the box windows 10). Only the 'half blocks' are there default. Same applies to some of the other fancier 'block' sizes.
Theres also a little 'bleed' on the right of the button where 1 pixel of terminal is black. Don't know if that is a font thing or a bug in powershell/visual studio console.
For reference these are the blocks available (not all of which are supported by all terminals)
Block elements 2580 ▀ UPPER HALF BLOCK 2581 ▁ LOWER ONE EIGHTH BLOCK 2582 ▂ LOWER ONE QUARTER BLOCK 2583 ▃ LOWER THREE EIGHTHS BLOCK 2584 ▄ LOWER HALF BLOCK 2585 ▅ LOWER FIVE EIGHTHS BLOCK 2586 ▆ LOWER THREE QUARTERS BLOCK 2587 ▇ LOWER SEVEN EIGHTHS BLOCK 2588 █ FULL BLOCK = solid → 25A0 ■ black square 2589 ▉ LEFT SEVEN EIGHTHS BLOCK 258A ▊ LEFT THREE QUARTERS BLOCK 258B ▋ LEFT FIVE EIGHTHS BLOCK 258C ▌ LEFT HALF BLOCK 258D ▍ LEFT THREE EIGHTHS BLOCK 258E ▎ LEFT ONE QUARTER BLOCK 258F ▏ LEFT ONE EIGHTH BLOCK 2590 ▐ RIGHT HALF BLOCK Shade characters 2591 ░ LIGHT SHADE • 25% 2592 ▒ MEDIUM SHADE = speckles fill, dotted fill • 50% • used in mapping to cp949 → 1FB90 🮐 inverse medium shade 2593 ▓ DARK SHADE • 75% Block elements 2594 ▔ UPPER ONE EIGHTH BLOCK 2595 ▕ RIGHT ONE EIGHTH BLOCK Terminal graphic characters 2596 ▖ QUADRANT LOWER LEFT 2597 ▗ QUADRANT LOWER RIGHT 2598 ▘ QUADRANT UPPER LEFT 2599 ▙ QUADRANT UPPER LEFT AND LOWER LEFT AND LOWER RIGHT 259A ▚ QUADRANT UPPER LEFT AND LOWER RIGHT → 1F67F 🙿 reverse checker board → 1FB95 🮕 checker board fill 259B ▛ QUADRANT UPPER LEFT AND UPPER RIGHT AND LOWER LEFT 259C ▜ QUADRANT UPPER LEFT AND UPPER RIGHT AND LOWER RIGHT 259D ▝ QUADRANT UPPER RIGHT 259E ▞ QUADRANT UPPER RIGHT AND LOWER LEFT → 1F67E 🙾 checker board → 1FB96 🮖 inverse checker board fill 259F ▟ QUADRANT UPPER RIGHT AND LOWER LEFT AND LOWER RIGHT
I think setting the Border
property with the Effect3D
enabled on Button
will do the trick.
I think setting the
Border
property with theEffect3D
enabled onButton
will do the trick.
Effect3D
uses a full border element and only has it below the Button
.
Button3 uses the Effect3D while Button1 and Button2 use the ShadowButton
implementation. Both look ok but I think the half box rendering looks nicer. But there are definetly issues:
Also the ShadowButton
click area includes the shadow (clicks in shadow will focus/click the button) which is different... not sure if better or worse but definetly different.
win.Add(new Button("Button 3")
{
X = 40,
Y = 1,
Border = new Border
{
Effect3D = true,
Effect3DBrush = new Attribute(Color.Black,Color.Black),
}
});
Heres what the shadows look like when theres a background that isn't just solid color. You can see the eye expects to see half an 'x' where that half shadow ends.
I have to say the Norton Commander query box in OP does look awesome. I'm getting inspired to ressurect this failed PR in the Designer https://github.com/gui-cs/TerminalGuiDesigner/pull/111 I like the grey/white and yellow/red (focus).
I agree the Norton style looks great.
I'd love to see us tackle a new visual style as part of v2. See https://github.com/gui-cs/Terminal.Gui/discussions/1940
The ShadowButton already looks pretty good! If just the 1 pixel black line wasn't there 😑
You can see the eye expects to see half an 'x' where that half shadow ends.
I think printing half of a letter or digit on a terminal is impossible.
You can see the eye expects to see half an 'x' where that half shadow ends.
I think printing half of a letter or digit on a terminal is impossible.
Yeah haha sorry I didn't mean it as a solution. Was just weighing up the pros and cons of each of them. The half height shadow looks wierd when hovering over background stuff but theres not much that can be done about it.
I guess if we add it we should make it feature toggle. Thick or thin shadows. As theres not likely to always be a clear 'best'
The ShadowButton already looks pretty good! If just the 1 pixel black line wasn't there 😑
Its so wierd! when you zoom in it doesn't get any bigger and it's there on cmd
, powershell
and visual studio dev terminal. I'm going to test this on my linux machine asap - see if it is something about my desktop resolution or something.
When zoomed in the bleed is still single pixel
Yup, as I thought. Ubuntu with Terminator does not have this artifact, neither does Terminology console.
Is anyone please able to test to see if this line artifact appears for them on Windows? I'm hoping this is a hardware issue.
Source code is in my initial reply.
I confirm that doesn't appears on "Windows Terminal". Only with Windows Host Console that line artifact appears.
Windows 11 Console Window Host 😞 - the artifact is there.
Console in 2022 starts to seem like rocket science.
UX-related: when clicking the button I felt that I expected it to go down when pressed.
UX-related: when clicking the button I felt that I expected it to go down when pressed.
It only go down on click and not on button pressed. So if you press the mouse button and move it before released, the button click will not be fired.
How about this? Works with keyboard or mouse because it responds to the Click event not the mouse down.
I think it looks a little off since it drops lower than the shadow indicates but I think that is the best your going to get with the limitations of console column height/resolution. Making the shadow thicker will make the buttons look worse.
Its also going to be tough for the 'depressed' button to reveal any background View content (at the moment it draws ' '
over the revealed area).
Heres the code:
using Terminal.Gui;
using Attribute = Terminal.Gui.Attribute;
Application.Init();
var win = new Window("Example App (Ctrl+Q to quit)");
win.Add(new ShadowButton("Button 1")
{
X = 2,
Y = 1
});
win.Add(new ShadowButton("Button 2")
{
X = 20,
Y = 1
});
win.Add(new Button("Button 3")
{
X = 40,
Y = 1,
Border = new Border
{
Effect3D = true,
Effect3DBrush = new Attribute(Color.Black, Color.Black),
}
});
Application.Run(win);
Application.Shutdown();
class ShadowButton : Button
{
public ShadowButton(string text) : base(text + " ") /*put a space on to leave 'shadow' room*/
{
Height = 2;
}
public bool Depressed { get; private set; }
public override void OnClicked()
{
base.OnClicked();
Depressed = true;
SetNeedsDisplay();
Application.MainLoop.AddTimeout(TimeSpan.FromMilliseconds(500), (m) =>
{
Depressed = false;
Application.MainLoop.Invoke(() =>
{
SetNeedsDisplay();
});
return false;
});
}
public override void Redraw(Rect bounds)
{
if (Depressed)
{
DrawDepressed(bounds);
}
else
{
DrawRegular(bounds);
}
}
private void DrawRegular(Rect bounds)
{
// draw regular button
base.Redraw(bounds);
// draw the 'end' button symbol one in
AddRune(bounds.Width - 2, 0, ']');
// shadow color
Driver.SetAttribute(new Terminal.Gui.Attribute(Color.Black, Colors.Base.Normal.Background));
// end shadow (right)
AddRune(bounds.Width - 1, 0, '▄');
Driver.SetAttribute(new Terminal.Gui.Attribute(Color.Black, Colors.Base.Normal.Background));
AddRune(0, 1, ' ');
Driver.SetAttribute(new Terminal.Gui.Attribute(Colors.Base.Normal.Background, Color.Black));
// underline shadow
for (int x = 1; x < bounds.Width; x++)
{
AddRune(x, 1, '▄');
}
}
private void DrawDepressed(Rect bounds)
{
// area revealed on the background control by pushing Button down
Driver.SetAttribute(
new Terminal.Gui.Attribute(Colors.Base.Normal.Foreground,
Colors.Base.Normal.Background));
// clear top line so button sinks to bottom line
for (int x = 0; x < bounds.Width; x++)
{
AddRune(x,0,' ');
}
// render button as down
Driver.SetAttribute(
HasFocus ?
new Terminal.Gui.Attribute(ColorScheme.Focus.Foreground,
Colors.Base.Focus.Background):
new Terminal.Gui.Attribute(ColorScheme.Normal.Foreground,
Colors.Base.Normal.Background));
var buttonRenderText = TextFormatter.Text;
var textWidth = buttonRenderText.Length;
for (int x = 1; x < textWidth; x++)
{
AddRune(x, 1, buttonRenderText[x-1]);
}
AddRune(textWidth-1, 1, ']');
}
}
UX-related: when clicking the button I felt that I expected it to go down when pressed.
It only go down on click and not on button pressed. So if you press the mouse button and move it before released, the button click will not be fired.
Ah ok sorry, you were meaning about visually effect.
While googling for Norton Commander button animations I came across this repo: https://github.com/magiblot/tvision - there is a screenshot of a button also showing those vertical lines in the button shadow, but between the shadow blocks. On purpose? Maybe another block type?
I have sometimes seen those appear when resizing or moving terminal around. I think we shouldn't worry about these artifacts (bleed, seperation lines etc). They are pretty minor and are not really our responsibility to resolve.
I have sometimes seen those appear when resizing or moving terminal around. I think we shouldn't worry about these artifacts (bleed, seperation lines etc). They are pretty minor and are not really our responsibility to resolve.
You are right. With Windows Terminal
on Windows 11
that doesn't happens and probably M$ will not worry with that on previous versions.
Sounds reasonable. I'm nevertheless curious how the actual animation looks in either Turbo Vision or Norton Commander. They must have had the same challenges. Couldn't find something so far.
@tznind and how about only hide the shadows when the button go down, instead of redraw the button bellow? How will be the effect?
@BDisp Your suggestion looks pretty nice. The button still moves one to the right, giving good visual feedback:
https://user-images.githubusercontent.com/3469970/199702244-b564db88-5661-44f6-8ea4-40886522c7bd.mp4
(The gray text appears because I disable the button when being clicked. This might require more tuning with regard to timing.) And the button does not retain its focus color, yet. It flickers, then it's gone. Focus is still on the button, though.
class ShadowButton : Button
{
public ShadowButton(string text) : base(text + " ") /*put a space on to leave 'shadow' room*/
{
Height = 2;
}
public bool Depressed { get; private set; }
public override void OnClicked()
{
base.OnClicked();
Depressed = true;
SetNeedsDisplay();
Application.MainLoop.AddTimeout(TimeSpan.FromMilliseconds(180), (m) =>
{
Depressed = false;
Application.MainLoop.Invoke(() =>
{
SetNeedsDisplay();
});
return false;
});
}
public override void Redraw(Rect bounds)
{
if (Depressed)
{
DrawDepressed(bounds);
}
else
{
DrawRegular(bounds);
}
}
private void DrawRegular(Rect bounds)
{
// draw regular button
base.Redraw(bounds);
// draw the 'end' button symbol one in
AddRune(bounds.Width - 2, 0, ']');
// shadow color
Driver.SetAttribute(new Terminal.Gui.Attribute(Color.Black, Colors.Base.Normal.Background));
// end shadow (right)
AddRune(bounds.Width - 1, 0, '▄');
Driver.SetAttribute(new Terminal.Gui.Attribute(Color.Black, Colors.Base.Normal.Background));
AddRune(0, 1, ' ');
Driver.SetAttribute(new Terminal.Gui.Attribute(Colors.Base.Normal.Background, Color.Black));
// underline shadow
for (int x = 1; x < bounds.Width; x++)
{
AddRune(x, 1, '▄');
}
}
private void DrawDepressed(Rect bounds)
{
// area revealed on the background control by pushing Button down
Driver.SetAttribute(
new Terminal.Gui.Attribute(Colors.Base.Normal.Foreground,
Colors.Base.Normal.Background));
// clear bottom line (shadow)
for (int x = 0; x < bounds.Width; x++)
{
AddRune(x, 2, ' ');
}
// render button as down
Driver.SetAttribute(
HasFocus ?
new Terminal.Gui.Attribute(ColorScheme.Focus.Foreground,
Colors.Base.Focus.Background) :
new Terminal.Gui.Attribute(ColorScheme.Normal.Foreground,
Colors.Base.Normal.Background));
var buttonRenderText = TextFormatter.Text;
var textWidth = buttonRenderText.Length;
for (int x = 1; x < textWidth; x++)
{
AddRune(x, 0, buttonRenderText[x - 1]);
}
AddRune(textWidth - 1, 0, ']');
}
}
Thanks. I almost have an half border implementation in the Border class when the height is equal to 1. I think there is no sense to make it if the width is equal to 1. Let's me know.
Very nice effect @heinrich-ulbricht . Definetly better than jumping down a line 🎉
@tznind Cannot get enough of it :D
https://user-images.githubusercontent.com/3469970/199712014-fb4e134d-7baa-49a7-9726-69b998ce1600.mp4
I only removed the shadow but this effects is better. I'm leverage the existing Border class to make it more reused to another views. So, this additional move must be implemented in each view that want to implement it.
Unfortunately the focus color seems lost after selecting the button. Is this expected in this proof of concept stage or should it be there? AH nevermind. It only happens when disabling/enabling the button during the animation. Works great if not disabling as can be seen in the last video. (But is it normal...? See first video...) Nevermind 2: this seems to be normal button behavior, nothing to do with the animation.
Just thought I'd mention that we can do this without subclassing using the DrawContentComplete
event. That is what I have done in designer (but not the animation yet). The main reason I did it was so could have the View work in TerminalGuiDesigner (doesn't currently support user subclass views) but also because avoiding inheritence is often a good choice when its for subtle stuff.
I know @BDisp is creating a border style that will do most of the drawing work in this.
Hopefullly after that, we can create a simple 5/10 line class ShadowStyler
that registers events and adjusts borders to achieve this in a much simpler way?
Note: removing the start and end rune from the 3D button seems to reduce the visual noise a bit. Experimenting how it feels.
Question for y'all:
Is it realistic to think this could be done/tested before next Tuesday for inclusion in v1.9.0 (https://github.com/gui-cs/Terminal.Gui/milestone/5)?
Question for y'all:
Is it realistic to think this could be done/tested before next Tuesday for inclusion in v1.9.0 (https://github.com/gui-cs/Terminal.Gui/milestone/5)?
I think it depends on @BDisp availability as his PR #2166 is adding the functionality. I think if we can squeeze it in that would be great as its a great 'killer feature' for the patch.
Question for y'all:
Is it realistic to think this could be done/tested before next Tuesday for inclusion in v1.9.0 (https://github.com/gui-cs/Terminal.Gui/milestone/5)?
I'll try to add at least the functionality to work on whatever border size, but only using the current glyphs, ok?
The 2588 █ FULL BLOCK isn't print, but it print in the Character Map. @tznind can you test please. Thanks.
@tig Greetings! You removed this from the v2.0 milestone - is this shadow-for-button feature being scrapped or just included in another feature?
@heinrich-ulbricht with the new Frame
class which the View
class now have 3 Frame's for Margin
, Border
and Padding
, it's now possible manipulating his Thickness
and ColorScheme
properties to make the shadow effect.
@BDisp Wonderful ❤️
I'm interested in how it will work in the new API so I had an experiment. You can do this:
Application.Init();
var w = new Window();
var btn = new Button("Click me"){
X = 2,
Y = 2
};
btn.Margin.Thickness = new Thickness{
Bottom = 1,
Right = 1,
};
btn.Margin.ColorScheme = new ColorScheme(){
Normal = new Attribute(Color.Black)
};
w.Add(btn);
Application.Run(w);
Application.Shutdown();
But if you want half height effect you need to do a bit more work in Draw. I did try adding draw code to Margin
draw events but I don't think they are hooked up yet.
btn.DrawContentComplete += (s,e)=>
{
Application.Driver.SetAttribute(
new Attribute(
Color.Black,
w.ColorScheme.Normal.Background
));
for(int x = 0 ; x <= btn.Margin.Bounds.Width ;x++)
btn.AddRune(x,1,'▀');
};
I am addressing this issue in
@tig Looking at my older videos above I notice that in those the button just slightly moves to the right, but stays in the same "row", not moving down. At the same time the shadow disappears. Overall this gives the illusion of movement.
You chose a different approach and the button moves a "row" down to where the shadow was. Is this a deliberate choice (because it looks better in your eyes), or is there a technical necessity?
For my eye it looks more pleasing if the button stays right where the mouse is and doesn't move a "row" down, although this might not be physically correct. Somehow with your approach I expect the mouse cursor to stick to the button and move as well - as it would be when pressing a button with the finger. Hope it's clear what I mean :D
@tig Looking at my older videos above I notice that in those the button just slightly moves to the right, but stays in the same "row", not moving down. At the same time the shadow disappears. Overall this gives the illusion of movement.
You chose a different approach and the button moves a "row" down to where the shadow was. Is this a deliberate choice (because it looks better in your eyes), or is there a technical necessity?
For my eye it looks more pleasing if the button stays right where the mouse is and doesn't move a "row" down, although this might not be physically correct. Somehow with your approach I expect the mouse cursor to stick to the button and move as well - as it would be when pressing a button with the finger. Hope it's clear what I mean :D
Much better! Thanks for pointing that out.
Is your feature request related to a problem? Please describe. Nope, just feature request I guess
Describe the solution you'd like I'd like to add buttons like Norton Commander used back in the day. They had a half-height shadow at the bottom. But currently with Terminal.GUI it only seems to be possible to add a full-height shadow at the bottom. This is kind of fat for a small button. The shadow also needs to be set off vertically only half-height (or is it like 1/3?) on the right.
Here's what it should look like:
I looked at all the border samples in UICatalog but did not see such a feature. It might be impossible. Or is there some rune to configure? I'm not so deep in the code.
I'm currently playing around with color schemes and the 3D effect - not very shadowy for a small button:
Describe alternatives you've considered Using no shadow for buttons.